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...