Khi nào một phương thức của một lớp trả về cùng một thể hiện sau khi sửa đổi chính nó?


9

Tôi có một lớp có ba phương thức A(), B()C(). Những phương thức đó sửa đổi ví dụ riêng.

Trong khi các phương thức phải trả về một thể hiện khi cá thể là một bản sao riêng (giống như Clone()), tôi có một lựa chọn miễn phí để trả về voidhoặc cùng một thể hiện ( return this;) khi sửa đổi cùng một thể hiện trong phương thức và không trả về bất kỳ giá trị nào khác.

Khi quyết định trả về cùng một thể hiện đã sửa đổi, tôi có thể thực hiện các chuỗi phương thức gọn gàng như thế nào obj.A().B().C();.

Đây sẽ là lý do duy nhất để làm như vậy?

Nó thậm chí có ổn không khi sửa đổi cá thể của chính mình và trả lại nó? Hay chỉ nên trả lại một bản sao và để lại đối tượng ban đầu như trước? Bởi vì khi trả về cùng một thể hiện đã sửa đổi, người dùng có thể cho rằng giá trị được trả về là một bản sao, nếu không nó sẽ không được trả về? Nếu nó ổn, cách tốt nhất để làm rõ những điều như vậy trên phương pháp là gì?

Câu trả lời:


7

Khi nào nên sử dụng xích

Chức năng xâu chuỗi hầu hết là phổ biến với các ngôn ngữ trong đó IDE có tự động hoàn thành là phổ biến. Ví dụ: hầu hết tất cả các nhà phát triển C # đều sử dụng Visual Studio. Do đó, nếu bạn đang phát triển với C # thêm chuỗi vào phương thức của bạn có thể tiết kiệm thời gian cho người dùng của lớp đó vì Visual Studio sẽ hỗ trợ bạn xây dựng chuỗi.

Mặt khác, các ngôn ngữ như PHP có tính năng động cao và thường không có hỗ trợ tự động hoàn thành trong các IDE sẽ thấy ít lớp hỗ trợ chuỗi hơn. Chaining sẽ chỉ phù hợp khi phpDocs chính xác được sử dụng để hiển thị các phương thức có thể tạo chuỗi.

Xâu chuỗi là gì?

Cho một lớp có tên Foohai phương thức sau đây đều có thể kết nối được.

function what() { return this; }
function when() { return new Foo(this); }

Thực tế là một tham chiếu đến thể hiện hiện tại và một tạo một thể hiện mới không thay đổi rằng đây là các phương thức có thể xâu chuỗi.

Không có quy tắc vàng rằng một phương pháp chuỗi có thể chỉ tham chiếu đối tượng hiện tại. Các phương thức nguyên vẹn, có thể chuỗi có thể qua hai lớp khác nhau. Ví dụ;

class B { function When() { return true; } };
class A { function What() { return new B(); } };

var a = new A();
var x = a.What().When();

Không có tài liệu tham khảo thistrong bất kỳ ví dụ trên. Mã a.What().When()này là một ví dụ về một chuỗi. Điều thú vị là loại lớp Bkhông bao giờ được gán cho một biến.

Một phương thức được nối kết khi giá trị trả về của nó được sử dụng làm thành phần tiếp theo của biểu thức.

Dưới đây là một số ví dụ

 // return value never assigned.
 myFile.Open("something.txt").Write("stuff").Close();

// two chains used in expression
int x = a.X().Y() * b.X().Y();

// a chain that creates new strings
string name = str.Substring(1,10).Trim().ToUpperCase();

Khi nào nên sử dụng thisnew(this)

Chuỗi trong hầu hết các ngôn ngữ là bất biến. Vì vậy, các cuộc gọi phương thức chuỗi luôn dẫn đến các chuỗi mới được tạo. Trường hợp như một đối tượng như StringBuilder có thể được sửa đổi.

Kiên định là thực hành tốt nhất.

Nếu bạn có các phương thức sửa đổi trạng thái của một đối tượng và trả về this, thì không trộn lẫn trong các phương thức trả về các thể hiện mới. Thay vào đó, tạo một phương thức cụ thể được gọi là Clone()sẽ làm điều này một cách rõ ràng.

 var x  = a.Foo().Boo().Clone().Foo();

Điều đó rõ ràng hơn nhiều so với những gì đang diễn ra bên trong a.

Bước ra ngoài và trở lại

Tôi gọi đây là thủ thuật bước ra và quay lại , bởi vì nó giải quyết được rất nhiều vấn đề phổ biến liên quan đến xích. Về cơ bản, điều đó có nghĩa là bạn bước ra khỏi lớp ban đầu thành một lớp tạm thời mới và sau đó quay lại lớp ban đầu.

Lớp tạm thời tồn tại chỉ để cung cấp các tính năng đặc biệt cho lớp gốc, nhưng chỉ trong các điều kiện đặc biệt.

Đôi khi một chuỗi cần thay đổi trạng thái , nhưng lớp Akhông thể đại diện cho tất cả các trạng thái có thể đó . Vì vậy, trong một chuỗi, một lớp mới được giới thiệu có chứa một tham chiếu trở lại A. Điều này cho phép lập trình viên bước vào trạng thái và quay lại A.

Dưới đây là ví dụ của tôi, hãy để trạng thái đặc biệt được gọi là B.

class A {
    function Foo() { return this; }
    function Boo() { return this; }
    function Change() return new B(this); }
}

class B {
    var _a;
    function (A) { _a = A; }
    function What() { return this; }
    function When() { return this; }
    function End() { return _a; }
}

var a = new A();
a.Foo().Change().What().When().End().Boo();

Bây giờ đó là một ví dụ rất đơn giản. Nếu bạn muốn có nhiều quyền kiểm soát hơn, thì Bcó thể quay lại một loại siêu mới Acó các phương thức khác nhau.


4
Tôi thực sự không hiểu tại sao bạn khuyên dùng chuỗi phương pháp chỉ phụ thuộc vào hỗ trợ tự động hoàn thành giống như Intellisense. Chuỗi phương thức hoặc giao diện lưu loát là một mẫu thiết kế API cho bất kỳ ngôn ngữ OOP nào; điều duy nhất mà autocomplete thực hiện là ngăn lỗi chính tả và lỗi gõ.
amon

@amon bạn đọc sai những gì tôi nói. Tôi đã nói nó phổ biến hơn khi intellisense là phổ biến cho một ngôn ngữ. Tôi không bao giờ nói nó phụ thuộc vào tính năng.
Phản ứng

3
Mặc dù câu trả lời này giúp tôi rất nhiều để hiểu các khả năng của chuỗi, tôi vẫn thiếu quyết định khi nào nên sử dụng chuỗi. Chắc chắn, tính nhất quán là tốt nhất . Tuy nhiên, tôi đang đề cập đến trường hợp khi tôi sửa đổi đối tượng của mình trong hàm và return this. Làm cách nào tôi có thể giúp người dùng thư viện của mình xử lý và hiểu tình huống này? (Cho phép họ áp dụng các phương thức chuỗi, ngay cả khi không bắt buộc, điều này có thực sự ổn không? Hay tôi chỉ nên theo một cách, yêu cầu thay đổi gì cả?)
Martin Braun

@modiX nếu câu trả lời của tôi là chính xác, thì vui lòng chấp nhận nó là câu trả lời. Những thứ khác mà bạn đang tìm kiếm là ý kiến ​​về cách sử dụng chuỗi, và không có câu trả lời đúng / sai cho điều đó. Điều đó thực sự là tùy thuộc vào bạn để quyết định. Có lẽ bạn đang lo lắng quá nhiều về những chi tiết nhỏ?
Phản ứng

3

Những phương thức đó sửa đổi ví dụ riêng.

Tùy thuộc vào ngôn ngữ, có các phương thức trả về void/ unitvà sửa đổi thể hiện hoặc tham số của chúng là không thành ngữ. Ngay cả trong các ngôn ngữ được sử dụng để làm điều đó nhiều hơn (C #, C ++), nó sẽ bị lỗi thời với sự thay đổi theo hướng lập trình kiểu chức năng hơn (các đối tượng bất biến, các hàm thuần túy). Nhưng bây giờ hãy giả sử có một lý do tốt.

Đây sẽ là lý do duy nhất để làm như vậy?

Đối với một số hành vi nhất định (nghĩ x++) dự kiến ​​rằng hoạt động trả về kết quả, mặc dù nó sửa đổi biến. Nhưng đó phần lớn là lý do duy nhất để tự làm điều đó.

Nó thậm chí có ổn không khi sửa đổi cá thể của chính mình và trả lại nó? Hay chỉ nên trả lại một bản sao và để lại đối tượng ban đầu như trước? Bởi vì khi trả về cùng một thể hiện đã sửa đổi, người dùng có thể thừa nhận giá trị được trả về là một bản sao, nếu không nó sẽ không được trả về? Nếu nó ổn, cách tốt nhất để làm rõ những điều như vậy trên phương pháp là gì?

Nó phụ thuộc.

Trong các ngôn ngữ mà sao chép / mới và trả lại là phổ biến (C # LINQ và chuỗi) thì việc trả về cùng một tham chiếu sẽ gây nhầm lẫn. Trong các ngôn ngữ nơi sửa đổi và trả lại là phổ biến (một số thư viện C ++) thì việc sao chép sẽ gây nhầm lẫn.

Làm cho chữ ký không rõ ràng (bằng cách trả lại voidhoặc sử dụng các cấu trúc ngôn ngữ như các thuộc tính) sẽ là cách tốt nhất để làm rõ. Sau đó, làm cho tên rõ ràng muốn SetFoocho thấy rằng bạn đang sửa đổi thể hiện hiện tại là tốt. Nhưng điều quan trọng là duy trì thành ngữ của ngôn ngữ / thư viện mà bạn đang làm việc.


Thx cho câu trả lời của bạn. Tôi chủ yếu làm việc với .NET framework và theo câu trả lời của bạn, nó chứa đầy những thứ không thành ngữ. Trong khi những thứ như phương thức LINQ trả về một bản sao mới của bản gốc sau khi nối thêm các hoạt động, v.v. những thứ như Clear()hoặc Add()của bất kỳ loại bộ sưu tập nào sẽ sửa đổi cùng một thể hiện và trả về void. Trong cả hai trường hợp, bạn nói điều đó sẽ gây nhầm lẫn ...
Martin Braun

@modix - LINQ không trả lại một bản sao trong khi nối thêm các hoạt động.
Telastyn

Tại sao tôi có thể làm obj = myCollection.Where(x => x.MyProperty > 0).OrderBy(x => x.MyProperty);sau đó? Where()trả về một đối tượng bộ sưu tập mới. OrderBy()thậm chí trả về một đối tượng bộ sưu tập khác, bởi vì nội dung được sắp xếp.
Martin Braun

1
@modix - Họ trở về đối tượng mới mà thực hiện IEnumerable, nhưng phải rõ ràng, họ không sao chép, và họ không tập (trừ ToList, ToArrayvv).
Telastyn

Điều này đúng, chúng không phải là bản sao, hơn nữa chúng là những đối tượng mới, thực sự. Cũng bằng cách nói bộ sưu tập tôi đã tham chiếu đến các đối tượng bạn có thể đếm qua (các đối tượng chứa nhiều hơn một đối tượng trong bất kỳ loại mảng nào), không phải các lớp kế thừa từ đó ICollection. Lỗi của tôi.
Martin Braun

0

(Tôi giả sử C ++ là ngôn ngữ lập trình của bạn)

Đối với tôi đây chủ yếu là một khía cạnh dễ đọc. Nếu A, B, C là các sửa đổi, đặc biệt nếu đây là một đối tượng tạm thời được truyền dưới dạng tham số cho một số chức năng, ví dụ:

do_something(
      CPerson('name').age(24).height(160).weight(70)
      );

so với

{
   CPerson person('name');
   person.set_age(24);
   person.set_height(160);
   person.set_weight(70);
   do_something(person);
}

Xin lỗi nếu bạn trả lại tham chiếu cho phiên bản đã sửa đổi, tôi sẽ nói đồng ý và chỉ cho bạn ví dụ về các toán tử luồng '>>' và '<<' ( http://www.cprogramming.com/tutorial/ toán tử_overloading.html )


1
Bạn đã sai, đó là C #. Tuy nhiên tôi muốn câu hỏi của tôi phổ biến hơn.
Martin Braun

Chắc chắn, cũng có thể là Java, nhưng đó là một điểm nhỏ chỉ để đặt ví dụ của tôi với các toán tử vào ngữ cảnh. Điều cốt lõi là nó tập trung vào khả năng đọc, bị ảnh hưởng bởi những kỳ vọng và nền tảng của người đọc, bản thân họ bị ảnh hưởng bởi các thông lệ trong ngôn ngữ / khung cụ thể mà người ta đang xử lý - như các câu trả lời khác cũng đã chỉ ra.
AdrianI

0

Bạn cũng có thể thực hiện phương thức xâu chuỗi phương thức với trả về bản sao thay vì sửa đổi trả lại.

Một ví dụ C # tốt là chuỗi.Replace (a, b) không thay đổi chuỗi mà nó được gọi mà thay vào đó trả về một chuỗi mới cho phép bạn xâu chuỗi một cách vui vẻ.


Vâng tôi biết. Nhưng tôi không muốn đề cập đến trường hợp này, vì tôi phải sử dụng trường hợp này khi tôi không muốn chỉnh sửa cá thể, dù sao đi nữa. Tuy nhiên, cảm ơn đã chỉ ra rằng.
Martin Braun

Theo các câu trả lời khác, tính nhất quán của ngữ nghĩa là quan trọng. Vì bạn có thể phù hợp với trả lại bản sao và bạn không thể nhất quán với sửa đổi trả lại (vì bạn không thể làm điều đó với các đối tượng không thay đổi) nên tôi sẽ nói câu trả lời cho câu hỏi của bạn là không sử dụng trả lại bản sao.
Peter Wone
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.