2009-04-15 31 views

Trả lời

18

Đó là một mẫu có thể sử dụng đủ nếu có nhiều thứ cần phải được đặt trên một đối tượng.

class Foo 
{ 
     int x, y, z; 
public: 
     Foo &SetX(int x_) { x = x_; return *this; } 
     Foo &SetY(int y_) { y = y_; return *this; } 
     Foo &SetZ(int z_) { z = z_; return *this; } 
}; 

int main() 
{ 
     Foo foo; 
     foo.SetX(1).SetY(2).SetZ(3); 
} 

mẫu này thay thế một constructor mà mất ba ints:

int main() 
{ 
     Foo foo(1, 2, 3); // Less self-explanatory than the above version. 
} 

Nó rất hữu ích nếu bạn có một số giá trị mà không phải lúc nào cần phải được thiết lập.

Để tham khảo, ví dụ hoàn chỉnh hơn về loại kỹ thuật này được gọi là "Named Parameter Idiom" trong C++ FAQ Lite.

Tất nhiên, nếu bạn đang sử dụng thông số này cho các tham số có tên, bạn có thể muốn xem boost::parameter. Hoặc bạn có thể không ...

+0

Gần đây tôi đã bị thu hút bởi kỹ thuật này. Sẽ rất thú vị nếu thấy ai đó có bất kỳ hạn chế nào. –

+0

Thú vị. Tôi đã không coi đó là một thay thế cho các nhà thầu phức tạp. Kỹ thuật này có thể cắt giảm một số lượng lớn các quá tải của các hàm tạo. –

+1

Có một số điều bạn không thể làm với điều này mà bạn có thể làm với các nhà xây dựng (ví dụ: khởi tạo các tham số và tham chiếu) – Eclipse

0

Tôi sẽ không nghĩ vậy. Thông thường, bạn nghĩ về đối tượng 'setter' khi làm điều đó.

Bên cạnh đó, nếu bạn chỉ đặt đối tượng, bạn chưa có con trỏ đến nó chưa?

2

Không phải tất cả những người định cư, nhưng một số người trong số họ có thể trở lại tham chiếu đến đối tượng hữu ích.

loại

a.SetValues(object)(2)(3)(5)("Hello")(1.4); 

tôi đã sử dụng thời gian một lần dài này trước đây để xây dựng biểu builder SQL mà xử lý tất cả những vấn đề Escapes và những thứ khác.

SqlBuilder builder; 

builder.select(column1)(column2)(column3). 
    where("=")(column1, value1) 
       (column2, value2). 
    where(">")(column3, 100). 
    from(table1)("table2")("table3"); 

Tôi không thể tạo lại nguồn trong 10 phút. Vì vậy, thực hiện là đằng sau màn cửa.

+0

Tôi đã thực hiện các công cụ tương tự để xây dựng các tài liệu XML trong C++. Câu hỏi ở đây mặc dù có vẻ là về những người định cư bất động sản nói chung. –

10

Bạn có thể trả về một tham chiếu đến this nếu bạn muốn chức năng chuỗi setter gọi nhau như thế này:

obj.SetCount(10).SetName("Bob").SetColor(0x223344).SetWidth(35); 

Cá nhân tôi nghĩ rằng mã đó là khó đọc hơn thay thế:

obj.SetCount(10); 
obj.SetName("Bob"); 
obj.SetColor(0x223344); 
obj.SetWidth(35); 
+0

Đó là câu hỏi về bố cục. Bạn chỉ có thể viết phần đầu tiên như phần thứ hai, chỉ cần bỏ qua obj. trên mọi thứ trừ cái đầu tiên và; trên tất cả mọi thứ nhưng cuối cùng. IMHO, nó là có thể đọc được sau đó. –

2

Nếu động lực của bạn liên quan đến chuỗi (ví dụ như đề xuất của Brian Ensink), tôi sẽ cung cấp hai nhận xét:

1. Nếu yo Bạn thấy mình thường xuyên cài đặt nhiều thứ cùng một lúc, điều đó có nghĩa là bạn nên tạo một struct hoặc class chứa tất cả các cài đặt này để chúng có thể được chuyển cùng một lúc. Bước tiếp theo có thể là sử dụng struct hoặc class này trong chính đối tượng ... nhưng vì bạn đang sử dụng getters và quyết định cách thể hiện nội bộ trong đó sẽ minh bạch cho người dùng của lớp, nên quyết định này sẽ liên quan nhiều hơn đến mức độ phức tạp của lớp học hơn bất cứ thứ gì.

2. Một giải pháp thay thế cho người đặt cược là tạo đối tượng mới, thay đổi và trả lại. Đây là cả hai không hiệu quả và không phù hợp trong hầu hết các loại, đặc biệt là loại có thể thay đổi. Tuy nhiên, đó là một lựa chọn mà đôi khi mọi người quên, mặc dù nó sử dụng trong chuỗi lớp của nhiều ngôn ngữ.

2

Mục đích tiêu biểu cho kiểu này đang được sử dụng để xây dựng đối tượng.

Person* pPerson = &(new Person())->setAge(34).setId(55).setName("Jack"); 

thay vì

Person* pPerson = new Person(34, 55, "Jack"); 

Sử dụng thứ hai nhiều phong cách truyền thống người ta có thể quên nếu giá trị đầu tiên truyền cho constructor là tuổi tác hay id? Điều này cũng có thể dẫn đến nhiều nhà xây dựng dựa trên tính hợp lệ của một số thuộc tính.

Sử dụng kiểu đầu tiên người ta có thể quên đặt một số thuộc tính đối tượng và có thể dẫn đến các lỗi mà đối tượng không được xây dựng hoàn toàn. (Một thuộc tính lớp được thêm vào sau đó nhưng không phải tất cả các địa điểm xây dựng đều được cập nhật để gọi đến setter yêu cầu.)

Khi mã phát triển, tôi thực sự thích sử dụng trình biên dịch để giúp tôi tìm tất cả các địa điểm nơi một đối tượng được tạo ra khi thay đổi chữ ký của một hàm tạo. Vì vậy, vì lý do đó tôi thích sử dụng các hàm tạo C++ thông thường hơn kiểu này.

mô hình này có thể hoạt động tốt trong các ứng dụng duy trì DataModel của họ theo thời gian theo quy tắc tương tự như được sử dụng trong nhiều ứng dụng cơ sở dữ liệu:

  • Bạn có thể thêm một lĩnh vực/thuộc tính để bàn/lớp đó là NULL theo mặc định. (Vì vậy, việc nâng cấp dữ liệu hiện có chỉ cần một cột NULL mới trong cơ sở dữ liệu.)
  • Mã không thay đổi sẽ vẫn hoạt động tương tự với trường NULL được thêm vào này.
+0

+1 để đảm bảo an toàn cho thời gian biên dịch. – Eclipse

+0

Chà. Đó là một sự thích nghi tuyệt vời đối với việc thiếu các thông số có tên của C++ (như Ada đã có 20 năm). –

+0

-> không. cho con trỏ mặc dù :) –

3

IMO setters là một mùi mã mà thường chỉ ra một trong hai điều:

Làm A Mountian Out Of A Molehill

Nếu bạn có một lớp học như thế này:

class Gizmo 
{ 
public: 
    void setA(int a) { a_ = a; } 
    int getA() const { return a_; } 

    void setB(const std::string & b) { v_ = b; } 
    std::string getB() const { return b_; } 
private: 
    std::string b_; 
    int a_; 
}; 

... và các giá trị thực sự đơn giản, vậy tại sao không chỉ làm cho các thành viên dữ liệu được công khai ?:

class Gizmo 
{ 
public: 
    std::string b_; 
    int a_; 
}; 

... Đơn giản hơn nhiều và, nếu dữ liệu đơn giản bạn sẽ không mất gì.

Một khả năng khác là bạn có thể

Làm Một Molehill Out Of A Mountian

Rất nhiều lần so với dữ liệu mà không phải là đơn giản: có thể bạn phải thay đổi nhiều giá trị, thực hiện một số tính toán, thông báo cho một số đối tượng khác; ai biết cái gì Nhưng nếu dữ liệu là không tầm thường đủ để bạn thực sự cần setters & getters, sau đó nó là không tầm thường, đủ để cần xử lý lỗi là tốt. Vì vậy, trong những trường hợp của bạn getters & setters nên được trả lại một số loại mã lỗi hoặc làm một cái gì đó khác để chỉ ra một cái gì đó xấu đã xảy ra.

Nếu bạn đang chaining cuộc gọi cùng nhau như thế này:

A.doA().doB().doC(); 

... và DOA() thất bại, bạn có thực sự muốn được gọi DOB() và DOC() không? Tôi nghi ngờ điều đó.

+6

RE: "A.doA(). DoB(). DoC();" nếu doA không thành công, đó là những ngoại lệ. – Eclipse

+0

+1, điểm tốt John. Cũng tốt Josh. –

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