Unikaj pobierających i ustawiających, wyświetlających informacje o użytkowniku

10

tło

Czytam książkę „Czysty kod” i równolegle pracuję nad obiektami kalistenicznymi Kata, takimi jak konto bankowe, i utknąłem na tej zasadzie:

Dziewiąta zasada dotycząca obiektów kalistenicznych polega na tym, że nie używamy getterów ani seterów.

Wydaje się to całkiem zabawne i zgadzam się z tą zasadą. Ponadto na stronie 98-99 Czystego kodu autor wyjaśnia, że ​​pobierający / ustawiający łamią abstrakcję i że nie musimy pytać o nasz obiekt, ale musimy powiedzieć o tym naszemu obiektowi.

Ma to dla mnie doskonały sens i całkowicie zgadzam się z tą zasadą. Problem pojawia się w praktyce.

Kontekst

Na przykład mam aplikację, w której muszę wymienić niektórych użytkowników i wyświetlić szczegółowe informacje o użytkownikach.

Mój użytkownik składa się z:

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

Problem

Jak mogę to zrobić lub co mogę zrobić, aby uniknąć osób pobierających, gdy muszę tylko wyświetlić prostą informację ( i muszę potwierdzić, że nie potrzebuję dodatkowej operacji na tym konkretnym polu ), aby wyświetlić wartość imienia w prostym ( wsparcie losowe)?

Co przychodzi mi na myśl

Jednym z rozwiązań jest wykonanie:

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

Co jest totalnie okropne, łamanie wielu zasad obiektów kalistenicznych i łamanie Prawa Demeter.

Innym może być coś takiego:

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();

Ale nie czuję się komfortowo z tym rozwiązaniem, które wydaje się być jedynie „akcesorium wyższego rzędu”, takim jak ominięcie standardowego gettera / setera za pomocą metody, która ma jedynie na celu dopasowanie prawa Demeter ...

Dowolny pomysł ?

mfrachet
źródło

Odpowiedzi:

17

Powszechnym błędnym wyobrażeniem na temat unikania programów pobierających i ustawiających jest unikanie ich wszędzie, co jest niemożliwe, gdy wejdziesz na powierzchnię swojej architektury wchodząc w interakcję z użytkownikiem.

Należy unikać używania metod pobierających i ustawiających w części aplikacji dotyczącej logiki biznesowej, w której obiekty powinny być chronione za pomocą agregatów zapewniających kontekst, a metody powinny działać jak polecenia.

Aby uniknąć programów pobierających i ustawiających, możesz zaprojektować rozwiązanie podobne do programowania reaktywnego , wdrażając system raportowania za pomocą obserwowalnych jednostek, ale to bardzo komplikuje architekturę i nie ma sensu na poziomie CRUD aplikacji, nawet jeśli projekt jest naprawdę dobry dla interfejsów użytkownika.

To, czy powinieneś rozważyć użycie programów pobierających / ustawiających, zależy całkowicie od części aplikacji, nad którą aktualnie pracujesz. Jeśli obawiasz się, że interfejs użytkownika korzysta z getterów, jest całkowicie w porządku, dla logiki biznesowej, gdzie uruchamiałbyś część kodu opartą na wartości pobranej przez getter, nie tyle (to krzyczy, że logika powinna być faktycznie zamknięta w sobie klasa, do której dzwonisz do gettera).

Ponadto, prawo demeter nie polega na liczeniu kropek, ale jedynie na rozerwaniu klasy zapewniającej kontekst poprzez użycie getterów na klasie, aby uzyskać jej składniki, do których nie powinieneś mieć dostępu samodzielnie.

Jeśli uważasz, że interfejs użytkownika jest zbyt głęboki w modelach biznesowych, możesz pomyśleć o wprowadzeniu modeli widoku do swojego systemu, które będą odpowiedzialne za przekształcanie określonych części modeli w reprezentacje wizualne.

Andy
źródło
Okej, myślę o tej sugestii, podobnie jak program odwzorowujący między moją jednostką domeny, a niestandardowym DTO dla mojej warstwy prezentacji, która miałaby niektóre akcesoria, prawda?
mfrachet
@Margin Prawie tak, tak.
Andy
Wszystkie te kaskadowe rzeczy brzmią naprawdę dobrze i gładko, dzięki za odpowiedź;)
mfrachet
2

Jednym z kierunków myślenia byłoby zapewnienie ogólnej funkcji składowej łańcucha znaków zamiast zapewniania dostępu do surowych danych. Coś takiego:

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

Widzisz, dzięki takiemu podejściu nie jesteś ograniczony jedynie do dostarczania pełnych danych takimi, jakie są, możesz zapewnić środki do transformacji danych w wygodny i znaczący sposób. Na przykład może być konieczne rozegranie lew z przypadkiem postaci:

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

Możesz także zdefiniować niektóre powszechnie używane, być może złożone formaty, więc możesz na przykład powiedzieć

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

Piękno tego podejścia polega na tym, że utrzymuje on poszczególne ciągi wewnątrz Userklasy, a jednocześnie zapewnia znacznie większą funkcjonalność kodowi wywołującemu. Minusem jest jednak to, że musisz wdrożyć mechanizm szablonów, w formatDescription()którym będzie kilka linii kodu.

W związku z tym może to być całkowita przesada: nigdy nie zapominaj, że zasady programowania są jedynie wytycznymi. I ilekroć przestrzeganie innej zasady narusza zasadę KISS, najlepiej zrobić to w prosty sposób. Tak więc, chyba że potrzebujesz przynajmniej takiego elementu formatującego, nie zawracałbym sobie głowy jego implementacją ze względu na prostotę, stosując podejście oparte na akcesoriach.

cmaster - przywróć monikę
źródło
5
To jednak wydaje mi się naruszeniem SRP. Czy naprawdę zadaniem Userobiektu jest parsowanie i kompilowanie / interpretacja języka szablonów?
Jörg W Mittag
@ JörgWMittag Niekoniecznie. Jak powiedziałem, zasada KISS ma pierwszeństwo. I nie powiedziałem nigdzie, że Userklasa sama musi wdrożyć tę funkcjonalność. Gdybym miał tylko dwie klasy, które potrzebowałyby takiej funkcjonalności, mógłbyś postawić się na mnie, biorąc pod uwagę mechanizm zamiany szablonów na własną klasę. Ma to być przykład tego, jak można podnieść abstrakcję, która Userzapewnia więcej niż tylko kontener danych. I wierzę, że o to właśnie chodzi w tym, żeby unikać akcesorów: robić OOP zamiast obsługiwać kilka structs.
cmaster
Wiesz, że KISS jest całkowicie subiektywny i cel SRP? KISS nie mówi ci nic o tym, co robić. A to, co dla ciebie jest „proste”, może nie być dla mnie „proste”. Widzę prawdziwą różnicę w jakości, jeśli kłócą się z KISS lub SRP.
oopexpert