o prawach i zasadach
Jakiś czas temu pisałem o Law of Demeter, o tym o co w tym właściwie chodzi oraz o plusach przestrzegania prawa :) Dzisiaj chciałbym przybliżyć Wam zasadę Tell, don’t ask, która w moim odczuciu jest punktem wyjścia do wspomnianego wcześniej prawa, przestrzeganie LoD jest jednym z następstw stosowania zasady TDA.Dobra, ale o co prosić, a o co nie pytać?
pytania i prośby, czyli „że co?”
Krótko podsumowując, zasada TDA mówi nam o tym, żebyśmy zamiast pytać obiekty o dane prosili je o wykonanie operacji na tych danych i zwrócenie nam wyniku przeprowadzonych instrukcji.Czyli zamiast :
Age age = sebastian.getAge(); if (age >= 21) { letDoTheThingsAdultDoes(sebastian); }
mielibyśmy coś takiego:
if (sebastian.isAdult()) { letDoTheThingsAdultDoes(sebastian); }
Czytelniej? Lepiej? Gorzej? A może przerost formy nad treścią i tak naprawdę to wszystko jedno?
Cóż, z zasadami tak to już bywa, że czasami warto je łamać :)
poproś o wynik, nie o dane
Powyższy przykład dobrze obrazuje to, co omawiana zasada chce chronić. Mowa tu o jednym z podstawowych paradygmatów programowania zorientowanego obiektowo, a mianowicie o enkapsulacji. Nie wydobywamy danych na zewnątrz, nie wyciągamy wnętrzności obiektów i nie operujemy na nich bezpośrednio. Zlecamy wykonanie działań temu obiektowi i czekamy na wynik, którym jesteśmy zainteresowani. W końcu do czyich odpowiedzialności powinno należeć operowanie na atrybutach obiektu?Jednak nie można popadać w przesadę. Oczywiście enkapsulacja jest bardzo istotna. Ważne jest aby obiekt posiadał odpowiedni interfejs, z którym można współpracować, lecz wyobraźcie sobie co by się stało, gdybyśmy nagle zabronili (zmienili zasadę w prawo) wyciągania danych z obiektu?
czy zawsze „don’t ask”?
Każda kolejna operacja to dodatkowe linijki kodu, to dodatkowe odpowiedzialności, które co prawda są zorganizowane wokół jednego kontekstu… tylko czy to jednak nie za dużo?Z rygorystycznym pisaniem kodu w myśl zasady TDA idą w parze dwa główne problemy. Po pierwsze, klasy mogą urosnąć do niewyobrażalnych rozmiarów. Jeżeli ochrona atrybutów stanie się naszą paranoją i każdą operacja która jest na nich przeprowadzana będzie realizowana przez obiekt to może się w pewnym momencie okazać, że ilość linijek jej kodu będzie przytłaczająca, a sam kod trudny do ogarnięcia.
Za ilością kodu często idzie zwielokrotniona ilość odpowiedzialności oraz zależności. Kod jest co prawda zorganizowany wokół pewnej klasy i jej instancji, niemniej jednak zachowania i operacje nic więcej wspólnego ze sobą nie mają. Dodatkowo, jeżeli same operacje, funkcjonalność sama w sobie jest złożona (np. skomplikowany algorytm), to istnieje prawdopodobieństwo zwiększenia ilości relacji.
A co w przypadku, gdy dane zachowanie jest wspólne dla kilku obiektów, które są instancjami różnych klas? A niestety wykonanie operacji wymaga sięgnięcia po część danych, które posiada obiekt?
Jak widzicie nie jest to wcale tak proste i oczywiste jak może się wydawać.
działania na wielu danych
Jeszcze ciekawszym przypadkiem jest sytuacja, gdy musimy przeprowadzić operację na kilku obiektach i przeprowadzenie tej operacji wymaga dostępu do danych.Prostym przykładem może być potrzeba obliczenia średniego wieku ludzi w grupie. Jak to zrobić bez bezpośredniego dostępu do ich wieku?
Czy tutaj też prosimy o wykonanie operacji, czy raczej zapytamy o dane?
reasumując…
Jak więc widzicie, od zasad (Tell, don’t ask) odstępstwa są dużo bardziej dopuszczalne niż łamanie praw (Law of Demeter). Oczywiście zawsze trzeba się kierować zdrowym rozsądkiem, ale w większości przypadków można założyć, że nieprzestrzeganie prawa zapewne będzie wiązało się z poważnymi konsekwencjami, natomiast w wypadku zasad może nam to wyjść na dobre. Nawet długofalowo :)I na koniec kilka linków dla tych, którym ciągle mało:
- http://martinfowler.com/bliki/TellDontAsk.html
- http://c2.com/cgi/wiki?TellDontAsk
- http://coding-is-like-cooking.info/2013/05/tell-dont-ask-object-oriented-design/
- https://pragprog.com/articles/tell-dont-ask
Super artykuł. Pozdrawiam serdecznie.
OdpowiedzUsuń