2010-04-01 35 views
10

Gần đây tôi đọc rằng getters/setters là ác và tôi phải nói nó có ý nghĩa, nhưng khi tôi bắt đầu học OOP một trong những điều đầu tiên tôi học được là "đóng gói các trường của bạn" để tôi học cách tạo lớp cho nó một số trường, tạo getters, setters cho chúng và tạo constructor nơi tôi khởi tạo các trường này. Và mỗi khi một số lớp khác cần thao tác đối tượng này (hoặc ví dụ như hiển thị nó), tôi truyền nó cho đối tượng và thao tác nó bằng getters/setters. Tôi có thể thấy vấn đề với cách tiếp cận này.Thiết kế OOP đúng không có getters?

Nhưng làm thế nào để làm đúng? Ví dụ hiển thị/render đối tượng đó là "dữ liệu" lớp - hãy nói rằng người, có tên và ngày sinh. Lớp có phương pháp để hiển thị đối tượng trong đó một số Renderer sẽ được chuyển làm đối số không? Sẽ không vi phạm nguyên tắc rằng lớp học chỉ nên có một mục đích (trong trường hợp này là trạng thái lưu trữ) vì vậy nó không nên quan tâm đến việc trình bày đối tượng này.

Bạn có thể đề xuất một số tài nguyên tốt mà các phương pháp hay nhất trong thiết kế OOP được trình bày không? Tôi dự định bắt đầu một dự án trong thời gian rảnh rỗi và tôi muốn nó là dự án học tập của tôi trong thiết kế OOP chính xác ..

+8

Bạn cần tìm nguồn tài liệu đọc tốt hơn, bạn tôi. Không phải mọi thứ bạn đọc trên intertubes đều đúng. Getters/Setters là đối diện của cái ác. Nếu bạn đã đăng nguồn gốc của đá quý này của sự khôn ngoan, chúng tôi có thể chọn nó ngoài, nhưng than ôi, không có liên kết .... aha-- liên kết được đăng. đã phê bình. noop. –

+4

@Sky Tất cả phụ thuộc vào ngữ cảnh, một số sử dụng của Getters/Setters là tốt và khác là "ác" (mặc dù tôi đã không sử dụng từ đó). Tôi cố gắng không quá ngoan ngoãn. –

+0

@Andreas - đã hiểu. Việc lạm dụng bất kỳ cấu trúc nào có thể là điều ác, nhưng để mô tả getters/setters như là ác trong cùng bối cảnh, ví dụ, eval chỉ là ngớ ngẩn, IMO. –

Trả lời

11

Allen Holub làm một giật gân lớn với "Why getter and setter methods are evil" trở lại trong năm 2003.

Thật tuyệt khi bạn đã tìm thấy và đọc bài viết. Tôi ngưỡng mộ bất cứ ai đang học hỏi và suy nghĩ nghiêm túc về những gì họ đang làm.

Nhưng đưa ông Holub với một hạt muối.

Đây là một chế độ xem có nhiều sự chú ý vì vị trí cực đoan và việc sử dụng từ "tà ác", nhưng nó chưa đặt thế giới thành lửa hoặc thường được chấp nhận làm giáo điều.

Nhìn vào C#: họ thực sự đã thêm cú pháp đường vào ngôn ngữ để làm cho hoạt động get/set dễ dàng hơn để viết. Điều này khẳng định quan điểm của ai đó về Microsoft như một đế chế độc ác hay mâu thuẫn với tuyên bố của ông Holub.

Thực tế là mọi người viết các đối tượng để khách hàng có thể điều khiển trạng thái. Nó không có nghĩa là mọi đối tượng được viết theo cách đó đều sai, xấu xa, hoặc không thể thực hiện được.

Chế độ xem cực đoan là không thực tế.

+0

Thực ra - bài viết của ông Holub khá đúng. Chỉ tiêu đề âm thanh cực đoan. Thật buồn, mọi người ngừng đọc sau khi:/ – MaR

+0

@MaR Hoàn toàn đồng ý, nó không cần thiết đối kháng với rất nhiều người nếu không sẽ cởi mở để nghe những gì bạn nói. –

+0

Tôi đã đọc bài viết. Allen Holub là một người rất thông minh, nhưng thực tế là không ai theo lời khuyên của anh ta. Các đối tượng vẫn đang được viết, mã đang chạy, và bất kỳ vấn đề nào có thể phát sinh không thể được đặt ở chân viết getters và setters. Đó là một lập luận học thuật vào thời điểm này. – duffymo

1

Tại sao bạn nghĩ getters là xấu? Xem bài với câu trả lời minh điều ngược lại:

Purpose of private members in a class

IMHO nó chứa rất nhiều về những gì chính đáng có thể được gọi là "OOP thực hành tốt nhất".

Cập nhật: OK, đọc bài viết bạn đang đề cập đến, tôi hiểu rõ hơn vấn đề là gì. Và đó là một câu chuyện hoàn toàn khác với tiêu đề khiêu khích của bài báo gợi ý. Tôi chưa đọc toàn bộ bài báo, nhưng AFAIU điểm cơ bản là người ta không nên xuất bản các trường lớp một cách không cần thiết thông qua các getters và setters được thêm vào một cách vô thức. Và với điểm này tôi hoàn toàn đồng ý.

Bằng cách thiết kế một cách cẩn thận và tập trung vào những gì bạn phải làm chứ không phải là cách bạn sẽ làm điều đó, bạn loại bỏ phần lớn các phương pháp getter/setter trong chương trình của bạn. Đừng hỏi thông tin bạn cần để thực hiện công việc; yêu cầu đối tượng có thông tin để thực hiện công việc cho bạn.

Cho đến nay rất tốt. Tuy nhiên, tôi không đồng ý rằng việc cung cấp bộ thu tiền như thế này

int getSomeField(); 

vốn đã làm tổn hại đến thiết kế lớp học của bạn. Vâng, nếu bạn chưa thiết kế giao diện lớp học của mình tốt. Sau đó, tất nhiên, nó có thể xảy ra mà bạn nhận ra quá muộn rằng lĩnh vực này nên là một long thay vì một int, và thay đổi nó sẽ phá vỡ 1000 nơi trong mã khách hàng. IMHO trong trường hợp như vậy nhà thiết kế là để đổ lỗi, không phải là người nghèo getter.

+0

Anh ta không nói anh ta nghĩ họ là ác, nhưng anh ta đã đọc chúng. – lugte098

+0

Tôi không có nghĩa là bạn nên sử dụng các lĩnh vực công cộng của khóa học, những gì tôi meand rằng bạn không nên tiết lộ bất kỳ chi tiết về thực hiện lớp trong trường hợp này những lĩnh vực nó có trừ khi hoàn toàn cần thiết. Bài viết có tại đây http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html – kane77

+0

@ lugte098 "Gần đây tôi đã đọc rằng getters/setters là điều xấu và tôi phải nói nó có ý nghĩa " –

4

"Encapsulate Fields của bạn" vì vậy tôi đã học để tạo ra lớp cung cấp cho nó một số lĩnh vực, tạo thu khí, setters

Python folks không làm điều này. Tuy nhiên, họ vẫn đang lập trình OO. Rõ ràng, getters gussy và setters là không cần thiết.

Chúng phổ biến, vì các giới hạn trong C++ và Java. Nhưng chúng dường như không cần thiết.

Những người dùng Python sử dụng properties đôi khi để tạo ra các hàm getter và setter giống như một thuộc tính đơn giản.

Vấn đề là "Đóng gói" là một chiến lược Thiết kế. Nó có rất ít hoặc không có gì để làm với việc thực hiện. Bạn có thể có tất cả các thuộc tính công khai và vẫn là thiết kế được đóng gói độc đáo.

Cũng lưu ý rằng nhiều người lo lắng về "người khác", những người "vi phạm" thiết kế bằng cách truy cập trực tiếp các thuộc tính. Tôi cho rằng điều này có thể xảy ra, nhưng sau đó lớp học sẽ ngừng hoạt động chính xác.

Trong C++ (và Java) nơi bạn không thể nhìn thấy nguồn, có thể khó hiểu giao diện, vì vậy bạn cần rất nhiều gợi ý. phương pháp riêng, getters rõ ràng và setters, vv

Trong Python, nơi bạn có thể nhìn thấy tất cả các nguồn, nó tầm thường để hiểu giao diện. Chúng tôi không cần phải cung cấp rất nhiều gợi ý. Như chúng ta nói "Sử dụng nguồn, Luke" và "Chúng ta đều là người lớn ở đây." Tất cả chúng ta đều có thể nhìn thấy nguồn, chúng ta không cần phải lo lắng về việc xếp chồng lên các getters và setters để cung cấp thêm các gợi ý về cách API hoạt động.

Ví dụ đối tượng hiển thị/hiển thị là lớp "dữ liệu" - giả sử Người có tên và ngày sinh. Lớp có phương pháp để hiển thị đối tượng trong đó một số Renderer sẽ được chuyển làm đối số không?

Ý tưởng hay.

Điều đó không vi phạm nguyên tắc rằng lớp học chỉ nên có một mục đích (trong trường hợp này là trạng thái cửa hàng) nên không nên quan tâm đến việc trình bày đối tượng này.

Đó là lý do tại sao đối tượng Render riêng biệt. Thiết kế của bạn khá đẹp.

Không có lý do gì khiến đối tượng Người không thể gọi trình kết xuất đồ họa đa năng và vẫn có tập hợp trách nhiệm hẹp.Sau khi tất cả đối tượng Person chịu trách nhiệm về các thuộc tính và chuyển các thuộc tính đó đến một Trình kết xuất đồ họa cũng nằm trong các trách nhiệm của nó.

Nếu nó thực sự là một vấn đề (và nó có thể có trong một số ứng dụng), bạn có thể giới thiệu các lớp học Helper. Vì vậy, lớp PersonRenderer hiển thị dữ liệu Person. Bằng cách đó, thay đổi đối với Người cũng yêu cầu thay đổi đối với PersonRenderer - và không có gì khác. Đây là Đối tượng truy cập dữ liệu mẫu thiết kế.

Một số người sẽ làm cho Render trở thành lớp nội bộ, chứa trong Person, do đó, nó là Person.PersonRenderer để thực thi một số ngăn chặn nghiêm trọng hơn.

+0

Với Python vs Java, nó cũng là một vấn đề cú pháp - với Python, cú pháp để truy cập một trường là giống như cú pháp để đọc từ một getter. Điều này có nghĩa là bạn có thể thay đổi việc triển khai mà không ảnh hưởng đến tất cả mã sử dụng lớp. Với Java, sự khác biệt thực hiện được tiếp xúc với người tiêu dùng của lớp, hoặc là làm instance.foo hoặc instance.getFoo(), do đó, một thay đổi đối với gợn thực hiện thông qua các phụ thuộc. Với Python (và Ruby và C#) nó luôn luôn là instance.foo, vì vậy không yêu cầu quy tắc "luôn đóng gói các trường của bạn" có giáo điều. – Douglas

+0

Cảm ơn bạn đã đề cập đến mẫu "Đối tượng truy cập dữ liệu"! –

0

Mọi thứ công khai là một phần của API của lớp học. Thay đổi các bộ phận này có thể phá vỡ những thứ khác, dựa vào đó. Một lĩnh vực công cộng, không chỉ được kết nối với một API, mà còn với biểu diễn bên trong, có thể là nguy hiểm. Ví dụ: Bạn lưu dữ liệu trong một trường dưới dạng mảng. Mảng này là công khai, vì vậy dữ liệu có thể được thay đổi từ các lớp khác. Sau đó, bạn quyết định chuyển sang Danh sách chung. Mã sử ​​dụng trường này làm mảng bị hỏng.

+0

tôi nghĩ đó là những gì mà cuộc thảo luận đang cố gắng đạt được. Bạn có để nó công khai và mở nó ra để lạm dụng như vậy, hoặc mindlessly tạo getters và setters và sau đó mở nó lên đến lạm dụng như vậy. – Anurag

4

Nếu bạn có getters và setters, bạn không có đóng gói. Và họ không cần thiết. Hãy xem xét std :: string class. Điều này có một biểu diễn nội bộ khá phức tạp, nhưng không có getters hoặc setters, và chỉ có một phần tử của biểu diễn là (có thể) được tiếp xúc đơn giản bằng cách trả về giá trị của nó (tức là size()). Đó là loại điều bạn nên nhắm tới.

+2

Tôi đồng ý với bạn mặc dù tôi không nghĩ rằng 'std :: string' là ví dụ tốt nhất. Hai 'toán tử []' trong chuỗi thực sự là một dạng của các hàm get/set. –

1

Giả sử bạn có nhiều lớp thực thể trong thiết kế của mình và giả sử chúng có lớp cơ sở như Dữ liệu. Việc thêm các phương thức getter và setter khác nhau để triển khai cụ thể sẽ làm ô nhiễm mã máy khách sử dụng các thực thể này như rất nhiều dynamic_casts, để gọi các phương thức getter và setter được yêu cầu.

Do đó, các phương thức getter và setter có thể vẫn còn ở đó, nhưng bạn nên bảo vệ mã máy khách. Đề xuất của tôi là áp dụng mẫu Khách truy cập hoặc bộ thu thập dữ liệu cho những trường hợp này.

Nói cách khác, hãy tự hỏi tại sao tôi cần các phương thức truy cập này, làm cách nào để thao tác các thực thể này? Và sau đó áp dụng các thao tác này trong các lớp khách truy cập để giữ mã máy khách sạch, cũng mở rộng chức năng của các lớp thực thể mà không làm ô nhiễm mã của chúng.

1

Ở một số ngôn ngữ, như C++, có khái niệm về friend. Sử dụng khái niệm này, bạn có thể thực hiện các chi tiết thực hiện của một lớp chỉ hiển thị với một tập con của các lớp khác (hoặc thậm chí cả các hàm). Khi bạn sử dụng Nhận/Đặt bừa bãi, bạn cung cấp cho mọi người quyền truy cập vào mọi thứ.

Khi được sử dụng ít, friend là cách tuyệt vời để tăng đóng gói.

2

Khái niệm cơ bản về lý do tại sao chúng được coi là độc ác là, một lớp/đối tượng nên xuất chức năng chứ không phải trạng thái. Trạng thái của một đối tượng được tạo thành từ các thành viên của nó. Getters and Setters cho phép người dùng bên ngoài đọc/sửa đổi trạng thái của một đối tượng mà không cần sử dụng bất kỳ chức năng nào. Do đó, ý tưởng, ngoại trừ DataTransferObjects mà bạn có thể có Getters và một hàm tạo để thiết lập trạng thái, các thành viên của một đối tượng chỉ nên được sửa đổi bằng cách gọi một chức năng của một đối tượng.

1

Trong bài báo sau liên quan đến endotesting bạn sẽ tìm thấy một mô hình để tránh getters (trong một số trường hợp) bằng cách sử dụng những gì tác giả gọi là 'xử lý thông minh'. Nó có rất nhiều điểm chung với cách tiếp cận của Holub tránh một số getters.

http://www.mockobjects.com/files/endotesting.pdf

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