2013-01-25 15 views
6

Hãy xem xét các lớp sau đây:Sử dụng giá trị trình bao bọc và toán tử() quá tải để đơn giản hóa thiết kế getter/setter: một thực tế nguy hiểm?

class MyClass1 
{ 
    public: 
     double x() const {return _x;} // getter 
     double y() const {return _y;} // getter 
     double z() const {return _x*_y;} // getter 
     void x(const double var) {_x = var;} // setter 
     void y(const double var) {_y = var;} // setter 
     void z(const double var) {_x = var; _y = 1;} // setter 
    protected: 
     double _x; 
     double _y; 
}; 

Như các nội dung thực tế của MyClass1 là một chi tiết thực hiện, các getter và setter cung cấp một cách thống nhất để có được và thiết lập các nội dung lớp học ngay cả khi họ interdependant (ở đây _z không tồn tại bên trong nhưng đối với người dùng, z là một biến số như xy).

Bây giờ để tránh phải viết getter/setter cho xy, người ta có thể sử dụng một wrapper như thế này:

template <typename Type> 
class Wrapper 
{ 
    public: 
     constexpr Wrapper(const Type& value) {_value = value;} 
     constexpr Type& operator()() {return _value;} 
     constexpr const Type& operator()() const {return _value;} 
     constexpr void operator()(const Type& value) {_value = value;} 
    protected: 
     _value; 
}; 

Và bây giờ lớp ban đầu trở thành:

class MyClass2 
{ 
    public: 
     Wrapper<double> x; 
     Wrapper<double> y; 
     double z() const {return x*y;} // getter 
     void z(const double var) {x = var; y = 1;} // setter 
}; 

Có một thực hành nguy hiểm hoặc một giải pháp tốt để tránh phải viết getters/setters?

Lưu ý: Ở đây MyClass1MyClass2 chỉ là ví dụ. Câu hỏi của tôi là rất "chung": là nguy hiểm để thay thế getters/setters của các lớp học theo đề nghị Wrapper khi getter/setter chỉ trả về/thiết lập một giá trị nội bộ.

+2

Tôi cho rằng bạn sẽ cần phải thực hiện một số loại hành vi để thực sự làm điều gì đó (xác thực, thông báo, vv) trên các accessors. Othewise họ sẽ đánh bại mục đích của việc có chúng. Bạn không nghĩ sao? imho – imreal

+1

tại sao lớp học của bạn cần có/thiết lập giao diện? nếu các hàm này không phải duy trì một bất biến lớp, thì có rất ít điểm trong việc mã hóa chúng. – TemplateRex

Trả lời

1

Như bạn đã đề cập, mục đích chính của getters và setters là cung cấp một cách thống nhất để truy cập và thiết lập các biến cá thể riêng của bạn.

Từ giải pháp trình bao bọc của bạn, đôi khi xuống theo dõi vòng đời chương trình bạn quyết định thay đổi trình thiết lập, bạn có thể vứt bỏ trình bao bọc và thay thế bằng trình lấy/đặt ban đầu như trong MyClass1 - mà không phải thay đổi tất cả các thành phần khác của mã gọi nó.

Vì vậy, từ thời điểm đó, tôi nghĩ giải pháp của bạn là cách tiết kiệm hợp lệ để bạn nhập thêm vài dòng mã.

1

Tôi thấy không có gì đặc biệt nguy hiểm ở đó, nhưng dường như không có gì. C++ cung cấp ngữ nghĩa tham chiếu cho bất kỳ loại đối tượng nào, không tương thích với hàm setter. Đôi khi các nội dung thực tế không chỉ là một chi tiết.

Bạn cũng có thể đi theo con đường khác (trên thực tế, đây là những gì tôi mong đợi để xem khi tôi nhấp vào câu hỏi này):

struct virt_z { 
    double x; 
    double y; 
    struct z_wrap { 
     virt_z &parent; 

     operator double() { return parent.x * parent.y; } 
     z_wrap &operator=(double in) { 
      parent.x = in; 
      parent.y = 1; 
      return *this; 
     } 
     z_wrap(virt_z &in) : parent(in) {} 
    } z; 

    virt_z() : z(*this) {} 
    virt_z(virt_z const &o) : x(o.x), y(o.y), z(*this) {} 
}; 

https://ideone.com/qqgj3D

+1

Và những gì về khách hàng nghèo của bạn mà phải đọc giao diện này và tìm ra rằng để thiết lập z họ cần phải gán cho z thông qua một toán tử quá tải = của một lớp bọc bên trong. Kiểu getter và setter chức năng đơn giản của OP MyClass1 tốt hơn cả MyClass2 và giải pháp của bạn vì nó rõ ràng khi đọc giao diện cách sử dụng nó. Cho rằng cả ba biên dịch cho cùng một mã, tôi cho rằng MyClass1 nên được ưa chuộng. –

+0

@ user1131467 Những thứ khác nhau phù hợp với các tình huống khác nhau. Đối với MyClass1, bất kỳ hàm nào có nhiều hơn "set' z' "không nên được gọi là" 'SetZ()' ", và đó là vấn đề cơ bản với getter/setter formism. Nếu thiết lập một cái gì đó có tác dụng phụ, sau đó bạn không thực sự chỉ cần thiết lập một số nhà nước. Tương tự như vậy, bạn có thể hiếm khi * thực sự * chỉ cần thay đổi giao diện để thêm các hiệu ứng phụ trong cùng một đối tượng như vậy. Nó chỉ là một huyền thoại duy trì bởi sách giáo khoa OO. – Potatoswatter

+0

Tại sao người dùng nên quan tâm đến chi tiết triển khai của giao diện? Họ chỉ nên sử dụng nó một cách tự nhiên. – balki

1

Nó không nguy hiểm như vậy, nó chỉ là hơn khó sử dụng (đối với người dùng lớp). MyClass2 obfuscates và clutters giao diện.

Nó có thể làm cho nó ít tẻ nhạt cho bạn để viết mã nhưng bạn chỉ phải viết mã một lần - giao diện sẽ được tái sử dụng nhiều lần. Do đó người đọc mã nên được ưa chuộng.

MyClass1 vượt trội hơn cả giải pháp MyClass2 và Potatoswatter vì lý do này.

(Thậm chí nếu có một cách trong C++ để làm điều này:

class MyClass3 
{ 
    double x, y, z; 
} 

MyClass3::z::operator=(double) { ... } // property set 
double MyClass3::z::operator() { ... } // property get 

như trong C# nó vẫn sẽ là một ý tưởng tồi Tôi nghĩ rằng vì loại này mã "ẩn" dẫn đến giả định xấu mà có thể Làm chậm kiểu gỡ lỗi. Cách chức năng ít nhất cho phép bạn nghĩ rằng có thể có điều phức tạp hơn trên bản sao nguyên thủy.)

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