niedziela, 24 sierpnia 2008

The Unforgiven II

Kontynuując wątek prezentacji Davea Thomasa, którą wczoraj polecałem...
W dalszym ciągu jestem pod wrażeniem... luźna forma, cięty humor, trafność i błyskotliwość przykładów z zachowaniem ogólności... po prostu majstersztyk.

Już chyba sama świadomość rozpoczynającego się dwutygodniowego urlopu odblokowała moją kreatywność więc dopiszę coś jako kontynuację.

Tak jak wspominałem ostatnio przydałoby się poszerzyć rozważania Davea o aspekty typów osobowości (np wg Belbina) i metaprogramów, ale nie będę się póki co wydurniał bo to nie moja profesja (póki co).

Zamiast tego spróbuję "zamapować" fazy Dave na standardowe cztery etapy uczenia się.
Zatem standardowy (nie wspierany przez np. NLP) proces uczenia wygląda jakoś tak:

1. Nieświadoma niekompetencja - spokojnie sobie pracujesz w przekonaniu o własnej wspaniałości i wszechwiedzy. Programujesz tak jak zwykle, projektujesz (albo i nie) wg sprawdzonych nawyków. Nawet nie masz pojęcia, że gdzieś tam w wielkim świecie ludzie podniecają się nowym frameworkiem czy metodyką. Po prostu jest git i nie trza psuć.

Jeżeli jesteś typem innowatora to wrodzona ciekawość każe Ci szukać nowych możliwości. Nie dlatego że potrzebujesz nowego młotka do swej skrzynki z narzędziami, po prostu... robisz to dla czystej przyjemności. Nagle odkrywasz coś, co wg Twojej kobiecej;) intuicji jest lepsze od wszystkiego co do tej pory używałeś. Po prostu czujesz, że to coś zdefiniuje Twoje dotychczasowe nawyki jako nieelegancki czy nawet głupie.

Jeżeli natomiast jesteś np galernikiem i siedzisz w pracy aby mieć kasę na codzienną wegetację może Ci sie przydarzyć przykra sprawa: pewnego pięknego dnia ktoś przychodzi i każe Ci użyć nowego młotka - np jakiegoś Springa czy Qi. Nie jest Ci to na rękę bo wymaga pewnego wysiłku umysłowego (znowu). Co gorsza - ZMIANA. AAAAAA zmiany są takie straszne.

2. Świadoma niekompetencja - Tak czy owak stało się. Stoisz przed czymś nowym i nic nie rozumiesz. Powoli zaczynasz się uczyć, lub uczą Cię (jeżeli masz szczęście - chociaż czasem lepiej nie mieć nauczyciela niż mieć słabego).

W zależności od wypracowanych strategii uczysz się krok po kroku na małych przykładach albo wolisz najpierw objąć ogólną ideę. Masz pecha gdy nauczyciel albo autor materiałów akurat preferuje strategię odwrotną do twojej:/

Używasz tego nowego młotka, ale dobrze wiesz, że jeszcze nie idzie Ci to zbyt dobrze. Coś się nie inicjalizuje, context wyrzuca NullPointerException, widok się nie renderuje.

Wg Davea jesteś Nowicjuszem albo Zaawansowanym początkującym i chcesz po prostu wreszcie zrobić coś co zatrybi (choćby zgrzytając).

3. Świadoma kompetencja - Z czasem stałeś się Kompetentny. Wiesz już jak zrobić aby działało, potrafisz nawet używać bardziej zaawansowanych ficzerów frameworka czy innego narzędzia, rozwiązujesz problemy mniej doświadczonych kolegów. Znowu jest git, jest stabilnie i bezpiecznie.

Ale niektórzy zaczną drążyć głębiej. Zaczną zaglądać pod maskę. Zobaczą tam rzeczy, o których istnieniu nie mieli nawet pojęcia. Z czasem zrozumieją jedno: "wiem, że nic nie wiem". Zaczną zgłębiać teorię stojącą za mechanizmami narzędzia. Staną się Biegli dzięki rozumieniu na wskroś narzędzia, które stanie się przeźroczyste jak szyba. Przykładowo znajomość wzorców projektowych czy struktur architektonicznych przełoży się na kompletne zrozumienie kształtu frameworka, który stanie się wręcz oczywisty.

4. Nieświadoma kompetencja - Z czasem nowa wiedza poszerzona o skojarzenia z innymi dziedzinami, które zostały odkryte podczas jej zgłębiania stanie się zasymilowana do tego stopnia, że przejdzie do różnych obszarów nieświadomości. Po prostu zaczniesz intuicyjnie myśleć w nowy sposób. Np wzorce projektowe staną się naturalnym sposobem myślenia i projektowania. Kształt narzucany przez framework stanie się naturalnym kształtem jaki nadajesz swoim projektom. Jesteś po prostu Ekspertem.

The door is closed, so are you're eyes
But now I see the sun, now I see the sun
Yes now I see it




BTW: Przedstawione kroki można przejść wstecz (4,3,2) w celu oduczenia się niepożądanych nawyków. Np student, który wychodzi z uczelni jako Ekspert programowania proceduralnego może uświadomić sobie własny styl myślenia - przejść z kompetencji nieświadomej do świadomej. Czyli powinien zacząć zastanawiać się co w ogóle robi, po co i jakie to ma konsekwencje. Później już tylko krok do świadomej niekompetencji i poszukiwania alternatyw aby iść znowu do przodu.


//============================
Oczywiście nie ma ważnych rzeczy, o których nie napisałby już Alex:)
Możliwy jest przeskok z drugiego do czwartego etapu Nieświadomej kompetencji: Świadoma kompetencja - czy można ją pominąć?
Uwaga na specjalizację: Generalista, czyli umiejętne zastosowanie Zasady Pareto

sobota, 23 sierpnia 2008

The Unforgiven

Jak spożytkować godzinę czasu aby później jej nie żałować...?
Proponuję refleksję nad sobą, swą pracą, swym rozwojem i karierą: Developing Expertise: Herding Racehorses, Racing Sheep.
Godzina czasu spędzona przed "filmikem z netu" może wydawać się nudnym sposobem na spędzenie popołudnia, ale może warto wreszcie zrobić coś dla siebie po całym tygodniu robienia różnych rzeczy dla innych.

Dave mówi na tyle ogólnie, że aplikuje się to dla każdego , nie tylko dla programersów i jest adekwatne do wszystkich aspektów życia. I gwarantuję, że nie będziecie się nudzić - w razie czego w ramach rekompensaty stawiam każdemu kto się znudzi butelkę whisky:)


Dave (kurcze, tego faceta nie da się nie lubić) przedstawia pięciostopniową drogę rozwoju. Co prawda całość jest osadzona w kontekście IT, ale tak jak już wspomniałem treść jest uniwersalna - szczególnie, że Dave snuje paralele (trudne słowo) do nauki chodzenia, gotowania, ba nawet nauki pilotażu. Dave opisuje takie oto fazy kompetencji:

New blood joins this earth...

Novice - robisz coś po raz pierwszy, kompletnie nie wiesz o co chodzi. Nowy członek teamu jest nastawiony na szybką relizację prostego zadania. On po prostu potrzebuje małych sukcesów. Nie obchodzi go biger pikczer, po prostu chce zrobić coś co działa.

Advanced begginer - powoli zaczynasz rozumieć o co mniej więcej chodzi. Uczysz się. Twój mozg zaczyna zauważać powtarzalne wzorce - i jak zauważa Dave nie da się tego w żaden sposób wyłączyć - jest to jego naturalna cecha. Jednak w dalszym ciągu potrzebujesz kogoś, kto powie Ci co trzeba zrobić.

Competent - Mała larwa przepoczwarza się w samodzielnie myślącą bestię. Całkiem dobrze orientuje się w wyuczonej domenie a samodzielne myślenie pozwala mu na planowanie działań na przyszłość.

Proficient - Odkrył, że jest jeszcze coś więcej i zrozumiał, że tak na prawdę nic nie wie. Zaczyna syntetyzować wiedzę z innych dziedzin aby rozumieć więcej i lepiej. Upierdliwy typek. Zadaje pytania, filozofuje - z tego powodu jest pierwszy do zwolnienia;)

Expert (Wizard) - zasób wiedzy i skojarzeń jest tak ogromny, że mózg optymalizując procesy myślowe wykonuje je w nieświadomości (niższe warstwy kory nowej? może po prostu w skrócie intuicja). Wizard mówi (i zapewne rozumuje) w oparciu o metafory ponieważ nauczył się, że wszystko zależy od kontekstu. Ekspert stale jest głodny wiedzy dlatego nie pytaj go jakich frameworków użyć w nowym projekcie bo skonstruuje frankensteina (wiem, wiem, że Frankenstein był naukowcem, który to dopiero stworzył potwora, ale tak sie mówi) tylko po to aby sprawdzić jak to będzie ze sobą działać:)))


Na zakończenie dowiemy się, która grupa jest najbardziej liczna, dlaczego ludzie zatrzymują się na pewnym poziomie i dlaczego jest to wygodne zarówno z punktu widzenia firmy jak i większości pracowników oraz dlaczego jest to zgubne na dłuższą metę. Ale nie będę Wam robił spoilera;P

//============================
Sądzę, że model Davea jest dosyć mocno uproszczony. Nie uwzględnia np. typów osobowości o których pisałem i obiecałem napisać więcej. Zresztą, najlepiej poczytajcie sami, np tu. W skrócie: różne osobowości mają różne priorytety - w szczególności parcie na rozwój, poszerzanie kompetencji itp.

Innym pominiętym aspektem są metaprogramy. W szczególności różnice w podejściach Szczegółowe — Globalne (przewiń w dół do paragrafu "METAPROGRAM VI: SZCZEGÓŁOWE — GLOBALNE").

Jednak na podkreślenie zasługuje kilka ważnych myśli, które poruszył Dave...
Chyba nie znajdziecie lepszego ich rozwinięcia niż te proponowane przez Alexa:

Czy twoje 20 lat doświadczenia w ogóle coś znaczy, czy może możesz pochwalić jednym rokiem doświadczenia powtórzonym 20 razy???

oraz

Czy jesteś dojną/mięsną krową?

Czi oriented

Główną wadą OOP jest to, że nie jest w ogóle Object Oriented!

Jeżeli jesteś ciekawy, kto ma na tyle tupetu aby coś takiego napisać to zapraszam tu: http://www.qi4j.org/

Natrafiłem na ten wynalazek po zapisaniu się na grupę DDD gdzie jest lansowany przez paru uczestników.

O ile być może nie koniecznie warto od razu tworzyć w Qi nowy system, to na pewno można zaczerpnąć z niego parę odświeżających idei, koncepcji czy nawet wzorców.

//=======================
OOP nie jest OO - heh szczyt bezczelności;)
Mimo tego będę zgłębiał temat, więc z czasem spodziewajcie się nowych postów otagowanych "Composite Oriented Programming" albo "Qi".

niedziela, 10 sierpnia 2008

Z pamiętnika zdomenowanego modelarza: #3 Artefakty biznesowe

Po krótkim wstępie do Domain Driven Design i po zdawkowym opisie architektury oraz otrząsnąwszy się z ulgą z dysonansu czas przyjrzeć się artefaktom z warstwy domenowej.

W kontekście trzech ostatnich dyskusji (głównie pozablogowych) spróbuję zsyntetyzować zagadnienia podstaw OO (kontrowersyjne dziedziczenie, wzorce projektowe) z paradygmatami DDD.

W DDD logikę domenową modelujemy za pomocą kilku rodzajów artefaktów (które Evans w swej książce nazywa Building Blocks). Mamy zatem niejako do wyboru kilka podstawowych figur, którymi możemy zagrać na różne sposoby:

Encje - rozróżnialne (w przeciwieństwie do Value Objects) obiekty. Intuicyjnie jest to coś co mapujemy na tabele (nie koniecznie 1-1 i nie koniecznie wszystkie atrybuty). W DDD ważne jest aby encje miały oprócz atrybutów również odpowiedzialność (metody biznesowe) - w przeciwnym wypadku mamy do czynienia z anemicznym modelem, o którym już pisałem.

Value Objects (VO) - po prostu opisują jakiś rzeczy i nie są rozróżnialne (w przeciwieństwie do encji). Zazwyczaj nie powinny być modyfikowalne - można je współdzielić więc modyfikacja w jednym miejscu popsuła by szyki w innym. O ile encje zazwyczaj odczytujemy z bazy (lub tworzymy po to aby je tam zapisać) to VO zwykle powstają w wyniku działania Servisu.

Servisy - czasem po prostu pewnych odpowiedzialności nie możemy w naturalny przypisać do żadnej encji ani VO. Wówczas potrzebujemy servisu, który się tym zajmie. Zwróćmy uwagę na podejście - Servis jest na równym poziomie (warstwie) z Encją! Nie jest to servis aplikacyjny (leżący ponad warstwą domenową) a servis biznesowy - artefakt tej samej "wagi" co encja.

Agregaty - rzeczywistość nigdy nie jest na tyle prosta by zamodelować ją przy pomocy jednej Encji. Agregaty to nic innego jak drzewa encji i VO. Ideą agregatu jest to, że mamy dostęp do "roota", który jest niejako interfejsem - to zawsze jemu wydajemy rozkazy - natomiast w jego gestii leży ewentualne delegowanie odpowiedzialności do swych składników.
Łańcuszki typu getCzłowiek().getUkładTrawieia.getŻołądek().add(new Kiełbasa(3)) są niedopuszczalne ponieważ łamią paradygmat enkapsulacji (który o zgrozo jest mylony z prywatnymi polami i publicznymi getterami/setterami). Oczywiście w tym przykładzie człowiek powinien mieć jakiś interfejs (w sensie metodę a nie w sensie słówka kluczowego interface) do karmienia go.
Agregaty są jako spójna całość zapisywane i odczytywane. Z agregatem łączy się kolejny artefakt (już raczej techniczny niż projektowy) fabryka - hermetyzująca po prostu złożony proces tworzenia nowego agregatu (tworzenia nowego a nie odczytywanie istniejącego ze źródła danych).

Polityki - last but not least. Po prostu stary dobry u sprawdzony wzorzec strategii. Gdy jakiś proces biznesowy (lub jego etap) jest alternatywny, lub spodziewamy się jego zmiany (jest niestabilny lub nie do końca przeanalizowany) wówczas enkapsulujemy go poza stabilny interfejs. Znowu enkapsulacja trochę bardziej wyrafinowana niż prywatne pole i publiczny getter/setter. Przy okazji (już o tym pisałem, ale nigdy za wiele powtarzać) trzymamy się GRASP.


Podsumowując w kontekście dziedziczenia: Jak widać DDD zachęca raczej do agregacji. Na dziedziczenie możemy sobie pozwolić np w politykach. Same polityki agregujemy we wnętrzu Servisów, Encji czy VO, ale na niższym poziomie - poziomie implementacji możemy śmiało i bez obaw zagrać dziedziczeniem. Polityka jest koherentna, bo z definicji posiada ściśle określoną jedną odpowiedzialność więc nie będzie tu raczej problemów opisywanych ostatnio.

Zatem patrząc ogólniej: dziedziczenie jest przeniesione z poziomu Encji, VO i Servisów na poziom składników - polityk. W wyniku zamiast definiować kilka różnych klas np Klientów (zwykły, vip, lojalny itp) możemy zaprojektować jedną klasę klienta składającą się między innymi z PolitykiRabatowej, która jest interfejsem. A jak to się w przyszłości rozwinie - nie jest to ważne z punktu widzenia klasy Klienta. Po kolejny przykład zapraszam tu.

//=================================
Porównajmy to z klasycznym podejściem. Znajdujemy rzeczowniki, projektujemy bazę, generujemy z niej klasy encji (właściwie to pascalowe rekordy, albo stuct z C). Później już tylko potrzeba paczek procedur (czasowniki) zamkniętych w klasy (po co te klasy? tylko przeszkadzają - nakładanie gaci przez głowę) i gotowe. Aby bylo bardziej biznesowo to rekordy umieszczam w pakiecie modelu albo domain a paczki procedur w pakiecie services. Radośnie programuję w Pascalu w Javie:)

czwartek, 7 sierpnia 2008

Telefonistka dziedziczy po telefonie

"I once attended a Java user group meeting where James Gosling (Java's inventor) was the featured speaker. During the memorable Q&A session, someone asked him: "If you could do Java over again, what would you change?" "I'd leave out classes," he replied. After the laughter died down, he explained that the real problem wasn't classes per se, but rather implementation inheritance (the extends relationship). Interface inheritance (the implements relationship) is preferable. You should avoid implementation inheritance whenever possible."

"The extends keyword is evil; maybe not at the Charles Manson level, but bad enough that it should be shunned whenever possible."

- tako rzecze Allen Holub - ekstremista obiektowy.


Odpowiedź na poprzedniego posta skłoniła mnie do naskrobania paru słów o dziedziczeniu w ogóle...

Dużo pisze się o tym, że dziedziczenie jest źle rozumiane, nadużywane czy używane w nieodpowiedni sposób. Można zadać pytanie: o co w ogóle chodzi? Przecież pani w szkole mówiła, że to podstawa OO;)

Relacja dziedziczenia pomiędzy klasami jest najsilniejszym rodzajem powiązania (coupling) co w oczywisty sposób prowadzi do problemu "kruchej klasy bazowej". Zmiany w klasie bazowej mają oczywiście drastyczny wpływ na kod systemu, który po niej dziedziczy. Projektowanie każdej takiej klasy bazowej to zestaw decyzji niemal strategicznych, a jej zmiany to i tak zazwyczaj katastrofa.

Poza tym oczywiście klasa dziedzicząca ciągnie za sobą (jak przykładowy smród za gaciami) bagaż klasy bazowej. Jak słusznie zauważył Paweł w OO nie ma pól i metod regresywnych:) W OO Dziedziczymy po przodku wszystko a nie tylko co jest przydatne - nawet pola prywatne... niby ich nie ma a w pamięci są;)

Dziedziczenie jest złym sposobem na "wyciąganie przed nawias" wspólnych elementów. Lepiej wspólne części hermetyzować do osobnych klas i następnie je agregować/komponować.

Osobiście kieruję się Zasadą Podstawienia Liskov: "Let q(x) be a property provable about objects x of type T. Then q(y) should be true for objects y of type S where S is a subtype of T."
hehe, ten pseudonaukowy-turbo-bełkot można w skrócie ująć tak: używaj dziedziczenia tylko wtedy, gdy będziesz korzystał z polimorfizmu. I to się sprawdza. Od kiedy staram sie trzymać tej zasady wiele problemów z rozbudową i zmianami po prostu już się nie pojawia. Zamiast dziedziczenia zazwyczaj stosuję kompozycję strategii, która jak mi się wydaje jest "metawzorcem" - opiera się na nim duża część innych wzorców - różny jest jedynie kontekst (ale o tym napiszę w przyszłości).

//=========================
"Klasa telefon i klasa telefonistka, która dziedziczy po telefonie" - taką odpowiedź usłyszałem podczas rozmowy rekrutacyjnej od "młodego, zdolnego i dobrze zapowiadającego się kandydata", na pytanie o jakiś nieksiążkowy przykład dziedziczenia.
Nie przeszedł rekrutacji...

poniedziałek, 4 sierpnia 2008

Strategia na wielokrotne dziedziczenie

Wielokrotne dziedziczenie jest be - wszyscy o tym wiemy. Poza oczywistymi problemami logicznymi narusza jedną z głównych zasad OO - utrzymywanie wysokiej kohezji - klasa powinna mieć jak najmniej odpowiedzialności, najlepiej jedną.

Ostatnio zmagałem się z zaprojektowaniem pewnego modelu dostarczonego z fazy analizy. Model analityczny był w sumie dosyć prosty, jednak kobieca intuicja podpowiadała, że będzie sprawiał problemy z rozbudową w przyszłości. Problem domenowy wyglądał tak: mamy zlecenie podania leku; zakładamy, że w przyszłości na pewno dojdą do wykonania inne rzeczy; możemy również zlecać wykonanie czegoś raz lub wielokrotnie (okresowo, wg zadanego wzorca powtarzania).

Model analityczny wyglądał tak: mamy zlecenie, po którym dziedziczy zleceniePodaniaLeku, po którym to z kolei dziedziczą zlecenia jednorazowegoPodaniaLeku jak i wielokrotnegoPodaniaLeku. Czujecie chyba eksplozję kombinatoryczną, która się szykuje w razie pojawienie się nowych rodzajów rzeczy do zlecenia czy nowych polityk powtarzania, lub (o zgrozo) pojawienie się nowych aspektów zlecenia.

Widać tu jak na dłoni wzorzec projektowy Strategii. Postępując zgodnie z tym wzorcem dojdziemy do modelu, gdzie zlecenie składa się ze strategii powtarzania - lub polityki idąc za terminologią Domain Driven. Zlecenie składa się też ze strategii wykonania pewnej czynności. Zlecenie "widzi" jedynie interfejsy strategii, natomiast z jego punktu widzenia implementacje nie są istotne.
Podejście to zapewni nam również zgodność z kolejną zasadą GRASP.

Wzorzec co prawda "widać" z perspektywy projektowej czy implementacyjnej, jednak z perspektywy analitycznej ponoć wygodniej jest pokazać wielodziedziczenie - po typie powtarzania i po typie czynności do wykonania. Jednak ponieważ wielodziedziczenie jest be, więc aby go uniknąć doszło na poziomie analizy do "rzutowania" aspektu powtarzania na aspekt czynności.

Sytuacja z dystansu wygląda śmiesznie: analitycy "szyfrują" rzeczywistość do jednowymiarowej przestrzeni aby uniknąć wielodziedziczenia, a projektanci muszą później dokonać "odszyfrowania" na swoje strategie;)

Dlatego też wspólnie uznaliśmy, że wielodziedziczenie jest git - ale tylko na poziomie analitycznym:P Dzięki niemu projektanci mogą niemal w sposób automatyczny dokonać transformacji modelu analitycznego na implementacyjny model obiektowy oparty na strategiach. Niemal automatyczny ponieważ wszystkie wielowymiarowe aspekty dokładnie "widać" gdy nie są spłaszczone do jednego wymiaru


//===========================
Tak właściwie to nie jestem do końca przekonany czy aby na pewno wielodziedziczenie jest immanentną cechą obiektów ze świata rzeczywistego. Być może jest to jedynie kwestia wyuczonego postrzegania; wynik bezkrytycznego powielania schematów myślowych. Pamiętam, że sam zanim przestawiłem się na agregację/kompozycję, widziałem wszędzie dziedziczenie - wszak kto ma w ręku młotek, ten wszędzie widzi gwoździe.

GRASP jest doskonale opisany w "Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and Iterative Development" autorstwa Craiga Larmana