czwartek, 25 grudnia 2008

Bo najważniejsza jest odpowiedzialność Synu...


...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
Role, odpowiedzialności i współpraca są głównym tematem książki lecz nie jedynym. Właściwie to są osią wokół której pojawiają się konteksty inny problemów projektowych, takich jak: style architektoniczne, elastyczność, analiza obiektowa, style obsługi błędów...

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:)

niedziela, 14 grudnia 2008

UP-DDD in Action: Hermetyczne agregaty



Drogie dzieci - w dzisiejszym odcinku z serii UP-DDD in Action zobaczycie jak nasz bohater Bob Budowniczy rozprawia się z brakiem hermetyzacji i proceduralną ignorancją.



Kontynuując temat enkapsulacji rozpoczęty w poprzednim poście zobaczmy jak wygląda on w kontekście Domain Driven Design.

DDD opiera się na starych i sprawdzonych technikach obiektowych więc proceduralne łańcuszki typu "train wreck" zbesztane w poprzednim poście nie mają prawa pojawić się w kodzie aspirującym do miana DDD.

Analizując specyfikacje building blocks z DDD widać, że Encje, VO i Agregaty posiadające odpowiedzialność biznesową (nie będące standardowym anemicznym modelem) są w zgodzie z paradygmatem Abstrakcji.

Dodatkowe założenie odnośnie Agregatów jest takie, że hermetyzują one swą strukturę. Oznacza to, że nie powinny zdradzać szczegółów swych składowych. Agregat posiada root (główną encję), która publikuje niejako zestaw odpowiedzialności/zdolności w celu ochrony wnętrzności przed światem zewnętrznym. Jeżeli świat zewnętrzny nie wie nic o wnętrznościach agregatu, to wówczas implementacja agregatu może z czasem się zmieniać (aby nadążać za nowymi wymaganiami). Dzięki temu zmiany są lokalne i nie mają wpływu na świat zewnętrzny.

Weźmy na ten przykład arcy interesujący model zamówienia;) Niech zamówienie posiada datę, miejsce dostawy oraz listę zamówionych produktów. Zamówienie będące agregatem nie może na przykład udostępniać gettera do listy produktów ponieważ istnieje możliwość modyfikacji tejże listy "poza świadomością" samego zamówienia.
Zakładając iż tworzymy obiektowy model biznesu i jego reguł to chcielibyśmy aby każda operacja na produktach była wykonywana poprzez samo zamówienie, które jest swego rodzaju mózgiem biznesowym.

Obiekt zamówienia posiadając wiedzę o swych wnętrznościach może na ten przykład domówić dodanie do niego pewnego produktu jeżeli miejsce dostawy zawiera się w pewnej strefie, klient nie jest pełnoletni, albo data realizacji wykracza poza jakąś tam granicę pewnej akcji marketingowej.

W tym akurat przykładzie jakimś rozwiązaniem byłoby upublicznienie iteratora po iście zamówień. Ale załóżmy przypadek bardziej ogólny - nie chcemy zdradzać żadnych szczegółów i kropka.

No dobra, wystarczy tego teoretycznego bełkotu! W praktyce niestety często istnieje konieczność dobrania się do wnętrzności obiektu. Na przykład po to aby wyeksportować zamówienie do PDF lub XML. Że o takim szczególe jak jego wyświetlenie na GUI nie wspomnę;P

Podchodząc do zamówienia jak do struktury danych potrzebowalibyśmy procedur, które odpowiadają za przejrzenie wnętrzności zamówienia i stworzenie odpowiednio PDFa lub XMLa prezentującego dane o zamówieniu i jego wewnętrznej strukturze (GUI to problem osobny - wręcz filozoficzny, więc o nim osobny akapit później).

Na szczęście istnieje wzorzec projektowy Budowniczego. Idea wzorca jest prosta jak nie przymierzając budowa cepa. Zakłada ona istnienie dyrektora budowy i robotnika (budowniczego). Dyrektor wie CO trzeba zrobić a robotnik wie JAK to zrobić.



Na powyższym przykładzie klasa Order jest dyrektorem budowy. Gdy ktoś poprosi dyrektora o wyeksportowanie zamówienie wówczas on wydaje rozkazy delegując pracę do budowniczego. Dyrektor (zamówienie) wie skąd pobrać dane do eksportu - z własnych prywatnych wnętrzności. Natomiast robotnik (budowniczy) jest abstrakcją w najczystszej formie - interfejsem. Konkretny budowniczy wie jak zająć się surowcami przekazywanymi mu przez dyrektora budowy. Na przykład budowniczy PDFów zna doskonale jakiś silnik do generowania dokumentów. Budowniczy XMLi zna format w jakim należy zbudować plik tekstowy. Budowniczy DTO wie jak należy skonstruować obiekt transferowy.

Mamy tutaj doskonałe rozdzielenie odpowiedzialności: hermetycznych struktur biznesowych i wiedzy o ich semantyce od technicznych aspektów formatów danych eksportowych.

Powyższy przykład można rozszerzyć o mały aspekt optymalizacyjny. Bo co jeżeli dany budowniczy nie jest z jakiegoś powodu zainteresowany na przykład listą zamówień? Najprościej byłoby aby jego implementacja metody buildNextProduct() po prostu ignorowała przekazywane produkty. Niestety mamy wówczas niepotrzebną iterację. Zamiast tego możemy rozszerzyć interfejs budowniczego o metodę isProductListNeeded(). Iterujemy po liście jedynie wówczas gdy metoda zwróci wartość true.



Wspomniałem wcześniej o aspekcie GUI. Popularne frameworki zasadzają się na refleksji i wymagają by istniały gettery do składowych obiektu, które chcemy wyświetlić. Heh jednym ze sposobów na wyświetlenie zamówienie w JSF mogłoby być stworzenie takiej implementacji budowniczego, która tworzy jakiś UIComponent który to następnie dodajemy do widoku - niestety jak dla mnie jest to nakładanie gaci przez głowę.

Jakimś rozwiązaniem jest zastosowanie Data Transfer Objects. Czyli nasz agregat biznesowy może eksportować się do DTO, które to DTO jest bindowane z widokiem. DTO ma już gettery i settery więc doskonale pasuje do frameworków.

Rozwiązanie to ma oczywiście plusy dodatnie i ujemne.

Przede wszystkim dzięki przepakowaniu obiektów biznesowych do głupich DTO nie udostępniamy w warstwie UI metod biznesowych (pamiętajmy, że Agregaty je posiadają). Serwisy aplikacyjne posługujące się DTO nadają się wówczas do bycia Web Serwisami - nie chcemy przecież upubliczniać naszego modelu biznesowego oraz jego metod (odpowiedzialności). DTO mogą zawierać jedynie istotne z punktu widzenia usługi dane, prezentować je w spłaszczony (prostszy) sposób oraz stanowić warstwę interfejsu do systemu pod którą możemy swobodnie zmieniać nasz model bez obawy o ciągłe modyfikacje klientów.

W małych projektach zmniejsza to oczywiście w znaczny sposób produktywność (rapid developnent hehehe). Zamiast spokojnie stukać sobie przeglądarkę do bazy musimy nakładać gacie przez głowę i przepakowywać dane bawiąc się samemu ze sobą w kotka i myszkę:/


//====================

Swoją drogą to dziwne, że frameworki nie wspierają dobrych modeli programowania a skupiają się jedynie na przykryciu technikaliów takich jak na przykład HTTP, transakcje, rozproszenie obiektów, bindowanie danych warstwą abstrakcji...

Nawet frameowrki, które nazywają się dumnie frameworkami aplikacji nie wspierają żadnego modelu aplikacji a hermetyzują jedynie niskopoziomowe aspekty techniczne.

W którymś z następnych odcinków zobaczymy jak wygląda to w http://www.qi4j.org/

poniedziałek, 8 grudnia 2008

Flaczki (Kod obiektowy - zawsze smaczny i zdrowy)



Zacznijmy od oczywistych oczywistości...
Programowanie Obiektowe zasadza się na 4 paradygmatach:
  • Abstrakcja
  • Enkapsulacja
  • Polimorfizm
  • Dziedziczenie
O złym jak Charles Manson dziedziczeniu w ogóle i złudnym dziedziczeniu wielokrotnym już pisałem:
- Telefonistka dziedziczy po telefonie
- Strategia na wielokrotne dziedziczenie
teraz kolej na enkapsulację i abstrakcję.

Czy tego typu kod jest obiektowy?
czlowiek.getOtrzewna().getZoladek().getTresc().add(new Kielbasa(2));
Na pierwszy rzut oka tak. Mamy piękne obiekty złożone we wspaniałą strukturę. Niestety analizując nawet niezbyt dogłębnie definicje 4 paradygmatów wymienionych jako oczywiste oczywistości możemy o tym kodzie powiedzieć, że co najwyżej używa obiektów ale obiektowy nie jest. Ba, można nawet pokusić się o stwierdzenie, że żadnych obiektów nie używa.

Przykładowy kod jest niezgodny z paradygmatem Enkapsulacji ponieważ literalnie wypruwa flaki z biednego człowieka. Obiekt człowiek ujawnia swą wewnętrzną strukturę oraz poniekąd implementację. Taki obiekt jest kompletnie nieodporny na zmiany ponieważ na ten przykład próba uszczegółowienia anatomii w wersji 2.0 i dodania nowych narządów spowoduje katastrofę. Enkapsulacja nie polega na bawieniu się samemu ze sobą w kotka i myszkę: prywatne pola ale za to publiczne gettery - po co się w ogóle tak wydurniać?

Przykładowy kod jest niezgodny z paradygmatem Abstrakcji ponieważ literalnie człowiek nie ma nic do gadania w temacie karmienia go kiełbasą. Co jeżeli dana instancja człowieka jest już syta albo nie lubi podwawelskiej?

Przykładowy kod jest to tak zwany "train wreck" - kod charakterystyczny dla struktur danych w językach proceduralnych na których to strukturach operują zewnętrzne procedury. Owszem może było to dobre jeszcze w szalonych latach '70 ale problem polega na braku hermetyzacji.

W odpowiedzi na bolączki proceduralnego kodu powstała właśnie koncepcja OO. Koncepcja, która sugeruje aby naszego przykładowego człowieka wyposażyć w odpowiedzialność lub możliwość pożywiania się:
czlowek.zjedz(pokarm) - wewnętrzna implementacja człowieka zdecyduje co z tym zrobić. W szczególności instancja człowieka może uznać, że sugerowany pokarm należy natychmiast wypluć
Możemy pójść jeszcze dalej i wyposażyć człowieka w umiejętność brania: czlowiek.wez(pokarm) - instancja człowieka może uznać za stosowne schowanie go do kieszeni, plecaka, lub w nawet poddanie obróbce przed spożyciem.

Ten sam styl rozumowania aplikuje się do obiektów agregowanych w ramach człowieka. Nie wystarczy zadeklarować metodę czlowek.zjedz(pokarm) a w niej radośnie posługiwać się łańcuszkiem
this.getOtrzewna().getZoladek().getTresc().add(pokarm);
Kontynuując obrany tok rozumowania należny metodę czlowek.zjedz(pokarm) zaimplementować mniej więcej tak: this.ukladPokarmowy.przyjmij(pokarm);
i tak dalej...
A taki układ pokarmowy lub pojedynczy narząd może być całkiem za darmo reużyty dla innych ssaków:)

Z punktu widzenia kodu wchodzącego w interakcję z obiektem człowiek nie jest istotne co instancja człowieka zrobi ani jak to zrobi. Dopóki człowiek enkapsuluje to pod stabilnym interfejsem kod jest "otwarty na rozbudowę niczym kwiat lotosu o świcie i zamknięty na modyfikacje niczym kwiat lotosu o zmierzchu" (czy jakoś tak;)

//=====================

Train Wrecks są dosyć popularne lub wręcz lansowane przez standardowe frameworki i architektury. Począwszy od encji hibernate przez encyjne EJB po ich łatwe bindowanie z kontrolkami w warstwie prezentacji. Może jednak coś w tym jest;)

W następnym odcinku zobaczymy co na to Domain Driven Design.

sobota, 22 listopada 2008

Do it head first


Nie jest dobrze. Ba, jest źle. Regres cywilizacji zaczął się od prymitywnych form eskalacji przemocy:

Aby wreszcie zaatakować ostatni i jednocześnie najcenniejszy bastion dorobku cywilizacji białego człowieka - programowanie obiektowe:

Na szczęście w ramach lubelskiego JUGa powstała kontrofensywa. Jest jeszcze szansa...

Jak niesie plotka - dżawarze z krainy pszenicy i buraka jednoczą się aby zachować przed zapomnieniem i zdeptaniem kunszt programistyczny. Ich sztandarem i relikwią jednocześnie jest mityczna koszulka z napisem "Java developers do it head first", której jeden egzemplarz (jak głosi legenda: czarna, rozmiar XL) podobno zachował się jeszcze gdzieś na świecie...

//=========
Bez odbioru...

wtorek, 18 listopada 2008

UP-DDD in Action: Planowanie jest nieodzowne, Sir!

Prezydent Dwight Eisenhower tak oto wspominał czasy gdy był generałem armii amerykańskiej:
"In preparing for battle I have always found that plans are useless, but planning is indispensable."

Dziś kolejny post z serii Unified Process & Domain Driven Design in Action. Prace nad projektem wciąż w fazie analizy biznesowej więc post będzie raczej wypełniaczem (podtrzymującym serię) o charakterze "filozoficznym".

Jaką rolę w projekcie spełnia UML (dokumentacja w ogólności)? Są różne podejścia...

Może na przykład nie mieć żadnej roli. Organizacja może radośnie postępować według metodyki chałupniczo-garażowej. Metodyka ta zakłada 3-etapowy proces:

radosna twórczość - po podpisaniu kontraktu następuje pięciominutowa odprawa sześciopaku programistów po czym rzucają się oni na klawiaturę (jak nie przymierzając marynarz atomowego okrętu podwodnego na kozę w porcie) i klepanie na wyścigi. Nikt nie wiec po co, na co i jak, ale to nie szkodzi... byle się wyżyć.

kosy racławickie - wiadomo... burdel rośnie, projekt się sypie, co raz jakaś eskalacja. Najlepszym rozwiązaniem jest pospolite ruszenie: nadgodziny + "ochotnicy" do pomocy. Jak wiemy z doświadczenia jest to oczywisty antywzorzec zarządzania ponieważ nowi ludzie jedynie spowalniają pracę, ale kto by się nad tym zastanawiał - przecież każdy brakujący diagram UML można zastąpić skończoną liczbą studentów...

stawanie na kancie tego no... nosa! - tydzień do release. Ludzie funkcjonują w trybie zombie. Każdy brakujący diagram UML można zastąpić skończoną ilością sznurka do snopowiązałki...

Jeżeli opisana metodyka chałupniczo-garażowa sprawdza się wyśmienicie - na co wskazuje na przykład sam fakt istnienia organizacji (że o systematycznym zwiększaniu zysków nie wspomnę) to oczywiście jak najbardziej jest słuszna i nie powinno się jej zmienić bo jeszcze kruchy stan równowagi się posypie. Biznes to biznes - doskonale to rozumiem.

Na drodze logicznego rozumowania można z czasem uświadomić sobie, że jednak wydanie paru groszy na dokumentację zwróci się wielokrotnie w przyszłości. Metodyka chałupniczo-garażowa owszem przynosi świetne dochody, ale okazuje się, że mogłaby przynosić jeszcze większe gdyby nie tracić czasu na zmaganie się z burdelem i po prostu udokumentować wszystko. Pycha pomysł!

I tu pojawia się kolejna możliwa rola dokumentacji: kula u nogi. Po radosnym zaimplementowaniu trzeba jeszcze zrobić te cholerne rysuneczki i posiedzieć nad wordem. A tu nad głową stoi poganiacz i zadaje najgłupsze pytanie jakie istnieje: "na kiedy będzie gotowy następny moduł?". Na szczęście wspaniałe narzędzia mają funkcję wstecznej inżynierii - dwa kliki i zestaw pięknych prostokątów gotowy. Teraz jeszcze tylko wkleić to jakoś do worda, dokleić jakiś schemat bazy i z dyni. Hmmm nie tak szybko... za mało tej dokumentacji... ale wystarczy wkleić jeszcze trochę kodu źródłowego i bedzie. No dobra, nie odwalajmy chały - tylko same nagłówki klas i metod. Jak wciąż mało to można dokleić trochę reguł biznesowych z jakiś roolsów. Ktoś kazał robić dokumentację to się zrobi - "będzie Pan zadowolony"

Istnieją różne dewiacje w tym podejściu. Można na przykład analizą nazwać projekty ekranów - co prowadzi do ciekawej paranoi. Innym skrzywieniem jest traktowanie schematu bazy danych jako projektu systemu - również ciekawa paranoja.

W sumie to dokumentacja tworzona po fakcie ma jednak swoją wartość - oczywiście pod warunkiem, że ktoś ją aktualizuje:P



A gdyby tak potraktować diagramy UML jako narzędzie do eksperymentowania? Nie chodzi o sam fakt posiadania dokumentacji, ale proces jej tworzenia. Wysiłek umysłowy włożony w zastanowienie się o co w ogóle chodzi. Skupienie się na tym CO a nie JAK. Eksperymentowania, które polega na najpierw na notowaniu wyników analizy biznesowej. Później poddaniu ich wnikliwej i rzetelnej analizie systemowej, czyli odfiltrowaniu chaosu wprowadzone przez ekspertów biznesowych i wyciągnięciu z tego czystego ekstraktu. W wyniku otrzymamy diagramy Use Caseów i Obiektów Biznesowych - stąd już blisko do warstw logiki aplikacji i biznesowej. I nie chodzi bynajmniej o to aby jedynie przepisać bełkot klienta na jajeczka z ludzikami i cieszyć się, że mamy diagramiki Use Case. Tworząc diagramy warto się zastanowić nad każdą kreską i jajeczkiem - czy dany element ma sens, czy nie da się czegoś uogólnić i wyłonić reużywalnej abstrakcji, czy nie da się tego zrobić prościej - właśnie o to upraszczanie chodzi. Upraszczanie na etapie "eksperymentowania" w UML zwróci się na etapie kodowania.

Analogiczne eksperymenty w UML można przeprowadzać na etapie projektowania. Zanim zacznie się kodować warto przez chwilę popatrzeć z większej wysokości na całość bo można przypadkiem zauważyć uogólnienie prowadzące całkiem za darmo do reużywalności. Można też całkiem przypadkiem znaleźć miejsca dla jakiegoś wzorca projektowego i całkiem za darmo zapewnić w przyszłości większą giętkość systemu i jego otwartość na rozbudowę.

Z samego planowania może wyjść więcej dobrego niż z posiadania gotowych (wygenerowanych) planów...
No i jak by nie było - efektem ubocznym tworzenia dokumentacji jest jej posiadanie:)



//=================
UML sam w sobie jest po prostu jakimś tam pismem obrazkowym. Może ma nieco lepiej zdefiniowane reguły niż pismo jaskiniowców, ale wciąż - pismem obrazkowym;P

Sztuka stosowania UML nie polega na skrupulatnym rysowaniu prostokątów od ekierki i następnie ich kolorowaniu tak aby nie wyjść kredką poza kontur.

Nie chodzi o samą formę ale o proces myślowy, który zaszedł np. podczas obiektowej analizy domeny biznesowej. Prostokąty to tylko notatka z walki ze złożonością i chaosem.

niedziela, 16 listopada 2008

Wzorowy uczeń


Ci, którzy mnie znają spodziewają się pewnie sarkastycznego posta na temat naszego idiotycznego systemu edukacji działającego na zasadzie tresowania szympansów. Otóż muszę niestety rozczarować - nie o tem dziś będę rozprawiał (że tak na koniec tygodnia pozwolę sobie po staropolsku zaciągnąć).

Nie będę się też znęcał się nad inwalidami pedagogicznymi, którzy znaleźli sobie przetrwalnik w różnego rodzaju państwowych instytucjach edukacyjnych i niczym pasożyty żerują na pieniądzach z naszych podatków, jednocześnie upychając wydalane podczas wspomnianego żeru odchody w głowach naiwnych dzieciątek (byle do emerytury - wcześniejszej).
Nic z tych rzeczy, żadnej negatywnej retoryki.

Jako prosty człowiek z chorobliwą skłonnością do myślenia szablonami i dopatrywania się w otaczającym uniwersum powtarzalnych schematów, łykam jak młody pelikan różnego rodzaju wzorce: wzorce projektowe, wzorce architektoniczne, wzorce analityczne, wzorce domenowe, wzorce kognitywne, wzorce w psychologii...

Rafał J. - nieoceniony dostawca ciekawych linków z poza mainstreamu zwrócił moją uwagę na wzorce pedagogiczne. To pocieszające, że jednak ktoś zastanawia się nad uniwersalnymi sposobami na efektywne dotarcie pod dekielek oraz dzieli się swymi spostrzeżeniami na temat prostych i sprawdzonych metod.

Wzorce pedagogiczne podobnie jak projektowe zostały skodyfikowane przy pomocy zestawu atrybutów takich jak: intuicyjna nazwa, problem i jego kontekst, siły oraz oczywiście sama treść wzorca.

Jakie znaczenie mają wzorce pedagogiczne dla kogoś kto już zakończył (lub kończy) karierę naukową? Pomijając ewentualność jej przedłużenia przy pomocy doktoratu lub odrabiania lekcji z dziećmi możemy stosować się do nich podczas codziennego samokształcenia...

Istnieje katalog wzorców pedagogicznych zorientowany na Computer Science. Większość z nich po zapoznaniu się z treścią jest dosyć oczywista, ale na przykład dla mnie nie wszystkie były uświadomione.



Wzorce, na które warto zwrócić uwagę:
Larger than Life
Zobacz co w ogóle można stworzyć zanim będziesz w stanie to stworzyć. Poznaj skalę problemu i poczuj jego powagę. Później przyjdzie czas na to aby krok po kroku poznawać szczegóły.

Niestety rożnego rodzaju tutoriale są tworzone wbrew tez zasadzie. Pokazuje się nam badziewiaste przykładziki - masz tu hello world i dalej jedziesz już sam. Do tego ewentualnie przedstawienie paru klasek bazowych, jakiś trywialny przykład. A gdzie bigger pikczer? Jak pownienem używać tego młotka żeby nie przywalić sobie w palucha? Jak i do czego został on pomyślany?

Kończy się to oczywiście żenującym designem systemu opartym na tym co już znamy z dotychczasowego doświadczenia i może być wbrew idei nowych (właśnie poznawanych) narzędzi/frameworków - bo niby na czym ma być oparte?

Lay of the Land
Weź do metaforycznej ręki gotowe i pełnowartościowe rozwiązanie prawdziwego, większego projektu. Oczywiście w tym momencie niewiele z niego rozumiesz, ale możesz sobie obejrzeć zarówno jego strukturę w skali makro jak i przyjrzeć się detalom. Po prostu widzisz do czego sam masz dążyć. Widzisz jak powinno się to dobrze zrobić.

Jest to podejście podobne do Larger than Life. W LtL przyglądamy się samemu problemowi a w LotL jego rozwiązaniu.

Early Bird
Najpierw sprawy ważniejsze. Klasyczne podejście top-down. I co? To ma być wzorzec? W sumie to oczywista oczywistość, ale czasem zapominamy się i wnikamy w nieistotne szczegóły gubiąc ogólny obraz.


//=================
Oczywiście podatność na dany wzorzec pedagogiczny to cecha osobnicza. Weźmy na przykład różnice kognitywne wynikające z podejścia globalne/szczegółowego i już możemy sobie wyobrazić diametralnie różne podejścia do nauki wymykające się jednolitym wzorcom.

Akurat wybrane 3 wzorce prezentują myślenie typowo globalne. Nie każdemu będzie to odpowiadać. Niektórzy czują się pewnie stojąc na fundamentach dobrze poznanych szczegółów.

Team złożony z jednego rodzaju zawodników daleko nie zajdzie...

piątek, 14 listopada 2008

Jest tylko jeden taki dzień w roku...

13 listopada dla większości populacji kojarzy się z czymś strasznym. Wszak to pechowa trzynastka! Ale na szczęście przynajmniej w tym roku nie trafił się piątek. Prawie nikt nie wie dlaczego trzynasty to taki straszny dzień, ale po co się nad tym w ogóle zastawiać. Trzeba normalnie i po bożemu się lękać, unikać czarnych kotów, nie przechodzić pod drabiną i dotrwać jakoś do następnego dnia w strachu, że słońce zgaśnie i mrok ogarnie ziemię;)

Mniej zabobonna część populacji mogła na ten przykład zanurzyć się w refleksji nad Nagrodą Nobla, którą 13.11.1924 Władysław Reymont otrzymał za powieść Chłopi;)

Jedni i drudzy mogli zjednoczyć się na libacji alkoholowej z okazji imienin Alojzego, Brykcjusza tudzież Jana.

Nie zapominajmy o rocznicy 13.11.1990 - kiedy to pojawiła się pierwsza strona WWW.

A'propos... co wczoraj robili wszyscy programiści? Oczywiście świętowali Światowy Dzień Usability! Wszyscy na niego czekali więc nie mógł przejść bez echa, niezauważony, o nie...

Prezesi wszystkich firm IT zarządzili tego dnia wewnętrzne narady i dyskusje. Hucznie debatowano jak tym razem zrobić jeszcze lepsze i bardziej ergonomiczne GUI. Mało tego... w pokojach, na korytarzach i w firmowych kuchniach nie mówiło się o niczym innym niż poprawienie usability tworzonych przez nas aplikacji.

Rzesze freelancerów (z uwagi na brak prezesów organizujących im czas) okupowały pękające w szwach sale konferencyjne na których odbywały się panele dyskusyjne, warsztaty i prezentacje poświęcone zorientowaniu systemów na człowieka.

Rząd Polski za wzorem rządu USA opublikował zestaw wytycznych odnośnie usability i udostępnił publikację podobną do tej.

Wszyscy jak jeden mąż obiecywali sobie w atmosferze entuzjazmu, że już nigdy więcej autystycznego oprogramowania - koniec błędów i wypaczeń! Od wczoraj nasz soft będzie tak intuicyjny jak produkty Google.

Padł nawet pomysł aby wydobyć spod ziemi i dla przykładu wychłostać taiwańskiego inżyniera, który zaprojektował zegarek Montana. Był to wymarzony prezent komunijny każdego chłopca z rocznika 1981 i jednocześnie kwintesencja anty-usability: 4 przyciski (właściwie to 3 nie licząc światełka) i 150 funkcji.



//====================

Tylko co jakiś czas użytkownik siedzący w domu lub pracy patrząc tempo w monitor syknie przez zęby: "o co tu k@#$a chodzi?"

jak pech to pech...

środa, 5 listopada 2008

Wprowadzenie do wstrzykiwania zależności i Springa zarazem

Dzisiaj krótki kurs wstrzykiwania zależności w Springu (i nie tylko). Kurs powstał na bazie prezentacji, którą przedstawiłem w ramach lubelskiego JUG.

Prezentacja była skierowana z założenia do początkujących, jednak aby nie zanudzić bardziej zaawansowanych zawiera ona również parę zagadnień dla nich - zostały one oznaczone czterolistną koniczynką (żeby pozostać w wiosennym klimacie). Zatem jeżeli jesteś początkujący i w pewnym momencie nie rozumiesz o co chodzi a na slajdzie widnieje koniczynka wówczas wszystko jest ok.

Celem posta jest przedstawienie ogólnej idei Inversion of Control (IoC) oraz jej szczególnej reifikacji (trudne słowo) zwanej Dependency Injection (DI). Jest to wg mnie bardzo istotne na początek ponieważ duża część nowszych frameworków zasadza się na tych fundamentach. Później możemy przejść do konkretnego przykładu w Springu. Przeglądając różne tutoriale stwierdzam brak wyjaśnienia o co w ogóle chodzi z IoC czy DI. Pokazuje się jedynie jakieś autystyczne przykładziki bez wyjaśnienia sedna problemu - po co i na co mam coś wstrzykiwać, co mi to daje i kiedy powinienem to robić.

Ludzie podobno najlepiej uczą się na przykładach. Owszem - do nauki prostych czynności typu rozbicie kokosa kamieniem wystarczy sam przykład; jednak jak przedstawić ideę na samym przykładzie? Albo inaczej: jaka ilość przykładów pozwoli na drodze logicznego myślenia wyłonić z nich ideę?


Zaczynamy.



Zgodnie z agendą przejdziemy po niektórych slajdach...



Na początek wymyślimy sobie problem. Problem oczywiście będzie polegał na tym, że pojawiają się zależności i nie wiemy jak sobie z nimi poradzić. Później nastąpi krótki przegląd możliwych rozwiązań. Wszystkie one są oczywiście złe:) Dalej całkiem przypadkiem wpadniemy na koncepcję rozwiązania dużo lepszego. Poznamy ogólną koncepcję IoC i DI. Teraz jesteśmy już gotowi na zapoznanie się z przykładową implementacją w Springu. Następnie przedstawię zarówno zalety jak i ograniczenia wstrzykiwania zależności. Na zakończenie poszerzymy nieco horyzonty... Spring to nie tylko wstrzykiwanie zależności.

KONTEKST PROBLEMU

Na potrzeby naszych rozważań próbowałem wymyślić jakiś ekscytujący problem biznesowy. Niestety bezskutecznie. Dlatego musimy się zadowolić wyświechtanym przykładem z zamówieniem. Zatem załóżmy, że mamy zamówienie, które musimy złożyć. Nasz proces składania zamówienie niech będzie trywialny: wyliczamy podatek i zapisujemy zamówienie.

Zakładam, że kombinujemy w stronę Object Oriented oraz, że chcemy zaprojektować nasze rozwiązanie zgodnie z GRASP.



Zgodnie ze standardowym (choć nie koniecznie najlepszym) tokiem myślenia powinniśmy dość do powyższego projektu. W centrum mamy encję biznesową Order. Nie chcemy aby była ona anemicznym modelem więc przykładowo przypisujemy jej metodę biznesową wliczającą wartość tegoż zamówienia. Dla przejrzystości pomijamy szczegóły takie jak atrybuty zamówienia (data, status itp) oraz jego wewnętrzną strukturę (kolekcję produktów) ponieważ nie wnoszą one nic w kontekście wstrzykiwania zależności.

Logika aplikacji (logika przepływu, use case, ogólnie - nie biznesowa) jest zhermetyzowana w serwisie. Logika ta jest prosta, polega na wyliczeniu podatku i zapisaniu zamówienia. Ponieważ chcemy podążać zgodnie z OO to aspekt podatku i zapisu został wydzielony (enkapsulacja). Oba aspekty mogą mieć różną postać dlatego zostały przykryte stabilnym interfejsem.


PROBLEM

Ok, nasza logika aplikacji zależy od 2 rzeczy: polityka wyliczania podatku i sposób zapisu zamówienia. Pytanie: skąd je wziąć?



Niektórzy się tym nie przejmują i radośnie hardcodują typy w kodzie. Tzn usztywnianie kodu następuje dużo wcześniej. Po co w ogóle wydzielać jakieś klasy:P

Gdy jednak projektant przyciśnie i mamy już hermetyzację oraz wymaganie co do możliwości zmiany implementacji pojawia się pomysł jej wyboru przy pomocy kaskady ifów albo słicza.

Dalej nauczeni doświadczeniem z problemami słiczy zmian w setkach miejsc enkapsulujemy je do jakiejś fabryczki. Koncepcja ta może wyewoluować nawet do całkiem zgrabnego rozwiązania opartego o Abstract Factory, który zaczytuje konfigurację np z XML.

Jako uzupełnienie wspomnę tylko rozwiązanie, które widziałem u sprytnego mistrza .NET: dyrektywy kompilacji - bez komentarza.

O ile rozwiązanie oparte o fabrykę abstrakcyjną jest już całkiem w porządku to ma jednak wadę estetyczną. Kod biznesowy czy jakikolwiek inny jest zbrukany jakimiś dziwnymi odwołaniami do technicznych obiektów fabryk. Fuj.

Skoro odrzucamy myśl o tym, że obiekt zajmuje się zdobywaniem swych zależności to nie pozostaje nic innego jak zapodawanie ich z zewnątrz.



POMYSŁ NA ROZWIĄZANIE


Jeżeli obiekt nie jest odpowiedzialny za tworzenie czy zdobywanie swych zależności lecz dostaje z kątowni to właśnie odkryliśmy ideę Inversion of Control!





W standardowym podejściu nasz kod korzysta z już istniejących bibliotek (nawet gdyby była to klasa napisane minutę temu) w celu nadbudowania nad nimi nowej struktury. Nasz kod krok po kroku wywołuje biblioteki. W IoC jest odwrotnie - to kod biblioteczny wywołuje nasz własny kod - wymaga to zmiany sposobu myślenia. Nie programujemy imperatywnie lecz deklaratywnie aby coś zbudować. Konstrukcja już istnieje lecz brakuje w niej pewnych szczególnych komponentów realizujących szczegółowe funkcje. Można to sobie wyobrazić w ten sposób, że kod biblioteki zajmuje się kompleksowym rozwiązaniem problemu jednak w pewnym miejscach woła kod klienta. To jaki kod klienta biblioteki ma być podłączony musimy właśnie w jakiś sposób zadeklarować (podłączony - chciało by się powiedzieć wstrzyknięty, ale nie uprzedzajmy faktów).

W naszym przykładzie servis składający zamówienie jest powiedzmy biblioteką. Załóżmy, że ktoś stworzył doskonałą bibliotekę do składnia zamówień, ale oczywiście nie wiedział jak zaimplementować wyliczanie podatku i zapis zamówienia. Dlatego zostawił to w gestii kodu klienta biblioteki. Jeszcze raz podkreślę różnicę: kod klienta nie woła biblioteki - jest na odwrót. Jest to podejście charakterystyczne dla frameworków.

Ogólnie można powiedzieć, że IoC jest podejściem do projektowania architektur.

PRÓBA ROZWIĄZANIA W NOWYM STYLU

Skoro nasz pomysł z odwróceniem kontroli nie jest objawem schorzenia, ponieważ jest opisany nawet na wiki, to spróbujmy może tak:



Skoro nasz servis potrzebuje załadowania go zależnościami przed rozpoczęciem pracy to wychodzi chyba na to, że składanie powinno nastąpić gdzieś w interfejsie użytkownika.



Nic bardziej mylnego!
UI służy do różnych dziwnych rzeczy ale na pewno nie powinno zajmować się decydowaniem o składaniu komponentów biznesowych. Wiem, że kiedyś w onClick pisało się żywego SQLa, ale czasy Visual Basica czy Delphi już się skończyły. Co prawda niechlubną tradycję podtrzymuje Seam, ale normalnie warstwa prezentacji służy do hermetyzowania logiki prezentacji (ot niespodzianka) - czyli np decydowania czy z uwagi na imieniny usera narysować kwiatek czy butelkę whisky.

POTRZEBUJEMY WSTRZYKIWANIA ZALEŻNOŚCI



Idea wstrzykiwania zależności jest prosta jak budowa cepa. Musi istnieć jakiś byt (zwany zazwyczaj kontenerem IoC), który ma dostęp do konfiguracji i bibliotek klas. Byt ten poproszony o jakiś obiekt szuka jego definicji w konfiguracji. Na podstawie konfiguracji jest w stanie określić jakiej klasy powinien być żądany obiekt oraz jakie ma zależności. Zależne obiekty są w razie potrzeby tworzony po czym są wstrzyknięte (po prostu wstawione) do obiektu żądanego. Oczywiście same obiekty zależne mogą same mieć własne zależności - kontener również dla nich przeprowadzi rekursywnie opisaną procedurę.

Uwagę na powyższym slajdzie przykuwa zapewne pękaty słoik. Istotne jest, że kontener pracuje na POJOs. Czyli DI nie wymaga uzależniania naszego kodu od jakiś dziwnych interfejsów. Nie wymaga dziedziczenia po ezoterycznych klasach bazowych. Kod jest czysty ponieważ zajmuje się tym czym powinien.

WYJAŚNIENIE

Zanim przejdziemy do przykładu, na który już pewnie wszyscy czekają z niecierpliwością, chcę wyjaśnić jedną kwestię...



Pojęcia IoC i DI są nagminnie używane zamiennie. IoC i DI nie są synonimami. Di jest jednym ze sposobów na osiągniecie IoC. Owszem najczęściej realizujemy IoC przez DI ale to nie znaczy, że można je utożsamiać.


PRZYKŁAD WSTRZYKIWANIA ZALEŻNOŚCI PRZEZ SPRING IoC

Na potrzeby przykładu zaimplementowałem projekt przedstawiony wcześniej w UML. Struktura pakietów odpowiada standardowym warstwom, więc mamy kolejno (wg"wysokości" a nie wg sortowania przez drzewko pakietów w Eclipse):




  • ui - interfejs użytkownika. Żałosny ten przykładowy interfejs, ale działa;P
  • logic.application - zawiera logikę naszej apliakacji, czyli konkretny Use Case - usługę składania zamówienia (jest to podejście proceduralne, ale co tam - na potrzeby tego przykładu wystarczające)
  • logic.business - model biznesu, mamy tu dwie standardowe figury z Domain Driven Desing: Encję oraz Politykę. Polityki są przykryte stabilnym interfejsem ponieważ zakładamy, że są polimorficzne. Polityka to nic innego mój ulubiony wzorzec projektowy - Strategy.
  • dao - zawiera interfejs OrderDAO i jego przykładową implementację opartą na XML


Przyjrzyjmy się bliżej klasie OrderServiceImpl z pakietu logiki aplikacji. Jej trywialna metoda biznesowa makeOrder jedyne co robi to skorzystanie z polityki podatkowej w celu wyliczenia podatku i z DAO w celu zapisania zamówienia.


@Override
public void makeOrder(Order order) {
taxPolicy.calculateTax(order);
orderDAO.saveOrder(order);

}


Metoda ta korzysta z prywatnych pól.

private OrderDAO orderDAO;
private TaxPolicy taxPolicy;

Skąd wezmą się ich wartości? Oczywiście zostaną wstrzyknięte przez kontener IoC Springa. W jaki sposób? Poprzez settery:

public void setOrderDAO(OrderDAO orderDAO) {
this.orderDAO = orderDAO;
}

public void setTaxPolicy(TaxPolicy taxPolicy) {
this.taxPolicy = taxPolicy;
}


Zwróćmy uwagę iż settery występują jedynie w klasie implementacji i nie ma ich w interfejsie. Po prostu to ta konkretne implementacja serwisu potrzebuje do swego sensownego działa tych 2 zależności. Być może inne implementacje nie koniecznie muszą mieć takie same zależności. Dlatego nie zakładajmy nić o zależnościach i nie umieszczajmy setterów w interfejsach.

Cały dramat zaczyna się właśnie w klasie UI nazwanej dumnie ConsoleApplication.

ApplicationContext context = new ClassPathXmlApplicationContext(
new String[]{"application-context.xml"});
BeanFactory factory = context;

OrderService orderService = (OrderService) factory.getBean("orderServiceBean");
orderService.makeOrder(new Order());


I to wszystko. Kolejne linijki odpowiadają za:

  • Utworzenie kontekstu Spring. W tym przypadku tworzymy go używające klasy
    org.springframework.context.support.ClassPathXmlApplicationContext.ClassPathXmlApplicationContext zajmuje się ona wyszukaniem podanego pliku konfiguracyjnego (który powinien znajdować się na class path). Plik ten zawiera definicję wszystkich zależności; zostanie omówiony za chwilę. Kontekst podczas "wstawania" zaczytuje konfigurację i ewentualnie tworzy obiekty wraz z ich zależnościami.
  • Rzutowanie kontekstu na fabrykę to jedynie lukier i dobra praktyka. Kontekst posiada wiele metod "technicznych", które nie są potrzebne "zwykłym" kawałkom kodu. Do pobierania obiektów z kontenera wystarczy nam interfejs BeanFactory.
  • Pobranie z kontekstu obiektu o zadanej nazwie. Fabryka zwraca Object więc potrzeba rzutowania. W tym miejscu interesuje mnie jedynie interfejs obiektu; z resztą nie mam nawet pojęcia jaka konkretna klasa może siedzieć w konfiguracji.
  • Pozostało nam już tylko wykonanie metody biznesowej na pobranym z kontenera obiekcie.


Łatwe, proste i przyjemne.

Gdzie cała magia? Oczywiście w konfiguracji Springa - plik application-context.xml (Przy okazji dodam, że wygodne może być rozbicie konfiguracji na kilka plików; np osobny plik dla każdej z warstw).

CO MISIO MA W ŚRODKU...

Składania XMLa konfiguracyjnego dla Springa jest dosyć intuicyjna.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<bean id="orderServiceBean" class="org.slawek.project1.logic.application.impl.OrderServiceImpl">
<property name="taxPolicy"><ref bean="polishTaxPolicy"/></property>
<property name="orderDAO" ref="xmlOrderDAO"/>
</bean>

<bean id="polishTaxPolicy" class="org.slawek.project1.logic.business.policies.impl.PolishTaxPolicy">
<constructor-arg type="int" value="22"/>
</bean>

<bean id="xmlOrderDAO" class="org.slawek.project1.dao.xml.XMLOrderDAO"/>
</beans>


Przeanalizujmy plik konfiguracyjny od dołu. Deklarujemy obiekt o nazwie xmlOrderDAO podając konkretną jego klasę. Wyżej podobnie deklarujemy obiekt polishTaxPolicy. Jednak akurat ta polityka zależy od warośći VAT, którą potrzebuje w swym konstruktorze - wstrzyknięcie do konstruktora deklarujemy w tagu constructor-arg.

Na końcu (a właściwie początku) deklarujemy obiekt orderServiceBean - ten sam, który pobieramy z BeanFactory. Obiekt ten zależy od 2 poprzednich - ma nawet settery, które tylko czekają aby im wstrzyknąć jakieś implementacje interfejsów. Wstrzyknięcie poprzez setter deklarujemy z użyciem tagu property.

Spring "widząc" taką konfigurację "wie" czym jest obiekt orderServiceBean (żądany w aplikacji z fabryki) i wie również jak wstrzyknąć jego zależności, które oczywiście same mogą mieć również własne zależności.

INTEGRACJA CAŁKOWITA
Powyższy przykład ma jedną wadę. Najwyższa warstwa aplikacji korzysta z kontekstu Springa. Owszem klasy niższych warstw nie zajmują się już pozyskiwaniem swych zależności, ale niestety nie uniknęliśmy tego w warstwie UI.

Nie wiem jak wygląda to w aplikacjach standalone, ale Spring gładko integruje się z frameworkami webowymi.




W przypadku czystego JSF czy Seam wystarcz w web.xml zadeklarować listenera, który "podniesie" kontekst Springa (wskazując mu ewentualnie lokalizację plików konfiguracyjnych). Później już tylko ustawienie specjalnego resolvera w faces-config.xml i o tej pory w wyrażeniach EL możemy się posługiwać obiektami z kontekstu Springa. W przypadku integracji z Seam możemy nawet deklarować zasięg beanów Springowych tak samo jak Seamowych. Więcej o integracji Seam ze Spring tu.

Ogólnie nie mamy już tej niezręcznej sytuacji polegającej na tym, że najwyższa warstwa jednak zdobywa zależności we własnym zakresie.

SINGLETONY

Domyślnie wszystkie beany w Springu są singletonami (attrybut scope tagu bean). Zmiana jego wartości na prototype skutkuje tym, że wstrzyknięcie referencji spowoduje wstrzyknięcie całkiem nowej instancji.



OGRANICZENIA WSTRZYKIWANIA ZALEŻNOŚCI W SPRINGU (UWAGA - KONICZYNKA)

W odróżnieniu od nowszych frameworków (np Seam) Spring wstrzykuje zależności podczas tworzenia obiektów. Co jeżeli Spring nie będzie odpowiedzialny za stworzenie obiektu - tak jak np w wypadku encji tworzonych przez frameworki ORM? Jeżeli Spring czegoś nie tworzy, to nie może temu czemuś wstrzyknąć zależności - logiczne.



Pewnie niejeden czytelnik zadaje sobie w tym momencie pytanie: o czym on do cholery pisze? Wstrzykiwanie zależności do encji z hibernate? Otóż z godnie z Domain Driven Design encje powinny mieć odpowiedzialność biznesową. W końcu to obiekty a nie jakieś recordy z Pascala albo struct z C.

Jednym ze sposobów na wstrzyknięcie zależności do encji jest Springowa adnotacja @Configurable. Niestety polega ona na weavingu bytecodu podczas ładowania więc praktycznie wyklucza to testowalność.
Zalecanym patentem są interceptory Hibernate. Interceptor może po stworzeniu encji ustawić jej to i owo.

Czyli oficjalne wersja jest taka: do encji wstrzykujemy zależności poprzez interceptory hibernate a wszystko inne jest nakłuwane Springiem. Moim skromnym zdaniem dwa sposoby na wstrzykiwanie zależności to co najmniej o jeden za dużo! Sugeruję aby zamiast rzeźbić po prostu pozbyć się problemu:)



Zamiast wstrzykiwać do encji sugerują aby wstrzykiwać do warstwy wyższej - logiki aplikacji. Scenariusz wyglądał by tak:
1. Coś (np polityka) jest wstrzyknięta do serwisu aplikacyjnego
2. Serwis przy pomocy DAO/Repozytorium pobiera encję
3. Serwis po prostu ustawia zależność na pobranej właśnie encji

Dzięki temu przy tak zwanej okazji dostajemy za darmo użyteczne narzędzie. Możemy z dużym prawdopodobieństwem założyć, że w różnych use case, czyli różnych serwisach aplikacyjnych będą potrzebne np różne polityki. W sugerowanym podejściu nie stanowi to żadnego problemu. Do jednego servisu wstrzykujemy politykę A, do drugiego B. Servis gdy trzeba ustawi swoją (wstrzykniętą) politykę do encji. Natomiast w razie gdybyśmy uparcie dążyli do wstrzykiwania zależności (np polityk) wprost do encji wówczas byłby niemały problem.

ZALETY PŁYNĄCE Z IoC i DI

Inversion of Control skłania do projektowania systemów za zasadzie konfigurowalnych (a co za tym idzie reużywalnych) komponentów. Posługując się Dependency Injection możemy dostarczać klientom różne wersje systemów, które technicznie różnią się jedynie plikami konfiguracyjnymi Springa.

Pamiętajmy jednak, że sam fakt korzystania z kontenera IoC nie zapewnia nam eleganckiej i reużywalnej architektury. Wszystko zależy od ilości i jakości pracy umysłowej włozonej w projekt. DI to jedynie narzędzie do realizacji naszej dobrej lub błędnej koncepcji.

Projektowanie systemu na zasadzie niezależnych komponentów drastycznie zwiększa testability. Idąc dalej możemy przygotowywać specjalne wersje plików konfiguracyjnych spreparowana na potrzeby testów integracyjnych. Na ten przykład w razie zależności od modułu komunikującego się z zewnętrznym systemem możemy na czas testów wstrzyknąć implementację tego modułu, która jedynie symuluje interakcję. Przydaje się to na pewno gdy interakcja jest długotrwała a my chcemy przeprowadzić testy masowe. Oczywiście kontener IoC nie powinien być angażowany do testów jednostkowych.



No więc właśnie...

Wstrzykiwanie zależności to jedynie fundament,na którym stoi framework. Zresztą zobaczmy na architekturę Springa



Oraz na architekturę pełnej aplikacji



Mam nadzieję, że powyższa architektura wygląda zachęcająco do dalszego zgłębiania Springa...

Jak widać Spring stawia na integrację dobrych rozwiązań zamiast na implementację wszystkiego od nowa.


Chociaż gdyby stale nie wymyślano koła od nowa to jeździli byśmy wciąż na drewnianych krążkach.



//========================

Prezentacja do ściągnięcia tu: open office i mikrosoft ofis. Natomiast przykłady zawarte zostały w prostym projekciku Eclipse. Projekcik ma strukturę źródeł zgodną z Maven ale sam nie jest projektem Maven - biblioteki są po prostu skopiowane do katalogu lib.


EDIT
video: część 1, część 2

sobota, 1 listopada 2008

UP-DDD in Action: Malutka Teoria Unifikacji

Planowałem stopniowo wprowadzać w fazy Unified Process, jednak jak widzę zniecierpliwieni czytelnicy już teraz zastanawiają się jak pogodzić ze sobą UP i DDD:) Maciek - specjalnie dla Ciebie zwiększamy tempo.

W skrócie UP zakłada etapowe przeprowadzenie projektu. Etapy mogą (powinny) być powtarzane iteracyjnie. Z kolei etap możemy podzielić na fazy. Fazy są właściwie spojrzeniem z rożnych poziomów abstrakcji na nasz system. W każdej fazie powstają na wyjściu pewne artefakty, które są wejściem do fazy następnej. Po ostatniej fazie zamykamy iterację i powinniśmy mieć działający system.

W dużym skrócie i uproszczeniu mamy fazy:
- analizy biznesowej
- analizy systemowej
- projektowania (opartego na założonej/wypracowanej architekturze)
- implementacji
Kiedyś popełniłem ich zgrubny opis i niech to nam na razie wystarczy (druga połowa posta). Póki co warto pamiętać, że w fazie analizy powstaje model dziedziny problemu a w fazie projektowania powstaje między innymi model obiektów biznesowych. Poczucie estetyki i intuicja biją na alarm: dwa modele to co najmniej o jeden za dużo! Gdzie jest Ockham ze swoją brzytwą?!

Domain Driven Design skupia się na fazie analizy systemowej i projektowania. Ojciec założyciel DDD (Eric Evans) dokonał niebywałego odkrycia;) Unifikacja! Co prawda nie jest ono na miarę GUT ale wnosi coś nowego do UP. Mianowicie utrzymywanie dwóch modeli: analitycznego i projektowego prowadzi do zasadniczego problemu: modele z czasem się rozjeżdżają. Cholera - nikt się chyba tego nie spodziewał (tak jak hiszpańskiej inkwizycji). Z czasem modyfikacje wprowadzane na poziomie projektu nie są aktualizowane na poziomie analizy. A z drugiej strony nowe odkrycia analitycznie nie są projektowane z braku czasu.

Zatem nie udawajmy, że wszystko jest git i nie oszukujmy się, że grzecznie będziemy pilnować spójności obu modeli. W brutalnej rzeczywistości komercyjnych projektów korporacyjnych to se ne da i kropka. Dobrze wiemy, że wystarczającym problemem jest już samo utrzymanie spójności pomiędzy implementacją a modelem projektowym:)

Zamiast walczyć z jakimś zjawiskiem, nauczmy się z nim żyć, albo jeszcze lepiej wykorzystajmy na własną korzyść. To właśnie postuluje DDD. Niech nasz projekt obiektów biznesowych będzie jednocześnie analitycznym modelem domenowym. Przecież sama koncepcja obiektowości powstała po to, aby struktury techniczne odpowiadały mniej więcej bytom z dziedziny problemu. Oczywiście analityk czasem musi się nagiąć nieco w stronę technikaliów i uszczknąć nieco ze swego pięknego modelu absolutu. Z drugie strony projektant musi zapomnieć o optymalnym modelu klas czy bazy. Na szczęście doświadczony projektant OO jest w stenie w miarę wiernie oddać strukturę domeny biznesowej. W razie czego kompromis to podstawa. Heh szczęście w nieszczęściu gdy analitykiem i projektantem jest jedna i ta sama osoba:)))



Opowiadając już konkretnie na pytania Maćka:
Wydaje mi się, że przepaść pomiędzy modelem analitycznym a projektowym nie powinna być problemem dzięki zastosowaniu starego indiańskiego triku architektonicznego - warstw.

Patrząc na projekt systemu i skupiając się na na warstwie logiki biznesowej powinniśmy widzieć nic innego jak analityczny model domenowy z UP. W DDD istnieje zestaw figur (zwanych building blocks) z których możemy budować model obiektów biznesowych. Tutaj ich opis.
A skupiając się na warstwie logiki aplikacji powinniśmy widzieć analityczny model przypadków użycia.



Dzięki temu, że takie "szczegóły" jak persystencja i prezentacja są wydzielone do innych warstw, warstwy logiki są "czyste" i możemy ich projekt - przy odpowiednim nastawieniu - traktować również jako model analityczny.

Odnośnie drugiej części pytania Maćka - o rzeczywistość: udało mi się ustalić dobre zasady współpracy z "ekspertami biznesowymi". Mam do nich dobry dostęp i możemy wspólnie tworzyć dokumentację analityczną - która przy okazji jest projektem moich warstw logiki:) Czyli przysłowiowe 2 pieczenie przy 1 ogniu. Jest to dla mnie bardzo ważne, ponieważ jak wszyscy pewnie wiemy logiczne rozumowanie nie jest mocną stroną "ludzi biznesu";) O myśleniu obiektowym możemy zapomnieć. Często dokumenty przez nich tworzone nie nadają się do czytania - a co dopiero analizowania.
Czasem dochodzi do takiej paranoi, że ktoś pisząc wymagania biznesowe niemal "szyfruje" w dokumencie prostą i oczywistą treść. Później analityk systemowy/projektant/programista musi to odszyfrowywać i domyślać się o so choszi.
Do tego nie każdy potrafi sensownie napisać parę stron, akapit czy choćby jedno zdanie! Pojawia się też problem kognitywny. Ja na ten przykład preferuję podejście top-down. Najpierw muszę uchwycić ogólną ideę a później mogę zgłębiać szczegóły. Krew mnie zalewy gdy muszę czytać czyjeś polucje, które akurat powstały w odwrotnym toku rozumowania.

Natomiast pismo obrazkowe (UML) ma to do siebie, że ciężko nabazgrać coś co jest kompletnie bez sensu. A w razie czego od razu widać:) Oczywiście sam fakt stosowania jakiegoś formalizmu nie implikuje, że dokumentacja musi mieć jakąkolwiek wartość oprócz dupochronu w razie szukania kozłów ofiarnych. O jakości dokumentacji stanowi tak na prawdę ilość pracy umysłowej włożonej w analizę.

Dlatego o wiele lepszym podejściem jest wspólne (ekspert biznesowy + analityk systemowy) tworzenie dokumentacji od razu w UML. Ekspert mówi, a analityk rysuje. Analityk pyta, ekspert odpowiada, analityk poprawia rysunek. Oczywiście nie zawsze mamy taki komfort.

//====================
Gdy się przyjrzeć z dystansem założeniem UP czy DDD można dojść do wniosku, że jest nic innego jak zestaw racjonalnych wytycznych. Są to oczywiste oczywistości, na które każdy kto jest skłonny do odrobiny refleksji wpadły sam - prędzej czy później. UP czy DDD jedynie zbierają i strukturyzują zestaw intuicyjnych zasad.

Aż dziw bierze gdy się zastanawiam jak w ogóle mogłem postępować (będąc zmuszanym) im wbrew i mało tego - coś tam powstało i nawet jakoś działa:P
Biedni spadkobiercy spuścizny...

sobota, 25 października 2008

Unified Process & Domain Driven Design in Action

Dzisiaj pierwszy post z nowej serii. Jest to odcinek pilotażowy tasiemca, który z założenia ma być od teraz głównym wątkiem tego blogaska. Zgodnie z tytułem blogaska - będzie holistycznie (hasło to przestanie być pretensjonalnym buzzwordem).

Przygotowuję się powoli do rozpoczęcia nowego projektu. Jest to któryś już z kolei projekt, w którym będę uczestniczył - tym razem jako team leader. Jak zwykle obiecuję sobie, że tym razem będzie bardziej profesjonalnie i że tym razem będzie to miało przysłowiowe ręce i nogi (oraz nie 3 ręce i nie 8 nóg).

W ciągu pięciu lat pracy dobrze nauczyłem się jak NIE podchodzić do projektów - jest to wiedza praktyczna. Marność nad marnościami. Jednak tym razem zamierzam zastosować nabytą w ciągu ostatniego roku wiedzę teoretyczną... Unified Process (krótki wstęp) będzie procesem wytwórczym według którego będę chciał postępować. Główne fazy UP to analiza i projekt. Aby im sprostać posłużę się wytycznymi Domain Driven Design (dotychczasowe wypociny).

Zatem zamierzam przeprowadzić studium przypadku. Od czasu do czasu będę relacjonował jak sprawdza się UP i DDD w praktyce. Oczywiście treść będzie obdarta z kontekstu biznesowego:) Przykłady z wiadomych względów zostaną podniesione na nieco wyższy poziom abstrakcji.

Dodatkowo całość będzie zanurzona w kontekście technologicznym. Sam jestem ciekaw jak frameworki udźwigną ambitne założenia DDD:) Nie będę zanudzał tutorialami o popularnych frameworkach - jest tego pod dostatkiem i każdy na pewno znajduje dla siebie materiały odpowiadające osobistym preferencjom kognitywnym.


Relacja pomiędzy UP a DDD polega na tym, że UP jako proces wytwórczy prowadzi przez kolejny fazy (analiza biznesowa, systemowa, architektura, projekt implementacja, testy, wdrożenie); fazy dodatkowo zapętlone w iteracje. Natomiast DDD jest sposobem myślenia o fazach analizy (OOA) i projektowania (OOD) jako całości.

//===================
Nie pozostaje mi nic innego jak zaprosić do śledzenia postów otagowanych "UP-DDD in Action"

niedziela, 19 października 2008

Szkielety, zręby, rusztowania, ruiny

Zbieram sobie spokojnie materiały do prezentacji o Inversion of Control w Springu (w sumie to raczej o Springu:) na potrzeby naszego JUG. Aż tu nagle... prowokujący post Piotrka.

Jako wstęp do pracy nad prezentacją o Springu, dziś będzie o frameworkach w ogóle.

Najpierw należy odróżnić framework od biblioteki. Biblioteka to będzie chyba zbiór reużywalnych i sprawdzonych kawałków kodu skupionych wokół jakiegoś problemu. Nie będę tutaj silił się na definicję, wiadomo o co chodzi (jeżeli ktoś się chce do niej przyczepić to proszę bardzo;P). Ogólnie sytuacja jest taka: masz zestaw klas czy procedur (w wersji oldskulowej) i radź sobie. Składaj je w coś większego.
Natomiast framework ma już swą formę. Jest rozwiązaniem ogólnym, w którym brakuje jedynie szczegółów, które to musimy właśnie zdefiniować. Coś jak wypełnianie szkieletu mięsem. Zazwyczaj zajmuje się powtarzalnymi szczegółami technicznymi - dla nas zostaje biznes. Przynajmniej takie są założenia;)
Jeszcze jedna kategoria: zaznaczam, że nie chcę wyjść na zarozumiałego dupka, który lepiej od twórców Springa wie czym jest Spring, ale wydaje mi się, że jest on biblioteką frameworków a nie frameworkiem.


Komentując posta podlinkowanego przez Piotrka... Rzeczywiście jest tak, że jakaś część programistów podchodzi do frameworków z oporem. Dziwne, bo wydaje mi się, że prędzej czy później każdy jakieś swoje nano-frameworki tworzy. Jest to chyba immanentna cecha programistów, którzy uchwycili w pewnym momencie Object Oriented. Najpierw zaczynasz od uogólnień i abstrakcji. Projekt cieszy swą elegancją i prostotą - cieszy to niestety tylko Ciebie i ew. paru kolegów. Później w razie potrzeby refaktoryzacja do paru wzorców projektowych oraz wyciągnięcie konfiguracji na zewnątrz kodu. Okazuje się, ze konfiguracji jest zbyt wiele (koszmar XML), więc idąc z duchem czasu stawiamy na Convention Over Configuration. I quasi-framework gotowy. Heh hiestety zazwyczaj jego życie trwa jeden projekt bo do innych się nie nadaje... jest zbyt mało ogólny - ale to szczegół.

Z drugiej strony... frameworki budzą niechęć. Z obserwacji wydaje mi się, że przyczyny mogą być różne:

ignorancja - w przypadku niektórych frameworków bez biegłości w OO i wzorcach projektowych może być niestety ciężko połapać się o co chodzi w danym frameworku. Przytłacza on swą pozorną (bo wynikającą z niewiedzy) złożonością. Patrząc bardziej ogólnie problem może polegać na niechęci do zmian i poznawania nowych rzeczy.

pycha - jest jakaś część programistów, która nie wyrosła jeszcze ze śpioszków i twierdzą, że każdą kupę potrafią zrobić sami. Więc radośnie wynajdują np ORM na nowo. Tworzą coś co już zostało napisane zamiast skupić się na rozwiązaniu problemu klienta (biznesowego problemu). Bo ja sam UMIE i nie potrzebuje tutaj niczyjej pomocy a wzorce są dla cieniasów - Nie no fajnie, ale to do cholery kosztuje! Oczywiście niektórzy z czasem dochodzą do poziomu na którym ich rozwiązania są reużywalne - powstają wówczas chałupnicze nano-frameworki domowej roboty. Pycha każe im sądzić, że są one lepsze i mają mniej bugów niż coś co powstało w silnym community. Patrząc bardziej ogólnie: pyszne Zosie Samosie gardzą nie tylko frameworkami ale i oldskulowymi bibliotekami - po prostu wszystkim czego same radośnie nie spłodziły.

lenistwo - lepiej samemu coś napisać niż wertować strony dokumentacji istniejącego frameworka. Do tego jeszcze tony konfiguracji (niestety nasz własny framework jeżeli ma być frameworkiem też będzie musiał być jakoś konfigurowalny:P). Oczywiście na krótką metę lub w małych projektach pisanie z palca może być bardziej opłacalne. Patrząc bardziej ogólnie: co z samorozwojem? Przyjrzenie się architekturze sprawdzonego rozwiązania zazwyczaj otwiera nowe furtki w mózgu - pewne konstrukcje projektowe mogą być na prawdę inspirujące.

Kwestia najważniejsza: tytułowe ruiny. Frameworki (nawet te poważne i "oficjalne") nie zawsze muszą być arcydziełem. Nie koniecznie muszą rzeczywiście wspierać nas w rozwiązaniu problemu. W praktyce mogą przeszkadzać i irytować. Np taki JSF - wydaje się być zaprojektowany przez doskonałych inżynierów oprogramowania, którzy jednak niestety chyba nigdy w życiu nie popełnili żadnej aplikacji webowej (choćby w php).

//===================
Dobry framework powinien być tak zaprojektowany aby można było wymienić w nim nie tylko "mięcho" ale również część niego samego, czyli "szkieletu". Stąd chyba w Springu tyle interfejsów w bardziej jak i mniej newralgicznych miejscach:)

środa, 1 października 2008

Bottega

Przez ostatnich parę dni szukałem swojego dyplomu ukończenia studiów. Bezskutecznie przetrząsając różne szpargały natknąłem się na wiele "pamiątek" z czasów studiowania informatyki. Niestety, nie były to pamiątki budzące sentymentalne wspomnienia. O nie...

Były to kserówki podręczników akademickich, skryptów i wykładów. Z zaciśniętymi zębami (na granicy szczękościsku;) mogłem odświeżyć sobie nieco wiedzę o: całkach, równaniach, obwodach RLC, równaniach, pierścieniach, równaniach, ciałach, równaniach, niezmiennikach, równaniach, złożoności, równaniach, twierdzeniu Kołmogorowa, równaniach, nieskończonej studni potencjału, równaniach, śmiesznej notacji składającej się z prostokątów i elips przedstawiającej składnię Pascala (wszystkie diagramy umiałem na pamięć:] ), równaniach, postaciach normalnych i mój ulubiony killer: gradient dywergencji rotacji laplasjanu hamiltonianu:)))
Tresowanie szympansów na umiejętność przetwarzania symboli - iksy na prawą, ygreki na lewą.

Okazuje się, że można też inaczej: "Systems Development": a New Discipline for a New Education.


Bottega - pracownia renesansowego mistrza. Miejsce, w którym Leonardo i jemu podobni oddawali się twórczości. Miejsce, które dzięki swej aranżacji i zawartości stymulowało w sposób naturalny różne aspekty kreatywności. Miejsce, w którego centrum stał człowiek.

Bottega stała się w przenośni i dosłownie inspiracją dla twórców wspomnianego w linku eksperymentalnego kierunku studiów "Systems Development". Kierunku, którego credo zawarte jest w sugestywnym symbolu: "Software is our medium, Craft (Mastery) is our goal, People are our focus, Systems is our perspective, and Agility is our process." Bottega promuje raczej myślenie syntetyczne niż analityczne. Synteza wielu aspektów ludzkiej aktywności daje szansę na powstanie nowej wartości.

Dr. Dave West zauważa, że mimo postępu procesów i technologii nadal utrzymujemy dosyć żenujący współczynnik projektów zakończonych sukcesem. Jako jedną z przyczyn wskazuje przepaść pomiędzy tym, czego nauczani są studenci, a tym czego wymaga rzeczywistość.

Nie chodzi tutaj bynajmniej o prymitywne podejście i roszczenia wobec nauczania trendowych technologii czy języków. Nic z tych rzeczy, uczelnia nie ma przecież na celu przyuczenie do jakiegoś zawodu. Problem jest postawiony dużo głębiej. Aby w ogóle myśleć o tworzeniu systemów dla ludzi, wypadałby najpierw rozumieć systemy ludzkie... Ale nie będę streszczał i tak krótkiego artykułu - na prawdę warto przeczytać (inaczej chyba bym nie zawracał głowy;P).


Jakie mierzalne wyniki edukacyjne daje zmiana podejścia? Wystarczy przeczytać artykuł do końca:P

//========================

Heh... Humanizm... trochę wypaczono go ostatnio - humanistą jest każdy, kto nie potrafi liczyć, ale za to potrafi zapamiętać parę tysięcy stron kodeksów czy oficjalnej wersji historii;))).