Là sửa đổi một tham số đến một antipotype? [đóng cửa]


60

Tôi đang lập trình bằng Java và tôi luôn tạo ra các trình chuyển đổi giống như thế này:

public OtherObject MyObject2OtherObject(MyObject mo){
    ... Do the conversion
    return otherObject;
}

Tại nơi làm việc mới, mô hình là:

public void MyObject2OtherObject(MyObject mo, OtherObject oo){
    ... Do the conversion
}

Đối với tôi nó là một chút mùi, vì tôi đã quen với việc không thay đổi các tham số đến. Đây có phải là sự thay đổi tham số đến một antipotype hay không? Nó có một số nhược điểm nghiêm trọng?


4
Câu trả lời đúng cho điều này sẽ là ngôn ngữ cụ thể, vì những nơi mà các tham số pass-by-value biến chúng thành các biến cục bộ.
Blrfl

1
Mẫu thứ hai đôi khi được sử dụng như một thước đo hiệu quả để tái sử dụng các đối tượng. Giả sử đó oolà một đối tượng được truyền cho phương thức, không phải là một con trỏ được đặt thành một đối tượng mới. Có phải đó là trường hợp ở đây? Nếu đây là Java thì có lẽ là vậy, nếu là C ++ thì có thể không phải là
Richard Tingle

3
Điều này có vẻ như tối ưu hóa sớm, aye. Tôi khuyên bạn nên sử dụng biểu mẫu đầu tiên nói chung, nhưng nếu bạn gặp phải nút cổ chai hiệu năng, bạn có thể sử dụng biểu mẫu thứ hai với một nhận xét để biện minh cho nó . Một bình luận sẽ ngăn bản thân bạn và những người khác nhìn vào mã đó và có cùng câu hỏi này xuất hiện trở lại.
Keen

2
Mẫu thứ hai rất hữu ích khi hàm cũng trả về một giá trị, ví dụ, thành công / thất bại. Nhưng không có gì thực sự "sai" với nó. Nó thực sự phụ thuộc vào nơi bạn muốn tạo đối tượng.
GrandmasterB

12
Tôi sẽ không đặt tên các hàm thực hiện hai mẫu khác nhau này theo cùng một cách.
Casey

Câu trả lời:


70

Nó không phải là một antipotype, đó là một thực hành xấu.

Sự khác biệt giữa một antipotype và một thực hành xấu đơn thuần là ở đây: định nghĩa chống mẫu .

Phong cách nơi làm việc mới mà bạn thể hiện là một thực tế tồi tệ , thời gian trước hoặc sau OOP, theo Clean Code của chú Bob.

Các đối số được hiểu một cách tự nhiên nhất là đầu vào cho một hàm.

Bất cứ điều gì buộc bạn phải kiểm tra chữ ký chức năng là tương đương với một lần lấy. Đó là một sự phá vỡ nhận thức và nên tránh. Trong những ngày trước khi lập trình hướng đối tượng, đôi khi cần phải có các đối số đầu ra. Tuy nhiên, phần lớn nhu cầu đối số đầu ra biến mất trong các ngôn ngữ OO


7
Tôi không thấy lý do tại sao định nghĩa bạn liên kết để loại trừ điều này được coi là phản mẫu.
Phường Samuel Edwin

7
Tôi cho rằng đó là lý do rằng "chúng tôi đang tiết kiệm CPU / mem bằng cách không tạo đối tượng mới, thay vào đó chúng tôi nên tái chế một thứ chúng tôi có và chuyển nó thành một đối số" rõ ràng là sai đối với mọi người có nền tảng OOP nghiêm trọng rằng điều này có thể không phải là một mẫu bao giờ - vì vậy không có cách nào chúng ta có thể coi đó là một mẫu chống theo định nghĩa ...
vaxquis

1
Còn trường hợp khi một tham số đầu vào là một tập hợp lớn các đối tượng cần được sửa đổi thì sao?
Steve Chambers

Mặc dù tôi cũng thích tùy chọn 1, nhưng nếu OtherObjectlà một giao diện thì sao? Chỉ người gọi (hy vọng) biết loại bê tông mà nó muốn.
dùng949300

@SteveChambers Tôi nghĩ, trong trường hợp này, thiết kế của lớp hoặc phương thức là xấu và nên được cấu trúc lại để tránh nó.
piotr.wittchen

16

Trích dẫn cuốn sách nổi tiếng "Clean Code" của Robert C. Martin:

Cần tránh các đối số đầu ra

Các hàm nên có một số lượng nhỏ các đối số

Mẫu thứ hai vi phạm cả hai quy tắc, đặc biệt là "đối số đầu ra". Theo nghĩa này, nó là tồi tệ hơn mẫu đầu tiên.


1
Tôi sẽ nói rằng nó vi phạm cái đầu tiên, rất nhiều đối số có nghĩa là mã lộn xộn, nhưng tôi nghĩ hai đối số là hoàn toàn ổn. Tôi nghĩ rằng quy tắc về mã sạch có nghĩa là nếu bạn cần 5 hoặc 6 dữ liệu đến, bạn muốn thực hiện quá nhiều trong một phương thức duy nhất, vì vậy bạn nên cấu trúc lại để tăng khả năng đọc mã và phạm vi OOP.
CsBalazsHungary

2
dù sao tôi nghi ngờ đây không phải là một luật nghiêm ngặt, mà là một đề nghị mạnh mẽ. Tôi biết nó sẽ không hoạt động với các kiểu nguyên thủy, nếu đó là một mô hình phổ biến rộng rãi trong một dự án, một thiếu niên sẽ có ý tưởng để vượt qua một int và hy vọng nó sẽ được thay đổi như các Đối tượng.
CsBalazsHungary

27
Bất kể Mã sạch có thể đúng như thế nào, tôi không nghĩ đây là một câu trả lời có giá trị mà không giải thích lý do tại sao những điều này nên tránh. Họ không phải là những điều răn giáng xuống trên một phiến đá, sự hiểu biết là quan trọng. Câu trả lời này có thể được cải thiện bằng cách cung cấp một bản tóm tắt về lý luận trong cuốn sách và một tài liệu tham khảo chương
Daenyth

3
Chết tiệt, nếu một người nào đó viết nó trong một cuốn sách thì đó phải là lời khuyên tốt cho mọi tình huống có thể tưởng tượng được. Không cần suy nghĩ.
Casey

2
@emodendroket Nhưng anh chàng, đó là các chàng trai!
Pierre Arlaud

15

Thành ngữ thứ hai có thể nhanh hơn, bởi vì người gọi có thể sử dụng lại một biến trong vòng lặp dài, thay vì mỗi lần lặp tạo ra thể hiện mới.

Tôi thường không sử dụng nó, nhưng ví dụ. trong lập trình trò chơi nó có vị trí của nó. Ví dụ, nhìn vào rất nhiều hoạt động Vector3f của JavaMonkey cho phép vượt qua cá thể cần được sửa đổi và trả về kết quả.


12
tốt, tôi có thể đồng ý nếu đây là nút cổ chai, nhưng bất cứ nơi nào tôi làm việc, các nút cổ chai thường là các thuật toán có hiệu quả thấp, không nhân bản hoặc tạo đối tượng. Tôi biết ít về phát triển trò chơi.
CsBalazsHungary

4
@CsBalazsHungary Tôi tin rằng các vấn đề khi tạo đối tượng là mối quan tâm có xu hướng liên quan đến phân bổ bộ nhớ và thu gom rác, có lẽ sẽ là mức độ tắc nghẽn tiếp theo sau sự phức tạp của thuật toán (đặc biệt là trong môi trường bộ nhớ hạn chế, ví dụ như điện thoại thông minh và tương tự) .
JAB

JMonkey cũng là nơi tôi đã thấy điều này rất nhiều vì nó thường là hiệu suất quan trọng. Tôi chưa bao giờ nghe thấy nó được gọi là "JavaMonkey"
Richard Tingle

3
@JAB: GC của JVM được điều chỉnh cụ thể cho trường hợp các đối tượng phù du. Một lượng lớn các vật thể sống rất ngắn là tầm thường để thu thập, và trong nhiều trường hợp, toàn bộ thế hệ phù du của bạn có thể được thu thập bằng một lần di chuyển con trỏ.
Phoshi

2
thực tế, nếu người ta phải tạo một đối tượng , nó sẽ không hiệu quả; nếu một khối mã phải được tối ưu hóa nhiều về tốc độ, thay vào đó bạn nên sử dụng các mảng nguyên thủy và mảng gốc; do đó, tôi cho rằng tuyên bố trong câu trả lời này không đúng.
vaxquis

10

Tôi không nghĩ đó là hai đoạn mã tương đương. Trong trường hợp đầu tiên, bạn phải tạo otherObject. Bạn có thể sửa đổi thể hiện hiện tại trong lần thứ hai. Cả hai đều có công dụng của nó. Mùi mã sẽ được ưa thích hơn một.


Tôi biết họ không tương đương. Bạn nói đúng, chúng tôi đang kiên trì những điều này, vì vậy nó sẽ tạo ra sự khác biệt. Vì vậy, bạn đang nói không ai trong số họ là antipotype, nó chỉ là câu hỏi của trường hợp sử dụng?
CsBalazsHungary

@CsBalazsHungary Tôi sẽ nói có. Đánh giá từ đoạn mã nhỏ mà bạn cung cấp.
Euphoric

Tôi đoán nó sẽ trở thành vấn đề nghiêm trọng hơn nếu bạn có một tham số nguyên thủy đến, tất nhiên sẽ không được cập nhật, vì vậy, như @claasz đã viết: nên tránh nó trừ khi nó không cần thiết.
CsBalazsHungary

1
@CsBalazsHungary Trong trường hợp đối số nguyên thủy, hàm thậm chí sẽ không hoạt động. Vì vậy, bạn có vấn đề tồi tệ hơn là thay đổi đối số.
Euphoric

Tôi muốn nói rằng một thiếu niên sẽ rơi vào cái bẫy khi cố gắng biến một kiểu nguyên thủy thành một tham số đầu ra. Vì vậy, tôi sẽ nói rằng bộ chuyển đổi đầu tiên nên được ưu tiên nếu có thể.
CsBalazsHungary

7

Nó thực sự phụ thuộc vào ngôn ngữ.

Trong Java, dạng thứ hai có thể là một mẫu chống, nhưng một số ngôn ngữ xử lý tham số truyền khác nhau. Trong Ada và VHDL ví dụ, thay vì đi qua giá trị hoặc tham khảo, thông số có thể có chế độ in, outhoặc in out.

Đó là một lỗi để sửa đổi một intham số hoặc đọc một outtham số, nhưng bất kỳ thay đổi nào đối với một in outtham số đều được gửi lại cho người gọi.

Vì vậy, hai hình thức trong Ada (đây cũng là VHDL hợp pháp) là

function MyObject2OtherObject(mo : in MyObject) return OtherObject is
begin
    ... Do the conversion
    return otherObject;
end MyObject2OtherObject;

procedure MyObject2OtherObject(mo : in MyObject; oo : out OtherObject) is
begin
    ... Do the conversion
    oo := ... the conversion result;
end MyObject2OtherObject;

Cả hai đều có công dụng của chúng; một thủ tục có thể trả về nhiều giá trị trong nhiều Outtham số trong khi một hàm chỉ có thể trả về một kết quả duy nhất. Bởi vì mục đích của tham số thứ hai được nêu rõ trong khai báo thủ tục nên không thể phản đối hình thức này. Tôi có xu hướng thích các chức năng cho dễ đọc. nhưng sẽ có trường hợp thủ tục tốt hơn, ví dụ người gọi đã tạo đối tượng.


3

Nó thực sự có vẻ có mùi, và không nhìn thấy bối cảnh nhiều hơn, không thể nói chắc chắn. Có thể có hai lý do để làm điều này, mặc dù có những lựa chọn thay thế cho cả hai.

Đầu tiên, đó là một cách ngắn gọn để thực hiện chuyển đổi một phần hoặc để lại kết quả với các giá trị mặc định nếu chuyển đổi không thành công. Đó là, bạn có thể có điều này:

public void ConvertFoo(Foo from, Foo to) {
    if (can't convert) {
        return;
    }
    ...
}

Foo a;
Foo b = DefaultFoo();
ConvertFoo(a, b);
// If conversion fails, b is unchanged

Tất nhiên, thường điều này được xử lý bằng cách sử dụng ngoại lệ. Tuy nhiên, ngay cả khi cần tránh các ngoại lệ vì bất kỳ lý do gì, có một cách tốt hơn để làm điều này - mẫu TryPude là một lựa chọn.

Một lý do khác là nó có thể là vì lý do thống nhất hoàn toàn, ví dụ, đó là một phần của API công khai, nơi phương thức này được sử dụng cho tất cả các hàm chuyển đổi vì bất kỳ lý do gì (chẳng hạn như các hàm chuyển đổi khác có nhiều đầu ra).

Java không tuyệt vời khi xử lý nhiều đầu ra - nó không thể có các tham số chỉ đầu ra như một số ngôn ngữ hoặc có nhiều giá trị trả về như các ngôn ngữ khác - nhưng thậm chí, bạn vẫn có thể sử dụng các đối tượng trả về.

Lý do nhất quán là khá khập khiễng nhưng đáng buồn là nó có thể là phổ biến nhất.

  • Có lẽ cảnh sát phong cách tại nơi làm việc của bạn (hoặc cơ sở mã của bạn) đến từ một nền tảng không phải là Java và đã miễn cưỡng thay đổi.
  • Mã của bạn có thể là một cổng từ một ngôn ngữ nơi phong cách này là thành ngữ hơn.
  • Tổ chức của bạn có thể cần duy trì tính nhất quán API trên các ngôn ngữ khác nhau và đây là kiểu mẫu số chung thấp nhất (đó là sự cố nhưng nó xảy ra ngay cả đối với Google ).
  • Hoặc có lẽ phong cách có ý nghĩa hơn trong quá khứ xa xôi và biến thành hình thức hiện tại của nó (ví dụ, nó có thể là mẫu TryPude nhưng một số người tiền nhiệm có thiện chí đã loại bỏ giá trị trả về sau khi phát hiện ra rằng không ai kiểm tra nó cả).

2

Ưu điểm của mẫu thứ hai là nó buộc người gọi phải có quyền sở hữu và trách nhiệm đối với đối tượng được tạo. Không có câu hỏi liệu phương thức tạo ra đối tượng hay lấy nó từ một nhóm có thể tái sử dụng. Người gọi biết rằng họ chịu trách nhiệm cho cả cuộc đời và xử lý đối tượng mới.

Nhược điểm của phương pháp này là:

  1. Không thể được sử dụng như một phương thức xuất xưởng, người gọi phải biết chính xác kiểu con của OtherObjectnó cần và xây dựng nó trước.
  2. Không thể được sử dụng với các đối tượng yêu cầu tham số trong hàm tạo của chúng nếu các tham số đó đến từ MyObject. OtherObjectphải được xây dựng mà không biết về MyObject.

Câu trả lời dựa trên kinh nghiệm của tôi về c #, tôi hy vọng logic dịch sang Java.


Tôi nghĩ với trách nhiệm mà bạn đề cập, nó cũng tạo ra vòng đời "khó nhìn" hơn của các vật thể. Trong một hệ thống phương thức phức tạp, tôi thích sửa đổi trực tiếp một đối tượng hơn là bắt đầu xem qua tất cả các phương thức chúng ta đang truyền.
CsBalazsHungary

Đây là những gì tôi đã nghĩ. Một loại tiêm phụ thuộc
Lyndon White

Một lợi thế khác là nếu OtherObject là trừu tượng hoặc một giao diện. Hàm không biết nên tạo kiểu nào nhưng người gọi có thể.
user949300

2

Với ngữ nghĩa lỏng lẻo - myObjectToOtherObjectcó thể có nghĩa là bạn chuyển một số dữ liệu từ đối tượng thứ nhất sang đối tượng thứ hai hoặc bạn chuyển đổi hoàn toàn một lần nữa, phương thức thứ hai có vẻ đầy đủ hơn.

Tuy nhiên, nếu tên phương thức là Convert(sẽ là nếu chúng ta xem phần "... thực hiện chuyển đổi"), tôi sẽ nói phương thức thứ hai sẽ không có ý nghĩa gì. Bạn không chuyển đổi một giá trị thành một giá trị khác đã tồn tại, IMO.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.