2010-07-07 24 views
26

Tôi đang đọc cuốn sách "Mã sạch" và đang đấu tranh với một khái niệm. Khi thảo luận về các đối tượng và cấu trúc dữ liệu, nó nêu rõ các điều sau:Mã sạch: Các đối tượng có thuộc tính công khai không?

  • Đối tượng ẩn dữ liệu của họ sau abstractions và hiển thị các chức năng hoạt động trên dữ liệu đó.
  • Cấu trúc dữ liệu hiển thị dữ liệu của chúng và không có chức năng có ý nghĩa.

Vì vậy, những gì tôi nhận được từ điều này là tôi không nên có bất kỳ thuộc tính công khai nào trên đối tượng của mình, tôi chỉ nên có phương thức thực hiện các thao tác trên thuộc tính. Nếu tôi cần truy cập các thuộc tính, chúng phải có cấu trúc dữ liệu, có thể được trả về từ một phương thức trên đối tượng của tôi? Với cách tiếp cận này, có vẻ như tôi sẽ cần phương thức GetHeight() và SetHeight() cho thuộc tính Chiều cao của tôi trên đối tượng của tôi, thay vì chỉ sử dụng lấyđặt thuộc tính.

Có lẽ tôi không hiểu chính xác những gì đang được đề xuất, nhưng đây là hiểu biết của tôi về "Đối tượng ẩn dữ liệu của họ". Nếu bạn có thể giúp tôi hiểu điều này, tôi rất cảm kích!

Cảm ơn trước!

+2

Để thêm vào các câu trả lời dưới đây, sự nhầm lẫn có thể xuất phát từ thực tế là nhiều ngôn ngữ không hỗ trợ tài sản. Trong trường hợp này, bạn có một sự lựa chọn giữa các phương thức accessor và các trường công cộng, và sự lựa chọn đúng luôn là các phương thức accessor. C# không có vấn đề này, vì nó hỗ trợ các thuộc tính. –

Trả lời

17

Thuộc tính công khai là tốt. Không phải viết các phương thức rõ ràng GetHeight()SetHeight() là thuộc tính gì. Thuộc tính trong C# là không phải là dữ liệu; nó được xem tốt nhất như là một cặp phương thức getter/setter. (Các thuộc tính thực sự được biên dịch thành các phương thức trong IL được tạo ra.)

Ẩn dữ liệu là có thể bởi vì bạn có thể thay đổi việc triển khai mà không thay đổi giao diện.Ví dụ, bạn có thể thay đổi

public int Height { get; set; } 

vào

public int Height { get { return m_width; } set { m_width = value; } } 

nếu bạn quyết định rằng đối tượng của bạn nên luôn luôn vuông. Mã sử ​​dụng lớp học của bạn sẽ không cần bất kỳ sửa đổi nào.

Vì vậy, nếu đối tượng của bạn phơi bày các thuộc tính công khai, nó vẫn "ẩn dữ liệu đằng sau trừu tượng và hiển thị các hàm hoạt động trên dữ liệu đó", như sách đề xuất.

4

Thuộc tính là phương pháp thực tế.
Trình biên dịch biên dịch các thuộc tính để lấy/đặt các phương thức MIL.

30

Thật vậy, thuộc tính C# không phải là dữ liệu, là một accessor, do đó, nó là một hàm hoạt động trên dữ liệu.

Bạn nên tránh các trường công khai chứ không phải các thuộc tính công khai.

+3

có, nhưng các trường công khai được chấp nhận trên các đối tượng chuyển dữ liệu –

+2

Tôi tránh các DTO, nhưng trong các ngữ cảnh cụ thể. Khi phải sử dụng chúng, tôi thích tính chất tự động hơn. – onof

+0

Đối với hầu như tất cả các dự án, không có lý do gì để thích các thuộc tính trên các trường và nhiều lý do để thích các trường. Các lĩnh vực là: [1] guarranteed behaviourless (thay đổi một tài sản để thêm hành vi đòi hỏi một biên dịch lại này là * tốt *); [2] đôi khi nhanh hơn và không bao giờ chậm hơn; [3] có mã ngắn hơn; [4] có thể là 'readonly', đó là một guarrantee mạnh hơn nhiều so với' get'. Chỉ sử dụng các thuộc tính nếu bạn đang viết một API công khai có các thuộc tính cần cho phép hành vi trong phiên bản tương lai tương thích nhị phân hoặc cần một trình thiết lập riêng (nhưng xem xét 'chỉ đọc' thay thế). –

2

Tạo người truy cập công khai với các trường riêng tư thiết lập hợp đồng giữa mã người dùng và lớp học của bạn. Lý tưởng nhất, hợp đồng này không nên thay đổi so với sửa đổi mã.

Trong C#, cách thực thi tuân thủ hợp đồng là với interface. Giao diện sẽ cho phép bạn chỉ định phương thức triển khai thuộc tính và phương thức bắt buộc nhưng không cho phép các trường.

Hơn nữa, ở các điểm khác nhau của .NET, thuộc tính thường được ưu tiên hơn các trường. ví dụ. PropertyGrid kiểm soát chỉ liệt kê các thuộc tính, các lớp mô hình ASP.NET MVC yêu cầu các thuộc tính, v.v.

3

Thuộc tính cơ bản là viết tắt của các phương thức Getter và Setter. Điểm của các phương thức Getter và Setter là để đối tượng xử lý bất kỳ thao tác nào trên các biến để bạn có thể thực hiện thêm bất kỳ hoạt động nào (chẳng hạn như xác thực dữ liệu) mà không gây hậu quả không mong muốn.

Tôi nghĩ rằng bạn có thể bị treo trên các thuộc tính tự động, không có biến số sao lưu và, kết quả là, trông giống như các biến.

0

Thực tế bằng cách sử dụng Thuộc tính, ví dụ:

public class Temp 
{ 
    public int SomeValue{get;set;} 
    public void SomeMethod() 
    { 
    ... some work 
    } 
} 

Bạn đang ẩn dữ liệu của mình vì có biến ẩn để lưu trữ giá trị được đặt và trả về bởi thuộc tính SomeValue.

Nếu bạn có

public class Temp 
{ 
    private int someValue; 
    public int SomeValue 
    { 
    get{ return this.someValue;} 
    set{ this.someValue = value;} 
    } 
    public void SomeMethod() 
    { 
    this.someValue++; 
    } 
} 

Sau đó, bạn sẽ thấy những gì tôi có ý nghĩa. Bạn đang ẩn dữ liệu của đối tượng someValue và hạn chế quyền truy cập vào dữ liệu của đối tượng đó bằng cách sử dụng tính năng chèn trước SomeValue.

3

Cuốn sách đang cố gắng mô tả lý thuyết rằng một đối tượng không nên vạch trần cách lớp thực sự được triển khai. Trong các đối tượng phức tạp hơn, nhiều biến nội bộ không nhất thiết phải chuyển tải thông tin phù hợp từ góc nhìn bên ngoài và chỉ nên có các phương thức tác động lên chúng.

Tuy nhiên, làm cho quy tắc này trở nên khó khăn và nhanh chóng bị xáo trộn khi bạn có các đối tượng đơn giản. Trong trường hợp hình chữ nhật, chiều cao và chiều rộng là các thuộc tính cơ bản mà người dùng sẽ muốn biết. Và kể từ khi thực hiện điều này là thẳng về phía trước, không sử dụng get và thiết lập sẽ chỉ làm cho mã của bạn phức tạp hơn nó cần phải được.

10

Nó chủ yếu là định nghĩa khác của thuật ngữ "thuộc tính". Một thuộc tính trong C# không phải là những gì hầu hết các ngôn ngữ khác nghĩ đến như là các thuộc tính.

Ví dụ:
Một C++ tài sản công cộng là:

class foo 
{ 
    public: 
    int x; 
}; 

Thuật ngữ tương ứng trong C# sẽ là một lĩnh vực công cộng:

class foo 
{ 
    public int x; 
} 

gì chúng ta đặt tên trong C# như tài sản sẽ setters và getters bằng các ngôn ngữ khác:

C#:

class foo 
{ 
    public int X { get; set; } 
} 

C tương ứng ++:

class foo 
{ 
    private: 
    int x; 

    public: 
    void setX(int newX) { this->x = newX; } 
    int getX() { return this->x; } 
} 

Nói tóm lại:
C thuộc tính # là hoàn toàn tốt, chỉ không mù quáng mặc chúng để có được bộ và không làm cho mỗi DataField trong lớp một của bạn tài sản công cộng, hãy nghĩ về những gì người dùng trong lớp của bạn thực sự cần biết/thay đổi.

1

Giống như các bài viết khác trong chủ đề này tôi sẽ chỉ ra rằng các thuộc tính trong C# chỉ là các trường hợp đặc biệt của các hàm truy cập mà bạn đề cập đến.Trong thực tế, bạn có thể sử dụng các phương thức get_Property và set_Property trong IL trên đối tượng của bạn có một cờ cho biết chúng là các thuộc tính, điều này cũng đúng cho các sự kiện thực hiện các phương thức add_ và remove_ prefixed.

Một điểm khác biệt quan trọng khi xử lý trừu tượng là liệu thiết lập thuộc tính sẽ hoạt động trên đối tượng khác ngoài việc cập nhật trạng thái bên trong hoặc ném ngoại lệ PropertyChanged.

Nếu bạn nhìn vào rất nhiều đối tượng BCL bên trong, các thuộc tính được thực hiện theo cách bạn có thể đặt tất cả các thuộc tính theo thứ tự bất kỳ để định cấu hình đối tượng. Nếu bất kỳ xử lý phức tạp nào được thực hiện, thì thường là một phương pháp mô tả những gì sẽ xảy ra là một lựa chọn tốt hơn.

9

Khi bạn đã kết thúc Mã Sạch Tôi muốn giới thiệu bạn đọc Bob Martin của cuốn sách khác:

Agile Principles Patterns and Practices In C#

Trong cuốn sách này, ammount rộng lớn của cuốn sách bàn về một nghiên cứu trường hợp và trong đó, Bob áp dụng các nguyên tắc được thảo luận trong Bộ luật sạch. Tôi đọc sạch Code đầu tiên nhưng nhìn lại tôi nghĩ rằng "Agile Patterns .." nên được đọc đầu tiên như Clean Code là nhiều hơn một cuốn sổ tay ngày hoặc sổ tay của nguyên tắc SW tốt.

Ví dụ, trong "mô hình Agile ..." đoạn mã sau được sử dụng:

public class OrderData 
{ 
public string customerId; 
public int orderId; 
public OrderData() {} 

... 

} 

Các xác nhận sau đây của việc sử dụng các giao dịch dữ liệu công khai với câu hỏi của bạn:

Don' t bị xúc phạm bởi việc sử dụng các thành viên dữ liệu công khai . Đây không phải là một đối tượng trong ý nghĩa thực sự. Nó chỉ đơn giản là một container cho dữ liệu. Nó không có hành vi thú vị cần được đóng gói. Đặt dữ liệu biến riêng tư và cung cấp các móc nối và bộ định cư sẽ là một sự lãng phí thời gian. Tôi có thể đã sử dụng cấu trúc thay vì một lớp, nhưng tôi muốn OrderData được chuyển qua tham chiếu thay vì theo giá trị.


Ngoài:

Cá nhân, tôi phải nói rằng Robert Martin đã có đóng góp lớn cho cộng đồng nhà phát triển SW (cùng với Martin Fowler, Michael Feathers ..) với những cuốn sách này. Tôi nghĩ rằng họ phải đọc.

+0

Ngoài ra còn có [The Clean Coder] (http://www.amazon.com/The-Clean-Coder-Professional-Programmers/dp/0137081073) - chủ đề khác nhau, nhưng cũng đáng đọc IMHO. – TrueWill

2

Đây là giao dịch.

Mặc dù các biến công khai có thể hữu ích nhân dịp, thường tốt nhất là giữ chúng riêng tư.

Thật dễ dàng để giữ mã của bạn được sắp xếp nếu đối tượng là đối tượng duy nhất có quyền kiểm soát biến của nó.

Hãy tưởng tượng rằng bạn muốn duy trì độ cao từ 0 đến 200. Nếu bạn có phương pháp đặt chiều cao, bạn có thể theo dõi điều này dễ dàng.

Ví dụ (Sẽ sử dụng Java vì tốc độ):

public void setHeight(int newHeight) 
{ 
    if (newHeight < 0) 
     height = 0; 
    else if (newHeight > 200) 
     height = 200; 
    else 
     height = newHeight 
} 

Như bạn có thể thấy, phương pháp này là rất có cấu trúc và kiểm soát.

Bây giờ hãy tưởng tượng rằng chúng tôi có một dòng mã chỉnh sửa chiều cao này bên ngoài vì bạn chọn đặt quảng cáo đó ở chế độ công khai. Trừ khi bạn kiểm soát nó bên ngoài mã, bạn có thể nhận được một chiều cao mà không hoạt động tốt với chương trình của bạn. Ngay cả khi bạn đã làm muốn kiểm soát nó, bạn sẽ lặp lại mã.

Ví dụ rất cơ bản, nhưng tôi nghĩ nó sẽ vượt qua mọi vấn đề.

5

Trong khi tài sản công cộng không phải là một mã mùi ngay lập tức, hãy xem xét bài viết này:

Coding with Reason by Yechiel Kimchi (từ cuốn sách 97 Things Mỗi Programmer nên biết)

" ... đừng hỏi một đối tượng để làm việc với thông tin. Thay vào đó, yêu cầu đối tượng thực hiện công việc với thông tin đã có. "

Tính năng này không hoạt động mọi lúc (ví dụ: Đối tượng chuyển dữ liệu). Những gì tôi xem ra là Inappropriate Intimacy.

+2

+1 để báo giá treo trên bàn của tôi! Cảm ơn bạn đã tham khảo. – JSprang

+0

Đây là cách tiếp cận OOP nghiêm ngặt. Khó đạt được trong một số ngữ cảnh. Hãy xem xét mô hình MVVM. – onof

3

Trong OO thuần túy "một đối tượng thực" phải ẩn hoàn toàn dữ liệu mà nó sử dụng để hoàn thành trách nhiệm của nó. Vì vậy, tránh phơi bày dữ liệu nội bộ, bất kể điều này được thực hiện bởi một trường công cộng, một thuộc tính công cộng hoặc các hàm getter/setter công cộng.

Dữ liệu nội bộ LÀ NOR HIDDEN NEITHER Chỉ bằng cách định tuyến truy cập vào thông qua thuộc tính!

Để trả lời câu hỏi của bạn: - Tránh tài sản công cộng nếu bạn đang viết một đối tượng - Sử dụng propertiers công cộng nếu bạn đang viết các cấu trúc dữ liệu (một lĩnh vực nào sẽ thực hiện công việc, quá)

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