...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:)
7 komentarzy:
W zasadzie to wystarczy określenie odpowiedzialności w nazwach klas i prowadzenie audytu kodu + uwagi dla piszących źle.
Właściwie to weryfikacja kodu była tylko jednym z aspektów mojego schorowanego pomysłu.
Myślałem też (o czym nie napisałem) o jakimś frameworku, który w sposób specjalny traktowałby obiekty różnych ról - coś jak np adnotacje @Entity, @Statefull, @Stateless w EJB3.0
A co do audytów to zgadzam się - teoretycznie, bo wiemy jak to w praktyce wygląda;P
Książka jest naprawdę dobra, także ją polecem :-) A tak przy okazji to Rebecca Wirfs-Brock jest też "populyzatorką" DDD
http://domaindrivendesign.org/events/oopsla2007/oopsla2007.html#Rebecca
Zapowiada się ciekawie. Jak mi żona zniesie bana na zakupy w księgarni to zapewne sięgnę po tą pozycję.
Co do wsparcia na poziomie składni. Hm... w Jave masz adnotacje. W EJB3 masz @WebService i insze. Wystarczy tylko dopisać magiczne puzderko, które będzie sprawdzało czy klasa robi to co ma robić.
Z tą składnią to był oczywiście żart (taki nieśmieszny branżowy żarcik - przepraszam:P).
Już wyjaśniam o co chodzi mi z tym specjlanym traktowaniem otagowanych (np adnotacjami) obiektów.
Od jakiegoś czasu chodzi mi po głowie taka mysl: framework aplikacji. Pisząc framework apliklacji mam na myśli coś co na prawdę wspiera budowanie kompletnej aplikacji. Mama na mysli coś więcej niż tylko framework przykrywający szczegóły techniczne (rozproszenie, transakcje, współbieżnośc, http, bindowanie danych, itp) grubszą lub cieńszą wartswą abstrakcji.
Chodzi mi o coś co jest oparte na jakimś w miare giętkim modelu apliakcji. Przykładowo: załóżmy, że framework zakłada n eleganckich warstw i zajmuje się przeprzyływem sterowania między nimi oraz ogólnie wszytkimi "stałymi fragmentami gry". Ewentualnie daje nam możliwość zadeklarowania, który z 5,10 czy 100 standardowych predefiniowanych odmian danego fragmentu zastosowac.
Rola programisty polegałby na dostarczeniu jedynie klas o odpowiednich rolach a framework umieszczałby je sobie gdzies na drabinach swoich warstw. Framework apliakcji zajmowałby się zapewnieniem współpracy obiektów o odpowiednich rolach. Przykładowo (w kotekście ról RDD) to framework używałby transformatora (siedzącego np pomiedzy warstwą logiki apliakcji a gui) do przepakowania wyników z dostawcy usług.
Książeczkę kupiłem w tamtym roku ;) ale mimo zacięcia filozoficznego lat młodzieńczych pozycja wydłuża listę TODO do przeczytania.
"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ę"
Fajnie by było gdyby kompilator umila zweryfikowac semantyczne znaczenie kodu tylko ze wtedy pewnie umiał by tez go wygenerować :) Takie cuda obiecywano przy okazji boomu na narzędzia CASE i wcześniej przy powstawaniu komputerów :) Zatem w najbliższej przyszłości programiści będą dalej mieli możliwość zarabiania na miseczkę ryżu :D Inna sprawa z dołożeniem jakichś metadanych określających rodzaj komponentu tylko czy ma to jakąkolwiek przewagę nad np konwencjami nazewniczymi skoro nie da się tego wymusić?
Wydaje mi się, że różnorodność sposobu funkcjonowania różnych biznesów czy nawet bardzo drastyczne zmiany jakie wymusza zmiana skali funkcjonowania uniemożliwiają opracowania "w miarę giętkiego modelu aplikacji".
Z drugiej strony może nie trzeba opracowywać takiego modelu. Niech zrobi to projektant mając do dyspozycji jakieś bardzo ogólne typy komponentów czyli idziemy w kierunku BPM.
W Pragmatic programmer'e A. Hunt i D. Thomas zalecają ... kompletnie inne podejście. Zamiast produkować superframework lepiej za pomocą posiadanych narzędzi opracować język specyficzny dla domeny danego problemu.
Jeszcze 2 skojarzenia:
1) ogolny framework tworzenia aplikacji -> superframework -> superklasa -> ...
2) ogolny framewokr tworzenai aplikacji -> framework do wszystkiego -> ...
Z ostatniej strony: młody jestem (niecałe 2,5 w IT) więc może jeszcze jakieś furtki w głowie mi się nie otworzyły i jestem opornikiem blokującym drogę nieuniknionemu postępowi :)
Zazwyczaj z czasem to się furtki zamykają, a nie otwierają ;)
Prześlij komentarz