Tránh getters và setters, hiển thị thông tin người dùng


10

Lý lịch

Tôi đang đọc "Sách mã sạch", và, song song, tôi đang làm việc trên các đối tượng nhiệt trị Kata như tài khoản ngân hàng, và tôi bị mắc kẹt theo quy tắc đó:

Quy tắc thứ 9 của các đối tượng calisthenic là chúng ta không sử dụng getter hoặc setters.

Có vẻ khá vui, và tôi đồng ý với nguyên tắc này. Hơn nữa, tại trang 98-99 của Clean Code, tác giả giải thích rằng getters / setters phá vỡ sự trừu tượng hóa và chúng ta không phải hỏi đối tượng của mình, nhưng chúng ta phải nói với đối tượng của mình.

Điều này có ý nghĩa hoàn hảo trong tâm trí của tôi, và tôi hoàn toàn đồng ý với nguyên tắc này. Vấn đề đến trong thực tế.

Bối cảnh

Ví dụ: tôi đang có một ứng dụng mà tôi phải liệt kê một số người dùng và để hiển thị chi tiết người dùng.

Người dùng của tôi gồm có:

-> Name
   --> Firstname --> String
   --> Lastname --> String
-> PostalAddress
   --> Street --> String
   --> PostalCode --> String

Vấn đề

Tôi có thể làm như thế nào hoặc tôi có thể làm gì để tránh getters khi tôi chỉ cần hiển thị một thông tin đơn giản ( và tôi phải xác nhận rằng tôi không cần thao tác thêm trên trường cụ thể đó ) để hiển thị giá trị Firstname một cách đơn giản ( ngẫu nhiên) hỗ trợ đầu ra?

Những gì xuất hiện trong tâm trí của tôi

Một giải pháp là thực hiện:

user.getName().getFirstName().getStringValue()

Điều này là hoàn toàn khủng khiếp, phá vỡ nhiều quy tắc của các vật thể nhiệt trị và phá vỡ Luật Demeter.

Một số khác sẽ là một cái gì đó như:

String firstName = user.provideFirstnameForOutput();
// That would have called in the user object =>
String firstName = name.provideFirstnameForOutput();
// That would have called in the name object =>
String firstName = firstname.provideFirstnameForOutput();

Nhưng tôi không cảm thấy thoải mái với giải pháp này, đó dường như chỉ là một "người truy cập bậc cao" như bỏ qua getter / setter tiêu chuẩn với một phương pháp chỉ nhằm phù hợp với luật Demeter ...

Bất kỳ ý tưởng ?

Câu trả lời:


17

Quan niệm sai lầm phổ biến về ý tưởng tránh getters và setters là tránh chúng ở mọi nơi, điều này là không thể khi bạn đến bề mặt kiến ​​trúc của bạn tương tác với người dùng.

Bạn nên tránh sử dụng getters và setters trong phần logic nghiệp vụ trong ứng dụng của bạn, nơi các đối tượng nên được bảo vệ bằng cách sử dụng tổng hợp cung cấp ngữ cảnh và các phương thức sẽ đóng vai trò là các lệnh.

Để tránh getters và setters bạn có thể thiết kế một giải pháp tương tự như lập trình phản ứng , triển khai hệ thống báo cáo thông qua các thực thể quan sát được, nhưng điều này cực kỳ phức tạp về kiến ​​trúc và không có ý nghĩa gì về mức độ CRUD của ứng dụng của bạn, mặc dù thiết kế thực sự tốt cho giao diện người dùng.

Việc bạn có nên cân nhắc sử dụng getters / setters hay không hoàn toàn phụ thuộc vào phần ứng dụng bạn hiện đang làm việc. Nếu mối quan tâm của bạn là giao diện người dùng sử dụng getters là hoàn toàn tốt, đối với logic nghiệp vụ, nơi bạn sẽ chạy một phần mã dựa trên giá trị được truy xuất thông qua một getter, không quá nhiều (điều này hét lên rằng logic nên được gói gọn trong lớp bạn đang gọi getter trên).

Ngoài ra, định luật demeter không phải là về việc đếm các dấu chấm, mà chỉ đơn giản là xé tan một lớp cung cấp ngữ cảnh bằng cách sử dụng các getters trên lớp để có được các thành phần mà bạn không nên tự mình truy cập.

Nếu bạn cảm thấy giao diện người dùng của mình đi quá sâu vào các mô hình kinh doanh của mình, bạn có thể nghĩ đến việc giới thiệu các mô hình xem cho hệ thống của mình chịu trách nhiệm chuyển đổi các phần cụ thể của mô hình thành biểu diễn trực quan.


Được rồi, tôi nghĩ đề xuất đó giống như một người lập bản đồ giữa thực thể miền của tôi, với một DTO tùy chỉnh cho lớp trình bày của tôi sẽ có một số người truy cập phải không?
mfrachet

@Margin Khá nhiều, đúng vậy.
Andy

Tất cả các công cụ xếp tầng này nghe thực sự tốt và mượt mà, cảm ơn vì câu trả lời;)
mfrachet

2

Một hướng để suy nghĩ sẽ là cung cấp một hàm thành viên định dạng chuỗi chung, thay vì cung cấp quyền truy cập vào dữ liệu thô. Một cái gì đó như thế này:

String lastName = user.formatDescription("$(Lastname)");
String fullName = user.formatDescription("$(Lastname), $(Firstname)");
String abbreviated = user.formatDescription("$(FnAbb). $(LnAbb).");

Bạn thấy, với cách tiếp cận này, bạn không bị hạn chế chỉ cung cấp dữ liệu đầy đủ, bạn có thể cung cấp phương tiện để chuyển đổi dữ liệu theo cách thuận tiện, có ý nghĩa. Chẳng hạn, bạn có thể cần chơi các thủ thuật với trường hợp nhân vật:

String accountName = user.formatDescription("$(firstname).$(lastname)");

Bạn cũng có thể xác định một số định dạng thường được sử dụng, có thể phức tạp một lần, vì vậy bạn có thể nói, ví dụ

String fullAddress = user.formatDescription(User.kAddressFormat);

Cái hay của cách tiếp cận này là, nó giữ các chuỗi riêng lẻ bên trong Userlớp, trong khi thực sự cung cấp nhiều chức năng hơn đáng kể cho mã gọi. Tuy nhiên, nhược điểm là, bạn cần thực hiện cơ chế tạo khuôn mẫu trong formatDescription()đó sẽ có một vài dòng mã.

Như vậy, điều này có thể là quá mức cần thiết: Đừng bao giờ quên rằng các nguyên tắc lập trình chỉ là hướng dẫn. Và bất cứ khi nào tuân theo nguyên tắc khác là vi phạm nguyên tắc KISS, tốt nhất bạn chỉ nên thực hiện theo cách đơn giản. Vì vậy, trừ khi bạn có ít nhất một số nhu cầu cho một thành viên định dạng như vậy, tôi sẽ không bận tâm thực hiện nó vì mục đích đơn giản, sử dụng phương pháp tiếp cận dựa trên người truy cập.


5
Điều này, tuy nhiên, có vẻ như một vi phạm SRP đối với tôi. Có thực sự là trách nhiệm của một Userđối tượng để phân tích và biên dịch / giải thích một ngôn ngữ khuôn mẫu?
Jörg W Mittag

@ JörgWMittag Không nhất thiết. Như tôi đã nói, nguyên tắc KISS được ưu tiên. Và tôi đã nói không nơi nào Userlớp phải tự thực hiện chức năng này. Nếu tôi chỉ có hai lớp cần chức năng như vậy, bạn có thể đặt cược cho tôi bao thanh toán cơ chế thay thế mẫu vào lớp riêng của nó. Điều này có nghĩa là một ví dụ về cách bạn có thể nâng sự trừu tượng Usercung cấp đến một mức độ mà nó thực sự không chỉ là một thùng chứa dữ liệu. Và tôi tin rằng, đó là những gì tránh được người truy cập là tất cả: thực hiện OOP thay vì xử lý một loạt các structs.
cmaster - phục hồi monica

Bạn biết rằng KISS hoàn toàn chủ quan và mục tiêu SRP? KISS không cho bạn biết bất cứ điều gì về việc phải làm. Và "Đơn giản" đối với bạn có thể không "đơn giản" đối với tôi. Tôi thấy một sự khác biệt thực sự về chất lượng nếu tranh luận với KISS hoặc SRP.
oopexpert
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.