...najbardziej trafną metaforą obiektów programistycznych jest organizm biologiczny. Podobnie jak komórki, obiekty "nie wiedzą", co dzieje się wewnątrz innych obiektów, ale komunikują się z nimi realizując bardziej złożone cele. W przeciwieństwie do takiego organizmu program monolityczny przypomina mechanizm zegarka, zawierający nieprzeliczalną (sic!) liczbę trybików. Trybiki nie mają żadnej inteligencji i są bezużyteczne poza mechanizmem, w którym pracują. Taki projekt nie ma szans powodzenia. Budując mechanizmy zegarowe, dochodzisz w końcu do takiej złożoności, że całość przestaje funkcjonować.
Heh czyli powiedzenie "działa jak w zegarku" nie jest komplementem lecz inwektywą:P
Cytat pochodzi z książki Projektowanie obiektowe - Role, odpowiedzialność i współpraca autorstwa Rebeki Wirfs-Brock i Alana McKean, którą to chciałbym w niniejszym poście zarekomendować.
/*
A na marginesie przypomnę, że Rebeka jest autorką ciekawej prezentacji krytykującej podejście do projektu polegające na rozpoczynaniu od zaprojektowania bazy danych - życia nie oszukasz.
*/
Na wstępie uprzedzam iż książka jest mocno abstrakcyjna a miejscami wręcz filozoficzna - raczej nie przypadnie do gustu tym, który idą z duchem czasu - czasu napier....
Książka Rebeki i Alana wprowadza nas w świat Responsibilty-Driven Design. Świat, w którym posłujemy się bytami o ściśle określonych rolach. Nie ma w nim miejsca na radosną twórczość, boskie klasy o wszechmocnej odpowiedzialności i tony spaghetti kodu. RDD z założenia ma na celu uporządkowanie chaosu i przygotowanie się na przyszłą i nieuniknioną ewolucję systemu.
W jaki sposób? O tym właśnie traktuje cała książka. Natomiast w skrócie sposób zasadza się na świadomym i konsekwentnym przypisywaniu tytułowych ról, odpowiedzialności i współpracy.
Czym jest współpraca i odpowiedzialność - myślę, że wszyscy intuicyjnie wiemy. Zasady GRASP są jasne w tych kwestiach:
- współpraca - utrzymujmy jak najmniej powiązań (Low coupling)
- odpowiedzialność - im niej tym lepiej (najlepiej niech klasa ma jedną odpowiedzialność) - w skrócie: dążymy do wysokiej kohezji klasy.
Natomiast tytułowe role to nic innego jak pewne zakresy odpowiedzialności. W RDD nie bawimy się już radośnie definiując na chybił trafił klasy a w nich metody (najlepiej statyczne hehe) typu załatwZprawę() albo zróbDobrze(). Nie, w ten sposób daleko nie zajdziemy - co najwyżej do wersji 1.0. W RDD myślimy nie na poziomie klasek ale na poziomie bytów pewnych typów zajmujących się ściśle pewnymi odpowiedzialnościami.
W RDD wyróżniamy zestaw głównych stereotypów ról, które mogą być grane przez nasze obiekty:
- magazyn informacji (information holder) - przechowuje i dostarcza informacje
- łącznik (structurer) - utrzymuje relację pomiędzy obiektami oraz informacje o tych relacjach
- dostawca usług (service provider) - wykonuje zadania oraz, w ogólności, oferuje usługi obliczeniowe
- koordynator (coordinator) - obsługuje zdarzenia przez przekazywanie zadań do innych obiektów
- kontroler (controller) - podejmuje decyzje i bezpośrednio kieruje zadaniami innych obiektów
- transformator (interfacer) - przekształca informacje i żądania pomiędzy różnymi częściami systemu
Można oczywiście zadać sobie pytanie: co mnie obchodzi jakiś RDD? To pewnie kolejna ściema dzięki której ktoś miał temat na książkę, kilka prezentacji w fajnych hotelach, temat na blogaska i oczywiście... źródło łatwych dochodów;P
Wg mnie warto jednak zastanowić się nad paroma koncepcjami zawartymi w RDD ponieważ wydają się one być uogólnieniem Domain Driven Design. W DDD mamy building blocks (o których już pisałem) służące do modelowania dziedziny: encje, VO, agregaty, polityki, servisy, repo i fabryki.
RDD unosi problem standardowych artefaktów na nieco wyższy poziom abstrakcji. Wspomniane stereotypy ról pokrywają building block z DDD. Ale stereotypy ról nie ograniczają się jedynie do klocków służących do modelowania dziedziny. Standardowe role pokrywają cały system - nie tylko warstwę biznesową.
//======================
A co by było gdyby język wspierał role na poziomie składni;)))? Gdyby zamiast public class TaxService{} deklarować public service Tax{} albo z innej beczki: public coordinatror EventManager{}. Wówczas może kompilator mógłby jakoś magicznie sprawdzać czy klasa nie robi tego co do niej nie należy i chronić zbyt cwanego programistę przed stworzeniem Potwora Spaghetti:)
Eeee... chyba wczorajsza dawka strawy z nadmierną ilością ryb i niedostatkiem wieprza, do której mój prasłowiański żołądek nie jest przystosowany, skutkuje dzisiejszym majaczeniem;)
Przecież na początek wystarczyłyby adnotacje w jakimś nowym, pięknym frameworku:)