2011-02-04 41 views
13

Gần đây tôi đã sử dụng một thư viện cho phép các loại vật liệu làm cú pháp:Trở * này trong chức năng thành viên

MyClass myObject; 
myObject 
    .setMember1("string value") 
    .setMember2(4.0f) 
    .setMember3(-1); 

Rõ ràng điều này được thực hiện bằng cách có setters trả về một MyClass & loại; một cái gì đó như trở lại * này. Tôi thích cách mã này trông, nhưng tôi không thấy nó rất nhiều. Khi điều đó xảy ra tôi thường nghi ngờ là tại sao.

Vì vậy, đây có phải là hành vi không tốt? Một số ý nghĩa của việc thực hiện nó như thế này là gì?

+7

Nó được gọi là "Giao diện thông thạo". –

+0

Xin chào Joe, +1 cho tên của bạn và một câu hỏi hay. 1 cho Robert Harvey cho tên, đây là một liên kết http://en.wikipedia.org/wiki/Fluent_interface – Joe

+0

Nó giống như hỏi tại sao không phải ai cũng có giáo dục đại học ... –

Trả lời

5

Điều này đôi khi được gọi là Named Parameter Idiom hoặc chuỗi phương pháp. Đây không phải là thực hành xấu, nó có thể hỗ trợ khả năng đọc. Hãy xem xét ví dụ này được tải lên từ Câu hỏi thường gặp về C++

File f = OpenFile("foo.txt") 
      .readonly() 
      .createIfNotExist() 
      .appendWhenWriting() 
      .blockSize(1024) 
      .unbuffered() 
      .exclusiveAccess(); 

Cách khác là sử dụng đối số vị trí cho phương pháp OpenFile, yêu cầu người lập trình nhớ vị trí của mỗi đối số.

+3

Hoặc sử dụng Thông số tăng cường, trông có vẻ trơn tru hơn. –

+0

-1 Mã OP không phải là thành ngữ tham số được đặt tên. Những gì bạn hiển thị (từ FAQ) là một ví dụ về thành ngữ tham số được đặt tên. Sự khác biệt là những gì có thể được thiết lập: thuộc tính của đối tượng cuối cùng bạn đang xây dựng (rất xấu, bạn thực sự không muốn điều đó), hoặc các thuộc tính của gói đối số (tốt). –

+5

Tôi có lẽ nên mở rộng trên bình luận downvote. Các thành phần thông số được đặt tên là tốt: nó sinh ra ** xây dựng một pha **, sau khi một hàm tạo đã thực hiện thành công, đối tượng được khởi tạo đầy đủ với bất biến lớp làm việc. Mã OP thay vì sử dụng ** xây dựng hai giai đoạn ** hoặc ** xây dựng nhiều giai đoạn **, đó là Xấu vì sau khi hàm dựng C++ hoàn tất thành công, bạn vẫn không có một đối tượng sẵn sàng để sử dụng. Bjarne đã viết về nó trong phụ lục của TCPPPL, có sẵn dưới dạng PDF từ trang web của mình. Ngoài ra, mã OP còn cho thấy các thuộc tính willy-nilly. Vì vậy, NPI = tốt, OP code = xấu. –

1

Không có vấn đề với kiểu này. Nhược điểm duy nhất là bạn không thể sử dụng giá trị trả về cho các mục đích điển hình hơn, như trả lại kết quả của hàm.

+0

xem câu trả lời của @Jerry Coffin và câu trả lời của tôi.Xây dựng hai giai đoạn là một quái vật, phơi bày thuộc tính willy-nilly cũng được, kết hợp (như ở đây) họ, tốt, bất cứ điều gì, monstrosity . Thay vào đó, bạn nên sử dụng ví dụ: thành phần tham số được đặt tên. –

+0

@Alf, cảm ơn vì đã chỉ ra sự khác biệt - Tôi đã nhìn thấy thành ngữ Các tham số có tên trước đây nhưng không bao giờ sử dụng nó, do đó sự tinh tế của nó đã bị mất cho tôi. Tôi sẽ không lên án việc xây dựng hai giai đoạn hay những thuộc tính lộ liễu như bạn một cách khắc nghiệt, nhưng tôi đồng ý rằng chúng không phải là giải pháp tốt nhất. –

1

Nó được gọi là fluent api. Nó không phải là thực hành xấu, chỉ là một phong cách lập trình khác nhau. Hạn chế lớn nhất (IMO) là vì bạn đang trả về một tham chiếu cho chính mình, bạn không thể trả lại bất kỳ điều gì khác và khó có thể gỡ lỗi câu lệnh lưu loát vì chúng được trình biên dịch xem là một dòng "khổng lồ" "của mã.

1

Tôi không chắc đó có phải là hành vi xấu hay không, nhưng một ngụ ý là bạn không còn có thể trả về mã lỗi nữa, do đó bạn buộc phải sử dụng ngoại lệ hoặc các đối tượng lỗi xấu. Ngoại lệ đôi khi là giải pháp đúng, nhưng thường không phải vì chúng có thể tốn kém để ném và bị vô hiệu hóa trên một số nền tảng.

Tôi thực sự nghĩ rằng đó là một điều phong cách tuy nhiên, và vì mối quan hệ chặt chẽ của C++ với C cả về cú pháp và văn hóa, nhiều lập trình C++ như mã lỗi và như vậy sẽ thích trở về họ thay vì trả lại một tham chiếu. Nhưng tôi đã nhìn thấy sự trở lại của * điều này thường xuyên và tôi không nghĩ rằng đó là thực hành xấu.

1

Về lý thuyết, bạn có thể kết thúc với một tham chiếu lơ lửng nếu bạn làm điều gì đó khủng khiếp như:

MyClass *myObject = new MyClass; 
MyClass & dangling = myObject->setMember1("string"); 
delete myObject; 
dangling.setMember2(yrParam); 

Vì vậy, phải nhận thức được điều đó.

2

Đó là thực tiễn phổ biến. Các quá tải của operator= ngụ ý làm như vậy cho các cuộc gọi chuỗi:

class Foo { 

public: 
    Foo& operator=(const Foo& f) { 
     if (this != &f) { // check for self-assignment 
     // do some stuff... 
     } 
     return *this; 
    } 

}; 

Mã này cho phép làm những việc như:

Foo a, b, c; 
a = b = c; 

Xin lưu ý rằng kiểm tra để tự phân là bắt buộc bởi vì bạn thường phải deallocate chất liệu trong đối tượng hiện tại, do đó, cho phép a = a sẽ phá vỡ mã của bạn.

Theo nhận xét của @Fred Nurk, tôi muốn thêm rằng bạn nên xem xét thành ngữ Sao chép và hoán đổi để tránh trùng lặp mã và phát hành mã không có ngoại lệ.
Có một cái nhìn tại các liên kết dưới đây để biết thêm thông tin:

What is the copy-and-swap idiom?
http://gotw.ca/gotw/059.htm

+4

Thành ngữ sao chép-hoán đổi xử lý (hiếm) trường hợp tự gán theo cách rõ ràng hơn. –

+0

+1, cũng có 'toán tử <<' và 'toán tử >>' chuẩn để phát trực tuyến. Chaining không phải là mới, chắc chắn và được chứng thực bởi Thư viện chuẩn. –

+0

+1 cho nhận xét của bạn @Fred Nurk. Nhưng chúng ta có thể áp dụng thành ngữ sao chép-hoán đổi mỗi lần không? Ví dụ khi một lớp cần tái phân bổ bộ nhớ lớn hơn trong một số trường hợp nhất định? – jopasserat

1

Không xấu thực tế, trong thực tế, bạn nhìn thấy nó thường với dòng đầu ra để nối nhiều chuỗi và giá trị.

Chỉ có bất lợi mà tôi thấy là nó ngăn cản bạn quay trở lại bất cứ điều gì khác, mặc dù nếu đó là một phương pháp thiết lập, nó không quan trọng.

6

Một số người gọi chương trình thông thạo này (hoặc giao diện thông thạo). Những người khác gọi nó là một mớ hỗn độn.

I có xu hướng phần nào hướng tới trại sau. Đặc biệt, kinh nghiệm của tôi đã được rằng trong nhiều trường hợp, người viết mã theo cách này phụ thuộc vào trên giao diện "thông thạo" cho một chút khởi tạo một đối tượng. Nói cách khác, mặc dù ngụy trang, nó vẫn là khởi tạo hai bước. Tương tự như vậy, mặc dù nó có thể tránh được trong nhiều trường hợp, nó thường có vẻ dẫn đến khá một chút về những gì cần được hoàn toàn riêng tư cho lớp học được thực hiện công khai có thể sửa đổi thông qua thao tác.

Cá nhân tôi thích đối tượng đó không thay đổi sau khi tạo. Điều đó rõ ràng không phải luôn luôn có thể, và trong một số trường hợp, bạn thậm chí không thể đến rất gần. Tuy nhiên, càng có nhiều nội bộ của đối tượng mà bạn mở ra để thao tác bên ngoài, bạn càng ít chắc chắn về đối tượng đó duy trì trạng thái mạch lạc (và, thông thường, bạn càng phải làm nhiều để duy trì trạng thái mạch lạc).

+0

Đúng nếu tôi sai, nhưng có vẻ như bạn đang chống lại người định cư nói chung (thông thạo hay không) :-) – Joe

+1

@Joe: vâng, nói chung tôi là. –

+0

Rất dễ sử dụng giao diện thông thạo và các đối tượng không thay đổi được. Hàm khởi tạo cho bất biến có tham số đối tượng. Đối tượng đó được thiết lập bằng giao diện thông thạo. –

2

Ví dụ của bạn là không phải các thông số có tên idiom.

Với thành phần tham số được đặt tên, bộ định tuyến có thể đặt chuỗi được đặt thuộc tính của gói đối số (tham số).

Mã của bạn thay vì có một loạt các trình cài đặt để sửa đổi đối tượng cuối cùng bạn đang xây dựng, được gọi là xây dựng hai pha.

Trong xây dựng hai giai đoạn chung chỉ là Bad ™ và xây dựng hai giai đoạn được triển khai bằng cách hiển thị các thuộc tính cho mã máy khách, như trong ví dụ của bạn, Rất kém ™.

Ví dụ, nói chung, bạn không muốn sửa đổi thuộc tính của đối tượng tệp, khi đối tượng đó đã được xây dựng (và có thể tệp được mở).

Chúc mừng & hth.,

Các vấn đề liên quan