piątek, 26 czerwca 2015

Prosić czy pytać?

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: