Getery i setery to zło, immutable ponad wszystko, ORMy wielką pomyłką

0

Z tydzień temu natknąłem się na serię artykułów na blogu http://www.yegor256.com których autor wydaje się być fanatykiem OOP. Wiele z nich mnie mocno zaintrygowało, zdecydowana większość jest dla mnie dość kontrowersyjna, więc chciałbym poznać waszą opinię na temat dobrych praktyk. Tematów jest wiele, więc w tym wątku chciałbym poruszyć te wymienione w temacie, które imo są całkiem ze sobą powiązane.

  1. Getery i setery = zło -> http://www.yegor256.com/2014/09/16/getters-and-setters-are-evil.html - czytając artykuł odnoszę wrażenie, że jako formę zastępczą autor zaleca to samo, tylko inaczej nazwane. Rozumiem, że nazewnictwo jest bardzo ważne, ale biorąc pod uwagę ile pobocznych projektów wspiera powszechnie przyjemne nazewnictwo set* / get* to trochę rzucanie sobie kłód pod nogi. Naprawdę między dog.giveBall() a dog.getBall() jest aż taka różnica, że mamy rezygnować np. ze wsparcia ORMów, bibliotek mapujących klasy na XMLe / JSONy czy innych rzeczy (np. projekt Lombok)? I dlaczego traktowanie niektórych obiektów w np. javie jako rozbudowane data holdery jest aż takie złe? Int jest ok, ale klasa reprezentująca współrzędne punktów kratowych, która zawiera 2 inty x i y z seterami i geterami już nie jest ok? Czemu?

  2. Tu pojawia się 'rozwiązanie' = printery -> http://www.yegor256.com/2016/04/05/printers-instead-of-getters.html - z jednej strony wygląda to ciekawie, bo rzeczywiście dość logiczne, że to np obiekt Ball winien mieć metodę 'toXml' - Podchodzisz do piłki i mówisz "Pokaż mi swoją reprezentację w xml". Ale w czym gorsze są konwertery, które tym zarządzają? Co jest w złego we wzięciu piłki i podejściu do konwertera "Ej stary masz tu piłkę, daj mi jej reprezentację xmlową, bo TY się na tym ZNASZ."?

  3. ORM to zło (http://www.yegor256.com/2014/12/01/orm-offensive-anti-pattern.html oraz http://www.yegor256.com/2016/07/06/data-transfer-object.html) - miałem okazję się łączyć przez JDBC do bazy. No da się. Ale jak użyłem javowego Hibernate'a albo już w ogóle Spring Data to była czysta przyjemność. 5 minut i wszystko gotowe, podstawowe SQLki porobione same z siebie, jak potrzebuję jakiejś wyszukanej to nie ma problemu żebym ją sam napisał. No jest to po prostu wygodne. Minusy z mojej strony? Odkąd dowiedziałem się i zrozumiałem, że dobrą praktyką jest robienie obiektów immutable, to jednak przeszkadzał mi fakt, że jak klasa Book reprezentuje encję książki w bazie danych, to jej pola nie mogły być finalne. No boli. Tutaj moje pytanie to bardziej doświadczonych - jak to obejść? Czy jest sens dokładać kolejną warstwę, która zawiera te same klasy i pola, tylko że immutable?

  4. No właśnie, immutable... http://www.yegor256.com/2014/06/09/objects-should-be-immutable.html - zgadzam się, Immutable jest bardzo spoko. Ale zastanawia mnie kwestia wydajności. Co jeśli mamy rozbudowaną klasę i bardzo często wykonujemy jakąś zmianę w jej środku? Nie wiem, np. klasa FermaKrólików, które ma trochę pól w sobie, w tym jakiegoś integera odpowiadającego za ilość królików (jak wiadomo ta liczba może dość szybko rosnąć :)). I teraz za każdym razem gdy wywołam ferma.dodajKrólika(), która mi zwiększa ilość królików, to ta metoda powinna stworzyć nową instancję klasy FermaKrólików, która w konstruktorze dostanie to samo co obecna ferma, z jednym wyjątkiem - nową ilością królików. Czy to nie jest średnio optymalne? A co jeśli w tej klasie trzymamy Immutable Listę Królików? I niech ich będzie milion. Za każdym razem mamy przepisywać milion królików + 1 do nowej listy, którą potem zamieniamy na immutable, np. za pomocą buildera? No bo to mi też nie wydaje się optymalne.

Co sądzicie? Chętnie dowiem się czegoś konstruktywnego.

13

trzy etapy jakie musi przejść każdy programista:

  1. programowanie bez żadnych zasad, zwykle byle jak
  2. ładne poprawne programowanie (tak żeby zachować wszystkie możliwe zasady ustanowione przez innych ludzi)
  3. wyższy level wtajemniczenia czyli pragmatyzm i świadomy wybór kiedy i jakie zasady stosować,

Ale zastanawia mnie kwestia wydajności. Co jeśli mamy rozbudowaną klasę i bardzo często wykonujemy jakąś zmianę w jej środku?

No i właśnie dlatego trzeba wiedzieć, kiedy stosować się do zasad, a kiedy trzeba poświęcić jakąś zasadę w imię np. wydajności (tak jak poświęca się gońca, żeby ocalić króla. Programowanie jest jak szachy, tyle, że łatwiejsze).

9

Koleś nie odróżnia DTO od ADM. Miesza różne odpowiedzialności w jednym obiekcie: klasa, która sama się zapisuje do bazy albo ręcznie (sic!) serializuje do XML, wynajduje na nowo wzorzec repozytorium (tylko nie opakowuje nim ORMa lecz pisze goły SQL)... Pierwszy na świecie fanatyk OOP, który łamie SRP i SoC. ;)

Co do immutable - generalnie, większość obiektów może taka być. Ale jeśli wiemy, że jakiś duży obiekt będzie często modyfikowany, to warto przemyśleć projekt - może da się go podzielić na część bardziej stałą i bardziej dynamiczną? A jeśli coś faktycznie musi być często modyfikowane, to raczej nie ma sensu robić tego immutable na siłę.

P.S. Problem geterów i seterów to problem chyba wyłącznie Javy, wynikający z braku bardziej ekspresywnych mechanizmów w języku.

2

P.S. Problem geterów i seterów to problem chyba wyłącznie Javy, wynikający z braku bardziej ekspresywnych mechanizmów w języku.

Bo C# ma właściwości? Wyjaśnij, bo mam wrażenie że problem widzisz w dodatkowych metodach.

To nie jest tak, że get i set to zło wcielone. Ludzie po prostu nadużywają tego na potęgę.
W teorii, możesz zrobić klasę Armory która będzie miała metody do składania i odbierania broni, a każda akcja będzie weryfikowana.
W praktyce robione są klasy Armory gdzie każde pole ma get/set (czy tam właściwość) i jazda z koksem. Każdy niech robi co chce. W praktyce wychodzi stuktura.

XML czy JSON, nawet pomimo tego że druga nazwa nawet sugeruje coś z obiektami, to tak naprawdę jedynie struktura danych, a nie obiekt. Tak samo większość ORM, powinna nazywać się SRM. Jeżeli pracujesz na danych, to jest to struktura, a jeżeli używasz zachowań, to masz do czynienia z obiektem.

Dlatego nie uważam że get/set czy w pełni odkryta właściwość to zawsze błąd. Możesz przecież dowiedzieć się (get) ile mam wzrostu, mimo że nie możesz tego zmienić. Z drugiej strony możesz dowiedzieć się czy światło jest zapalone i zmienić ten stan (get/set). Z tym że set/get w swojej najprostszej postaci, czyli zwróć lub ustaw pole, nie musi występować zawsze.

1

P.S. Problem geterów i seterów to problem chyba wyłącznie Javy, wynikający z braku bardziej ekspresywnych mechanizmów w języku

Inne języki mają właściwości z przezroczystymi setterami/getterami, co oznacza, że można pisać publiczne właściwości a potem je zamieniać na gettery/settery bez zmiany kodu.

Tylko, że myślę, że problemem nie jest wcale to "jak stworzyć gettera", tylko raczej, że:

  1. ludzie myślą, że ich potrzebują.

Pisząc obiektowo, stosując enkapsulację, powinno się w zasadzie ukrywać prywatne właściwości obiektów, a gettery/settery łamią tę zasadę. Poza tym istnieje zdroworozsądkowa zasada YAGNI (You Ain't Gonna Need It), a ludzie łamią i tę zasadę i piszą settery/gettery do każdej prywatnej właściwości, nawet kiedy nigdy nie będą tego potrzebować. Wymyślają sobie problemy na zapas.

  1. ludzie chcą pisać zawsze obiektowo

Jeśli ktoś robi dużo setterów i getterów to możliwe, że wcale nie pisze obiektowo, tylko potrzebuje zwykłych pojemników na dane, coś jak struktury z C/C++, czy tablicy asocjacyjne z PHP itp. Ja wtedy piszę publiczne właściwości w obiektach i nie martwię się, że to nie OOP (z getterami/setterami byłoby tak samo nie-OOP, bo w takim samym stopniu łamałbym enkapsulacje). OOP to nie jest święta krowa...

0
krzysiek050 napisał(a):

Bo C# ma właściwości? Wyjaśnij, bo mam wrażenie że problem widzisz w dodatkowych metodach.

Fanatycy Javy jak zwykle wszędzie widzą C#, mimo że nikt słowem tu o nim nie wspominał. :D :D :D

To nie ja widzę problem w dodatkowych metodach tylko autor tego bloga. To on płacze z powodu pisania get i set.

LukeJL napisał(a):

Jeśli ktoś robi dużo setterów i getterów to możliwe, że wcale nie pisze obiektowo, tylko potrzebuje zwykłych pojemników na dane, coś jak struktury z C/C++, czy tablicy asocjacyjne z PHP itp. Ja wtedy piszę publiczne właściwości w obiektach i nie martwię się, że to nie OOP (z getterami/setterami byłoby tak samo nie-OOP, bo w takim samym stopniu łamałbym enkapsulacje). OOP to nie jest święta krowa...

Też się nie martwię, że to nie OOP. To nie moja wina, że mainstreamowe języki OOP nie mają oddzielnego mechanizmu do tworzenia pojemników na dane i obiektów. A pojemniki na dane są niezbędne do rozwiązywania wielu problemów w wielu rodzajach aplikacji.

Poza tym, dobre ORMy wcale nie wymagają pojemnika na dane do działania, poradzą sobie także z prawdziwym obiektem z prywatnymi polami. Tylko dla mnie to jest złamanie SRP, więc sam takiego podejścia nie stosuję.

1

Autor nie płacze z powodu pisania get i set tylko dlatego, że niczym się to nie różni od stosowania pól publicznych i nie ma to nic wspólnego z enkapsulacją.
https://martinfowler.com/bliki/TellDontAsk.html

0
somekind napisał(a):
krzysiek050 napisał(a):

Bo C# ma właściwości? Wyjaśnij, bo mam wrażenie że problem widzisz w dodatkowych metodach.

Fanatycy Javy jak zwykle wszędzie widzą C#, mimo że nikt słowem tu o nim nie wspominał. :D :D :D

To nie ja jestem fanatykiem Javy, tylko Ty jej przeciwnikiem. C# wspomniałem bo ma właściwości, co jest nowszym i ładniejszym podejściem, mimo że wcale nie kluczowym.

0
caer napisał(a):

Autor nie płacze z powodu pisania get i set tylko dlatego, że niczym się to nie różni od stosowania pól publicznych i nie ma to nic wspólnego z enkapsulacją.

Różni się odpornością na zmiany. Do metody możesz dodać logikę (np. walidację ustawianej wartości), do pola jej nie dodasz. Mając metodę od razu, możesz wprowadzać zmiany bez zmuszania klientów do zmiany ich kodu.

krzysiek050 napisał(a):

To nie ja jestem fanatykiem Javy, tylko Ty jej przeciwnikiem.

No tak, zapomniałem, że dla fanatyków Javy pisanie o faktach jest hejtem. :D :D :D

C# wspomniałem bo ma właściwości, co jest nowszym i ładniejszym podejściem, mimo że wcale nie kluczowym.

Delphi ma właściwości od dawna. Czemu nie wspomniałeś Delphi? Czemu ta paranoja dotyczy tylko C#?

0

C# wspomniałem bo ma właściwości, co jest nowszym i ładniejszym podejściem, mimo że wcale nie kluczowym.

Taka nowość że już Common Lisp je miał :D

0

Garbage Collectory raczej lubią sprzątać Immutables ;)

1

NIe widze kontrowersji...

  1. Getery i setery = zło. No tak. A raczej po prostu bezedura.
    Btw. dobre biblioteki do JSONa (np.: Jackson) albo dobre "ORMy" czyli JOOQ getterów (JavaBeanów) nie potrzebują.

  2. ORM to zło - a raczej JPA to bryndza w 90% projektów. Skomplikowany lifecycle obiejktów, którego programiści nie ogarniają (dirty check/ managed/ detached). Jaja z transakcjami/ flush itp.
    Dziedziczenie/proxy to temat na osobną kategorię katastrof. A można po prostu pisać w SQL (jeżeli już ktoś jest tak głupi żeby bazy danych używać :-)). Mam zespole co najmniej raz w tygodniu śmiech jak człowiek wie jak coś zrobić w SQL, ale traci całe godziny na przełożenie tego na JPA. (nawet głupi mapping one to many co chwila pokazuje jakieś problemy ).
    MyBatis lub JOOQ jest prostsze i pozwala pisać mniej magiczny kod. Na dłuższa metę sprawdza się - mniej czasu traci zespół na wyjaśnianie cudów, znikających obiektów z bazy, nie zapisanych zmian itp.

Ogólnie to lubię czytac gościa - czasem coś walnie, ale ma dużo racji. Walczy z tym samym co ja czyli nonsensownym niewłaściwym użyciem technologii, na które cierpi całe IT.
Silver bullet / golden hammer to niestety problemy ludzkiego umysłu. Jakby to przełożyć na czasy jaskiniowe wyglądało by tak:
Jaskiniowiec1: hej - wynalazłem coś super... patrzcie!
Jaskinowcy2-7: o ... ale co to
Jaskiniowiec1: to koło - patrzcie mogę dzięki temu przetoczyć ten kamień prawie bez wysiłku
Jaskiniowiec4: ale super - od dziś będziemy rozpalali ogień tylko tym.
(no bo przecież się da : http://www.survivaltube.pl/ogien/106-rozpalanie-ognia-przy-pomocy-kola-zamachowego)

0
somekind napisał(a):
caer napisał(a):

Autor nie płacze z powodu pisania get i set tylko dlatego, że niczym się to nie różni od stosowania pól publicznych i nie ma to nic wspólnego z enkapsulacją.

Różni się odpornością na zmiany. Do metody możesz dodać logikę (np. walidację ustawianej wartości), do pola jej nie dodasz. Mając metodę od razu, możesz wprowadzać zmiany bez zmuszania klientów do zmiany ich kodu.

Bardziej mi chodziło o to, że koniec końców i tak wystawiamy na zewnątrz dane, które niby miały być 'zamknięte'. Autor się pruje, że obiekty to nie pojemniki na dane i to mnie dziwi trochę, bo czasami takie pojemniki się po prostu przydają, do pogrupowania kilku rzeczy w jedno.

jarekr000000 napisał(a):

(...)
Dziedziczenie/proxy to temat na osobną kategorię katastrof. A można po prostu pisać w SQL (jeżeli już ktoś jest tak głupi żeby bazy danych używać :-)). Mam zespole co najmniej raz w tygodniu śmiech jak człowiek wie jak coś zrobić w SQL, ale traci całe godziny na przełożenie tego na JPA. (nawet głupi mapping one to many co chwila pokazuje jakieś problemy ).
(...)

To jakiś bait? Co jest złego w bazach danych? A raczej - jaka jest lepsza alternatywa?

2

@Naitoreivun
See
W dużej cześci systemów składowanie danych w bazie danych ( shared mutable state) to porażka. Głównie dlatego, że zwykle szybko się okazuje, że jeden model nie wystarcza.
Proponuje na ogół dwa rozwiązania: dla większych systemów pełny CQRS/ES - taki jak np. w Lagom(java). Taki jak opisuje Greg Young.
Dla mniejszych "dziecięcy ES" czyli prevayler (moje videło //vimeo.com/192629524)
Pierwsze jest o wiele bardziej skalowalne i eleganckie od klasycznego backendu bazodanowego (przy czym baz typu SQL itp możesz nadal w ramach CQRS (ReadSide) używać (ale nie do składowania danych)).
Drugie jest mega proste i szybkie.
To powoduje, ze nie bardzo widzę sens ładowania się w klasyczne rozwiązania oparte o perzystencję w DB.
W obu przypadkach (CQRS/ES i Prevayler) powstający kod jest o wiele ładniejszy (z punktu widzenia clean code/ clean architecture).

1 użytkowników online, w tym zalogowanych: 0, gości: 1