Dziś jedynie dwa linki, które uważam za godne polecenia.
Przeczytałem niedawno tekst, który lekko zachwiał moim światopoglądem: Aristotle's Error or Agile Smagile. Tekst traktuje ogólnie o historii - znajdziecie tam również rzekomą genezę Object Oriented.
Rzekomo pierwotnie wcale nie chodziło o modelowanie świata, lecz o zabawy ze stertą - co będzie gdy zamiast chwilowo przechowywać zmienne na stosie wrzucimy je na dłużej do sterty.
Teorię OO Analysis i OO Design rzekomo "dorobiono" później. Tak, tak, wiem - sam jestem w szoku podobnym do tego gdy wydedukowałem, że nie ma żadnego św Mikołaja;P
Nie wiem na ile historia o pierwocinach OO jest prawdziwa, ale tym razem wielki szacunek dla Uncle Boba za zdystansowane podejście.
Kolejny link to ciekawa prezentacja człowieka, któremu doświadczenie również pozwala zdystansować się od mainstreamu Are We There Yet?. Prezentacja pozwala uświadomić sobie ograniczenia OO oraz wyjaśnia (tym, którzy jeszcze tego nie widzą) dlaczego niektóre wydawałoby się proste języki wprowadzają wysoką złożoność (przypadkową).
Zobaczycie też jak bardzo podobne są do siebie języki, o które często spieramy się "który lepszy". Nie brakuje też aluzji do takich "przełomowych usprawnień" jak usunięcie średników:) Dowiecie się również z czego wg autora prezentacji wynika potrzeba posiadania testów:)
//=======================
Od razu ostrzegam, że treści kryjące się pod linkami są mocno filozoficzne, ale może warto oderwać się na chwilę od kodu aby później móc spojrzeć na niego inaczej...
Inżynieria oprogramowania w ujęciu systemowym.
Zintegrowane podejście do metodyk,
technologii (głównie Java EE), architektury i rozwoju ścieżki kariery programisty.
środa, 25 listopada 2009
wtorek, 10 listopada 2009
poniedziałek, 9 listopada 2009
Czysty kod to przetestowany kod?
Clean Code to skądinąd szczytna idea, na fali której kilka osób chce się wylansować ostatnimi czasy. Przyjrzymy się jej dzisiaj nieco bliżej aby sprawdzić czy nie kryje się za nią przypadkiem merytoryczna próżnia...
Ewangelizatorem tej idei jest nasz ulubiony Wujek Dobra Rada - Uncle Bob. Polecam wszystkim jego prezentację pt Craftsmanship and Ethics.
Uncle B z pasją opowiada o profesji i etyce w naszej branży. Właściwie to przekonuje, że już najwyższy czas aby wykrystalizowały się one bo póki co jeszcze się ich nie dorobiliśmy. Tutaj zgadzam się z nim bez 2 zdań. Metodyka byle-jak-byle-działało-ale-miało-zaokrąglone-rogi-na-gui to aktualnie standard.
Jako że są to idee bardzo mi bliskie to pierwsza część prezentacji jest na prawdę budująca. Dalej Uncje B twierdzi, że drogą do profesjonalizacji i nieskazitelnego kodu jest Test Driven Development. "Nie wolno Ci napisać ani linijki produkcyjnego kodu bez napisania uprzednio testu!" grzmi wujaszek! Kropka.
Postępuj tak a w nagrodzę po śmierci będą czekać na Ciebie w niebie zastępy dziewic - do tego nimfomanek rozumiejących maszynowy kod w reprezentacji binarnej. Marzenia...

...ale chyba nie będzie tak łatwo.
TDD owszem jest techniką, która ma swoje przeznaczenie i zastosowania ale nie dajmy się zwieść, że rozwiązuje wszystkie problemy. Co najwyżej niektóre.
Sensowna krytyka tego typu rozumowania znajduje się w typ poście: It's OK Not to Write Unit Tests. Wujkowi Dobra Rada chyba tylko wydaje się, że rozprawił się z adwersarzem przy pomocy racjonalnych argumentów: Excuse me sir, What Planet is this?
O ile w pewnych sytuacjach testy mogą "sterować" developmentem to nie zapominajmy o technikach, które rzeczywiście prowadzą do clean code.
Co nam po nawet 100% pokryciu kodu testami jeżeli kod będzie jedną wielką kupą...
Działającą nawet perfekcyjnie, ale wciąż jedną wielką kupą... [wstawić_swoją_ulubioną_metaforę]
Refaktoryzacja to podstawa? Co nam po wsparciu dla refaktoryzacji dzięki pokryciu kodu testami, które zwalniają nas z napięcia towarzyszącemu zmianom skoro... nie mamy pojęcia jak refaktoryzować ani w ogóle, w którą stronę refaktoryzować?
Do tego właśnie zmierzam... TDD nie daje żadnych wskazówek jakich technik developerskich użyć aby osiągnąć upragniony Clean Code. To oczywiste, że nie daje wskazówek, bo nie leży to w "domenie" TDD. Ale trzeba to sobie uświadomić i nie dajmy sobie wcisnąć kitu, że testy jednostkowe rozwiązują problem - są po prostu niewystarczające. Pomagają rozwiązać - ale rozwiązanie właściwe leży chyba gdzie indziej.
Czysty kod, aby powstał musi być wypracowany poprzez pewien wysiłek umysłowy. Nie wystarczy dorwać się do klawiatury i radośnie stukać (o tym na koniec). Na ten proces musi składać się rzetelna analiza problemu, projekt i dopiero implementacja. Nie mam tu oczywiście na myśli metodyki Waterfall, ale po prostu pewnej pracy, którą trzeba wykonać - nawet w umyśle jednej osoby.
Dla każdego z tych etapów mamy zestaw sprawdzonych technik oraz narzędzi, które oczywiście nie gwarantują sukcesu, ale zwiększają prawdopodobieństwo powstania upragnionego Clean Code.
Na poziomie analizy możemy posłużyć się Archetypami Modeli Biznesowych. Są to skatalogowane gotowe rozwiązania standardowych problemów biznesowych. Modele te zostały wypracowane przez wytrawnych analityków dzięki czemu można je dostosowywać do swych potrzeb. Są również zaprojektowane z myślą o rozbudowie modelu. Można by dużo o nich pisać, ale nic nie zastąpi przykładu. A przykład w jednym z najbliższych postów. Póki co zastanówmy się co nam po doskonale obłożonym testami modelu, który jest kupą...
Gdzieś na styku analizy i projektu mamy na przykład Domain Driven Design.
Zanim przejdziemy do poziomu projektu zatrzymajmy się przez chwilę na architekturze, będącej stylem, na podstawie którego będziemy projektować. Zastanówmy się co nam po doskonale obłożonym testami kod jeżeli architektura jest kupą...
Na poziomie projektu mamy zestaw technik np Object Oriented. GRASP i SOLID - pomagające w poprawnym interpretowaniu OO i uniknięciu proceduralnego kodu dziwacznie upchanego w klasy.
Nie zapominajmy o Wzorcach Projektowych. Kiedyś było o nich głośno, ale aktualnie młodzież woli sobie wyklikać/wygenerować trochę kodu. Ci bardziej Agile obłożą go paroma testami i jest git;)
Wzorce mogą pomagać u utrzymaniu czystej struktury kodu, ale przy ich "pomocy" można również bardzo skutecznie niepotrzebnie zagmatwać powinny pozostać prostsze - cała sztuka w wyważeniu. Ale tego oczywiście nie dowiemy się z marketingu TDD;)
Co do wzorców to planuję kiedyś stworzyć serię postów z realistycznymi (nieksiążkowymi) przykładami - ale jak zwykle czasu brak. Co do GRASP i SOLID to można znaleźć trochę dobrych przykładów - np ten.
Kiedy już wiemy co i jak to na poziomie implementacji możemy podeprzeć się techniką testów jednostkowych. Ale to czy kod będzie Clean zależy od pracy umysłowej włożonej również (albo przede wszystkim) wcześniej.
Konkludując: TDD ew. jako dodatek na drodze do Clen Code wujaszku;P
Aby uzupełnić całość warto wspomnieć o podejściu Specify First - dobry opis w poście TDD is not test-first. TDD is specify-first and test-last. Niektórzy twierdzą, że dopiero przy takim podejściu można mówić o prawdziwym TDD.
Chodzi generalnie o to, że pisząc najpierw test - nawet nie interfejs implementacji, a test - stawiamy się w roli kodu "klineta" danej klasy. Na pewno wpływa to pozytywnie na nasz design - bardzo pozytywnie.
Ale to znowu półśrodek. Technika owszem dobra, ale dla tych z nas, którzy są wciąż jeszcze na poziomie myślenia kodem.
Myślenie kodem to zupełnie normalna faza w rozwoju programisty i każdy przez nią przechodzi. Jeżeli jesteś początkujący to zapewne zauważyłeś, że gdy mówisz do kogoś ifami i pętlami to on krzywi się i zaczyna rysować jakieś prostokąty połączone strzałkami aby cokolwiek zrozumieć. Jeżeli bawisz się w to już kilka lat to zapewne irytują cię juniorzy, którzy mówią do Ciebie kodem. Normalna sprawa, abstrahowanie przychodzi z czasem ...
Zatem "Specify first" jest dobre na początek gdy świerzbią nas paluszki i chcemy jak najszybciej dorwać się do klawiatury i nastukać coś co można będzie za chwilę radośnie debugować. Analizowanie, modelowanie, projektowanie... to dobre dla zgredów.
Przy podejściu Top-Down rzeczywiście klasy "niższe" mogą być pokraczne bez wyobrażenie sobie jak będą używane. Ale top-down powinien być style analizowania problemu, natomiast projektowanie klas można przeprowadzić bottom-up dzięki czemu powstaną "komponenty" - niczym niezależne części "ruchomej machiny". Z czasem przychodzi umiejętność wyobrażania sobie i manipulowania obiektami "w pamięci" (własnej) bez potrzeby pisania czegokolwiek. Pomocy może być ew. ołówek, kartka i UML. Ktoś pamięta w ogóle co to jest UML? Nie to nie jest kolejna biblioteka do mockowania;P
Taaak.... kiedyś pewnie powstanie Unified Mocking Library - w sumie już nic mnie nie zdziwi w naszej branży...
//===============
Od razu uprzedzę ew. komentarze aby uniknąć niepotrzebnego niezrozumienia.
W tekście nie zajmuję stanowiska ZA albo PRZECIW testom jednostkowym. Moim celem było zwrócenie uwagi na fakt, że same testy niewiele wnoszą do koncepcji Clean Code. TDD jest techniką implementacyjną oraz od niedawna jest sprzedawany jako technika specyfikowania dla niecierpliwych/początkujących.
Moja teza jest następująca: aby osiągnąć Clean Code potrzeba dodatkowo kilku innych technik developerskich. Dopiero synteza całości może dać zadowalający rezultat.
I niestety trzeba się wysilić znacznie bardziej niż tylko dopisać więcej kodu - testującego. A podstawowe zagadnienia z zakresu inżynierii oprogramowania (Low Coupling, High Cohesion, IoC,...) przydają się również choćby do pisania kodu podatnego na testy - o wysokim poziomie testability.
Ewangelizatorem tej idei jest nasz ulubiony Wujek Dobra Rada - Uncle Bob. Polecam wszystkim jego prezentację pt Craftsmanship and Ethics.
Uncle B z pasją opowiada o profesji i etyce w naszej branży. Właściwie to przekonuje, że już najwyższy czas aby wykrystalizowały się one bo póki co jeszcze się ich nie dorobiliśmy. Tutaj zgadzam się z nim bez 2 zdań. Metodyka byle-jak-byle-działało-ale-miało-zaokrąglone-rogi-na-gui to aktualnie standard.
Jako że są to idee bardzo mi bliskie to pierwsza część prezentacji jest na prawdę budująca. Dalej Uncje B twierdzi, że drogą do profesjonalizacji i nieskazitelnego kodu jest Test Driven Development. "Nie wolno Ci napisać ani linijki produkcyjnego kodu bez napisania uprzednio testu!" grzmi wujaszek! Kropka.
Postępuj tak a w nagrodzę po śmierci będą czekać na Ciebie w niebie zastępy dziewic - do tego nimfomanek rozumiejących maszynowy kod w reprezentacji binarnej. Marzenia...

...ale chyba nie będzie tak łatwo.
TDD owszem jest techniką, która ma swoje przeznaczenie i zastosowania ale nie dajmy się zwieść, że rozwiązuje wszystkie problemy. Co najwyżej niektóre.
Sensowna krytyka tego typu rozumowania znajduje się w typ poście: It's OK Not to Write Unit Tests. Wujkowi Dobra Rada chyba tylko wydaje się, że rozprawił się z adwersarzem przy pomocy racjonalnych argumentów: Excuse me sir, What Planet is this?
O ile w pewnych sytuacjach testy mogą "sterować" developmentem to nie zapominajmy o technikach, które rzeczywiście prowadzą do clean code.
Co nam po nawet 100% pokryciu kodu testami jeżeli kod będzie jedną wielką kupą...
Działającą nawet perfekcyjnie, ale wciąż jedną wielką kupą... [wstawić_swoją_ulubioną_metaforę]
Refaktoryzacja to podstawa? Co nam po wsparciu dla refaktoryzacji dzięki pokryciu kodu testami, które zwalniają nas z napięcia towarzyszącemu zmianom skoro... nie mamy pojęcia jak refaktoryzować ani w ogóle, w którą stronę refaktoryzować?
Do tego właśnie zmierzam... TDD nie daje żadnych wskazówek jakich technik developerskich użyć aby osiągnąć upragniony Clean Code. To oczywiste, że nie daje wskazówek, bo nie leży to w "domenie" TDD. Ale trzeba to sobie uświadomić i nie dajmy sobie wcisnąć kitu, że testy jednostkowe rozwiązują problem - są po prostu niewystarczające. Pomagają rozwiązać - ale rozwiązanie właściwe leży chyba gdzie indziej.
Czysty kod, aby powstał musi być wypracowany poprzez pewien wysiłek umysłowy. Nie wystarczy dorwać się do klawiatury i radośnie stukać (o tym na koniec). Na ten proces musi składać się rzetelna analiza problemu, projekt i dopiero implementacja. Nie mam tu oczywiście na myśli metodyki Waterfall, ale po prostu pewnej pracy, którą trzeba wykonać - nawet w umyśle jednej osoby.
Dla każdego z tych etapów mamy zestaw sprawdzonych technik oraz narzędzi, które oczywiście nie gwarantują sukcesu, ale zwiększają prawdopodobieństwo powstania upragnionego Clean Code.
Na poziomie analizy możemy posłużyć się Archetypami Modeli Biznesowych. Są to skatalogowane gotowe rozwiązania standardowych problemów biznesowych. Modele te zostały wypracowane przez wytrawnych analityków dzięki czemu można je dostosowywać do swych potrzeb. Są również zaprojektowane z myślą o rozbudowie modelu. Można by dużo o nich pisać, ale nic nie zastąpi przykładu. A przykład w jednym z najbliższych postów. Póki co zastanówmy się co nam po doskonale obłożonym testami modelu, który jest kupą...
Gdzieś na styku analizy i projektu mamy na przykład Domain Driven Design.
Zanim przejdziemy do poziomu projektu zatrzymajmy się przez chwilę na architekturze, będącej stylem, na podstawie którego będziemy projektować. Zastanówmy się co nam po doskonale obłożonym testami kod jeżeli architektura jest kupą...
Na poziomie projektu mamy zestaw technik np Object Oriented. GRASP i SOLID - pomagające w poprawnym interpretowaniu OO i uniknięciu proceduralnego kodu dziwacznie upchanego w klasy.
Nie zapominajmy o Wzorcach Projektowych. Kiedyś było o nich głośno, ale aktualnie młodzież woli sobie wyklikać/wygenerować trochę kodu. Ci bardziej Agile obłożą go paroma testami i jest git;)
Wzorce mogą pomagać u utrzymaniu czystej struktury kodu, ale przy ich "pomocy" można również bardzo skutecznie niepotrzebnie zagmatwać powinny pozostać prostsze - cała sztuka w wyważeniu. Ale tego oczywiście nie dowiemy się z marketingu TDD;)
Co do wzorców to planuję kiedyś stworzyć serię postów z realistycznymi (nieksiążkowymi) przykładami - ale jak zwykle czasu brak. Co do GRASP i SOLID to można znaleźć trochę dobrych przykładów - np ten.
Kiedy już wiemy co i jak to na poziomie implementacji możemy podeprzeć się techniką testów jednostkowych. Ale to czy kod będzie Clean zależy od pracy umysłowej włożonej również (albo przede wszystkim) wcześniej.
Konkludując: TDD ew. jako dodatek na drodze do Clen Code wujaszku;P
Aby uzupełnić całość warto wspomnieć o podejściu Specify First - dobry opis w poście TDD is not test-first. TDD is specify-first and test-last. Niektórzy twierdzą, że dopiero przy takim podejściu można mówić o prawdziwym TDD.
Chodzi generalnie o to, że pisząc najpierw test - nawet nie interfejs implementacji, a test - stawiamy się w roli kodu "klineta" danej klasy. Na pewno wpływa to pozytywnie na nasz design - bardzo pozytywnie.
Ale to znowu półśrodek. Technika owszem dobra, ale dla tych z nas, którzy są wciąż jeszcze na poziomie myślenia kodem.
Myślenie kodem to zupełnie normalna faza w rozwoju programisty i każdy przez nią przechodzi. Jeżeli jesteś początkujący to zapewne zauważyłeś, że gdy mówisz do kogoś ifami i pętlami to on krzywi się i zaczyna rysować jakieś prostokąty połączone strzałkami aby cokolwiek zrozumieć. Jeżeli bawisz się w to już kilka lat to zapewne irytują cię juniorzy, którzy mówią do Ciebie kodem. Normalna sprawa, abstrahowanie przychodzi z czasem ...
Zatem "Specify first" jest dobre na początek gdy świerzbią nas paluszki i chcemy jak najszybciej dorwać się do klawiatury i nastukać coś co można będzie za chwilę radośnie debugować. Analizowanie, modelowanie, projektowanie... to dobre dla zgredów.
Przy podejściu Top-Down rzeczywiście klasy "niższe" mogą być pokraczne bez wyobrażenie sobie jak będą używane. Ale top-down powinien być style analizowania problemu, natomiast projektowanie klas można przeprowadzić bottom-up dzięki czemu powstaną "komponenty" - niczym niezależne części "ruchomej machiny". Z czasem przychodzi umiejętność wyobrażania sobie i manipulowania obiektami "w pamięci" (własnej) bez potrzeby pisania czegokolwiek. Pomocy może być ew. ołówek, kartka i UML. Ktoś pamięta w ogóle co to jest UML? Nie to nie jest kolejna biblioteka do mockowania;P
Taaak.... kiedyś pewnie powstanie Unified Mocking Library - w sumie już nic mnie nie zdziwi w naszej branży...
//===============
Od razu uprzedzę ew. komentarze aby uniknąć niepotrzebnego niezrozumienia.
W tekście nie zajmuję stanowiska ZA albo PRZECIW testom jednostkowym. Moim celem było zwrócenie uwagi na fakt, że same testy niewiele wnoszą do koncepcji Clean Code. TDD jest techniką implementacyjną oraz od niedawna jest sprzedawany jako technika specyfikowania dla niecierpliwych/początkujących.
Moja teza jest następująca: aby osiągnąć Clean Code potrzeba dodatkowo kilku innych technik developerskich. Dopiero synteza całości może dać zadowalający rezultat.
I niestety trzeba się wysilić znacznie bardziej niż tylko dopisać więcej kodu - testującego. A podstawowe zagadnienia z zakresu inżynierii oprogramowania (Low Coupling, High Cohesion, IoC,...) przydają się również choćby do pisania kodu podatnego na testy - o wysokim poziomie testability.
poniedziałek, 19 października 2009
Mały refreshing
Jako, że na blogspocie pojawiły się nowe szablony (a poprzedni zielony już mi się znudził) uznałem, że czas na zmianę.
Przy okazji lekkiej zmianie uległa sama nazwa bloga (poprzednia brzmiała chyba zbyt pretensjonalnie), motto i sekcja "o blogu".
//================
Zastanawiam się też nad pozbyciem się słowa "holistycznie" z nazwy - ostatnio kojarzy się ono z medycyną alternatywną, homeopatią i innymi tego typu rzeczami;) Ale nie wszystko n a raz.
Przy okazji lekkiej zmianie uległa sama nazwa bloga (poprzednia brzmiała chyba zbyt pretensjonalnie), motto i sekcja "o blogu".
//================
Zastanawiam się też nad pozbyciem się słowa "holistycznie" z nazwy - ostatnio kojarzy się ono z medycyną alternatywną, homeopatią i innymi tego typu rzeczami;) Ale nie wszystko n a raz.
niedziela, 18 października 2009
JUG, JDD, DDD, Cooluary v3 - podsumowanie

Tydzień upłynął pod znakiem Domain Driven Design.
Wtorek: prezentacja "DDD + impl w Seam" na lubelskim JUG.
Był to beta test przed piątkową ewangelizacją DDD w Krakowie podczas Java Developers' Day. Przy okazji podziękowania za feedback, który otrzymałem od wielu osób po wtorkowym wystąpieniu. Dzięki niemu udało mi się skompresować ponad 2h do 45 min na potrzeby JDD:)
Sobota: COOluary i powtórka DDD. Tym razem już 3h, sam DDD bez Seam - panowie słusznie założyli, że technikalia są drugorzędne.
Zarówno podczas prezentacji na JUG jak i DDD pojawiło się kłopotliwe pytanie odnośnie poniższego slajdu:

Slajd prezentuje Building Block, przy pomocy których to modelujemy domenę biznesową.
Ale zanim przejdziemy do pytania - przyda się wstęp. Jeżeli ktoś był na prezentacji, to może spokojnie przewinąć się niżej...
NIEKRÓTKI WSTĘP
Logikę dzielimy na dwie warstwy:
- Warstwę logiki aplikacji
- warstwę modelu domenowego
Warstwa logiki aplikacji jest cienka nie bez kozery. Zawiera ona logikę specyficzną dla danej aplikacji i jednocześnie nie będącą logiką reguł biznesowych - przykładowo: transakcje, bezpieczeństwo, jakiś mailing, ale również koszyk w sklepie internetowym. Model zamówienia i produktów to logika biznesowa, ale sam koszyk jest ficzerem aplikacji.
Warstwa modelu domenowego jest budowana z klas grających pewne role. Z ról wynika odpowiedzialność tych klas. Czyli opieramy się na paradygmacie Responsibility Driven Design.
Te role to:
Encje - identyfikowalne (posiadająće ID) byty biznesowe. Nie tylko rzeczowniki, ale również np metafory przejścia/zmiany stanu. Co ważne z rolą encji wiąże się realizacja odpowiedzialności biznesowych. Zatem mówimy stanowcze NIE dla anemicznego modelu;)
Inaczej niż w popularnych ORMach encja jak najbardziej powinna mieć metody biznesowe.
Value Objects - obiekty nieidentyfikowalne i zwykle niemodyfikowalne. Jeżeli 2 Vo mają takie same atrybuty to są wówczas tożsame. VO również mogą posiadać odpowiedzialność biznesową. W pewnych podejściach są marginalizowane a w innych wręcz wypierają encje - ale to temat na osobne posta, więc zostawmy.
Agregaty - Encje, które zawierają w sobie grafy encji lub VO. Co ważne agregaty hermetyzują swą implementację więc wszelkie operacje na wnętrznościach wykonujemy przez metody biznesowe encji głównej będącej Aggregate-root.
Repozytoria - zarządzają persystencją Encji/agregatów. To oczywiście jedynie interfejsy; implementacje są warstwę niżej. Nie ma tu metod wyszukujących po kryteriach pochodzących z GUI znanych z DAO.
Fabryki - hermetyzują złożoną (zwykle) logikę tworzenia agregatów.
Servisy - servisy biznesowe (nie mylić z aplikacyjnymi) zawierają logikę, której nie sposób sensownie przypisać do żadnej encji/agregatu/vo.
Polityki - to nic innego jak Wzorzec Strategii. Enkapsulują zmienność/wariacje logiki biznesowej. Jest to kluczowa figura podczas modelowania i rozmowy z expertem biznesowym.
Zdarzenia biznesowe - chodzi po prostu o decoupling artefaktów biznesowych od technicznych obiektów obsługujących. Dodatkowo możemy mieć asynchroniczność.
Przykład agregatu: Zamówienie zawierające pozycje zamówienia:
@Entity
public class Order{
@Id private OrderId id;
@OneToMany private List<OrderItem> items = new ArrayList();
private BigDecimal sum = new BigDecimal(0);
//.... status, createDate, rebatePolicy, productRepository,...
public void add(Product product, int quantity){
OrderItem oi = orderItemFactory.build(product, quantity, rebatePolicy);
//TODO jeżeli produkt jest już na liście to możemy jedynie zmienić ilość w odpowiednim order item
items.add(oi);
sum = sum.add(oi.getCost());
}
public void submit(){
if (status != Status.NEW)
throw new InvalidStateException();
status = Status.IN_PROGRESS;
createDate = new Date();
eventsManager.handle(orderEventsFactory.orderSubmitted(this));
}
public Iterator<OrderItem> getOrderItems(){
return items.iterator();
}
}
Kod zawiera kilka charakterystycznych dla DDD konstrukcji.
- lista produktów jest prywatna i nie ma do niej gettera a już niedopuszczalny jest setter. Dostęp do listy poprzez iterator, który nie umożliwia zmiany.
Pamiętajmy, że w JPA nie musimy uzywać getterów/setterów - wystarczy adnotacji na polu, wówczas ORM używa refeksji. - operacje biznesowe poprzez odpowiednie metody: add, submit
- operacja add przyjmuje produkt - szczegółem impl jest tworzenie wewnątrz zamówienia jakiegoś OrderItem
- operacja add zmienia wewnętrzny stan - co by było gdyby można było zmieniać listę udostępnioną getterem? Kto musiałby pamiętać o zmianie sumy?
- operacja submit zmienia jakieś pola - jakie? to szczegół. Pojawią się nowe pola w encji Order - wówczas zmieniamy tylko metodę submit. Operacja dodatkowo może sprzeciwić się rzucając wyjątek oraz generuje darzenie.
"KŁOPOTLIWE" PYTANIE
Czy agregat/encja może wołać: repozytorium/servis/fabrykę?
Ogólnie: jakie zależności (pomiędzy czym a czym) są dozwolone w DDD?
Przyznam się, że sam miałem kiedyś tego typu pytania... Nie dawały mi spokoju: jakie są ograniczenia, co z czym można łączyć a co jest zabronione? Co gorsza, członkowie grup dyskusyjnych podawali sprzeczne "złote" zasady.
Zasada jest prosta: możesz robić wszystko co jest dobre dla modelu.
Wszystkie building blocks DDD są równo traktowane - nie ma ważnych i ważniejszych, mogą korzystać z siebie nawzajem jeżeli wynika to z logiki biznesu.
Dobre wyjaśnienie tego problemu znajdziecie w prezentacji Is Domain-Driven Design More than Entities and Repositories? Tak, tak - sarkastyczny tytuł oznacza, ze J. Nilsson będzie lekko kpił sobie z prymitywnych interpretacji DDD typu JavATE;)
Ale jak uniknąć kodu spaghetti w warstwie modelu domenowego jeżeli zaczniemy w nim łączyć wszystko ze wszystkim? Przecież DDD ma chronić nas przed chaosem a nie sprzyjać jego generowaniu.
Podczas COOLuarów doszliśmy do sformułowania dobrej zasady: Jeżeli z logiki biznesowej wynikają pewne powiązania to nie należy ich unikać. Jeżeli logika biznesowa charakteryzuje się pewnych poziomem złożoności - wynikającej z natury danego procesu, to nie unikniemy powiązań. Ważne aby nie wprowadzać dodatkowej złożoności (przypadkowej).
Dobrą metaforą warstwy logiki aplikacji jest scenariusz dla aktorów, którymi są Building blocks. Albo inaczej: kod logiki aplikacji niejako żongluje klockami biznesowymi. Chodzi o to aby czytać taki scenariusz i "widzieć" czystą logikę.
Więcej na ten temat w końcówce posta Understandability.
Jeżeli teraz jeden z aktorów nie chce pobrudzić się i wywołać np repozytorium to musimy zrobić to wprost w głównym wątku scenariusza (a wynik sztucznie przekazać gdzieś do środka aktora). Powoduje to pojawienie się sztucznych zgrzytów w scenariuszu. I zamiast ułatwiać zrozumienie go - utrudnia.
//=========================
J. Nilsson twierdzi, że jest to typowe pytanie zadawane, przez każdego adepta DDD. Wg mnie taka jest kolej rzeczy - w każdej dziedzinie. Wynika to z modelu kompetencji braci Dreyfus gdzie na początku potrzebujemy dokładnych wytycznych jak zrobić. Później uczymy się, że to zależy od tego co chcemy osiągnąć.
piątek, 16 października 2009
MVC Revisited
Kto w 2009r pisze jeszcze o MVC? Czy można jeszcze coś konstruktywnego dodać w tym temacie?
Spróbuję napisać coś może nie tyle konstruktywnego co porządkującego pojęcia... Doczytajcie do końca a być może dowiedzcie się czegoś co uzupełni wasz zasób ciekawostek służących do robienia wrażenia np na niewiastach podczas imprez;)
MVC - styl to architektoniczny odkryty bodajże jakieś 30 lat temu. Zatem należy mu się najpierw rys historyczny: jak to drzewiej wyglądało w Smalltalku...
Widok - czysty rendering
Kontroler - obsługa zdarzeń urządzeń sprzęgu ludzkiego (taki się kiedyś mówiło) typu klik myszką czy naciśnięcie klawisza
Model - dane do prezentacji i logika nimi manipulująca.
Jak to wygląda dziś? Weźmy na ten przykład 2 popularne technologie prezentacji w Javie:
SWING
Czym są kontrolki w Swingu... Widokiem? Nie do końca, ponieważ delegują rendering do rendererów. Sam np JButton jest reprezentacją jakiegoś bytu na GUI - posiada atrybuty typu położenie, ale rysowaniem zajmują się renderery wchodzące w skład aktualnego look&feel. Ale może nie wnikajmy w implementację i dla uproszczenie niech będzie to widok.
Kontrolki mają również swój wewnętrzny model "prezentacyjny". SetText przecież ustawia "coś" we wnętrzu kontrolki. Zwykle kopiujemy to coś z modelu właściwego budując własny framelet (nano framework) wokół Swinga. Ale sam rendering następuje na podstawie wewnętrznego modelu kontrolki.
Przyjrzyjmy się obsłudze zdarzeń... W Smaltalku zdarzenia techniczne obsługiwał kontroler. W Swingu robi to kontrolka, która deleguje odpowiedzialność do listenerów. Czyli to listenery są kontrolerem? Nie zawsze... w niektórych frameworkach Swingowych implementujemy własne kontrolery wołane przez listenery.
Więc co z tym MVC? Wygląda na to, że to kontrolka jako taka implementuje MVC (patrząc z technicznego punktu widzenia)
JSF
W JSF mamy Managed Beans... Obsługują one zdarzenia oraz decydują o nawigacji - czyli kontroler. Zawierają model do renderingu i zbierania danych z formularzy.
A co będzie, jeżeli zbindujemy jakieś kontrolki graficzne z ich komponentami UIComponent (będącymi polami Managed Beana)? Wówczas MB może sterować atrybutami kontrolek graficznych, czyli mamy logikę prezentacji, czyli widok. Znowu wszystko w jednym.
SZERSZY KONTEKST
Aby tego było mało spójrzmy na listę istniejących reifikacji (koncepcji realizacji) idei MVC zgromadzoną w tym oto poście: Interactive Application Architecture Patterns.
Na liście wiele ciekawych pozycji:
- Model-View-Presenter - w podejściu Microsoft (+ Application Controller) oraz Dolphin
- Supervising Controller
- Passive View Pattern
- Presentation-Abstraction-Control
Sam dodatkowo natknąłem się na:
- Hierarchical MVC (framework Scope, który przez parę miesięcy rozwijałem niezależnie od twórców, którzy porzucili coś tak pięknego;)
- MMVC (rozróżnienie na model prezentacyjny + model domenowy)
Wszystkie wymienione podejścia nazwami nawiązują do MVC, ale nim nie są w formie oryginalnej. Czy to źle? Oczywiście, że nie - MVC w swej oryginalnej formie nie przystaje do konstrukcji współczesnych bibliotek i zakładanego przez nie poziomu abstrakcji. Kto np dziś skupiałby się na szczegółach technicznych obsługi zdarzeń generowanych przez urządzenia.
WARSTWY
MVC jest często mylony z architekturą warstwową. Wystarczy zrobić prosty eksperyment myslowy aby uświadomić sobie błąd: zwizualizujmy sobie oba podejścia:
MVC wygląda jak trójkąt.
Natomiast warstwy są jak Ogry;) Albo raczej jak kanapka, tudzież tort.
MVC może być (i zwykle jest) użyty na szczycie stosu warstw jako wzorzec dla samej warstwy prezentacji. Natomiast pod spodem mamy "zwykłe" warstwy, które nic nie wiedzą o żadnym MVC w prezentacji.
W literaturze spotyka się jednak uproszczone podejścia, gdzie składniki trójkąta MVC utożsamiane są z warstwową architekturą, odpowiednio:
V - warstwa prezentacji
M - warstwa modelu (anemicznego najlpiej)
C - warstwa logiki aplikacji
Mówi się wówczas, że MVC jest traktowany jako wzorzec architektury aplikacji a nie jako wzorzec projektowy prezentacji.
Niech i tak będzie... ;)
Byle tylko nie twierdzić, że widok to HTML + CSS, kontroler to PHP a model to baza danych;P
//=================
Post powstał tak a'propos dzisiejszej prezentacji Marka Richardsa na JDD podczas, której wspomniał on o bezmyślnie przyjmowanych dogmatach:)
Spróbuję napisać coś może nie tyle konstruktywnego co porządkującego pojęcia... Doczytajcie do końca a być może dowiedzcie się czegoś co uzupełni wasz zasób ciekawostek służących do robienia wrażenia np na niewiastach podczas imprez;)
MVC - styl to architektoniczny odkryty bodajże jakieś 30 lat temu. Zatem należy mu się najpierw rys historyczny: jak to drzewiej wyglądało w Smalltalku...
Widok - czysty rendering
Kontroler - obsługa zdarzeń urządzeń sprzęgu ludzkiego (taki się kiedyś mówiło) typu klik myszką czy naciśnięcie klawisza
Model - dane do prezentacji i logika nimi manipulująca.
Jak to wygląda dziś? Weźmy na ten przykład 2 popularne technologie prezentacji w Javie:
SWING
Czym są kontrolki w Swingu... Widokiem? Nie do końca, ponieważ delegują rendering do rendererów. Sam np JButton jest reprezentacją jakiegoś bytu na GUI - posiada atrybuty typu położenie, ale rysowaniem zajmują się renderery wchodzące w skład aktualnego look&feel. Ale może nie wnikajmy w implementację i dla uproszczenie niech będzie to widok.
Kontrolki mają również swój wewnętrzny model "prezentacyjny". SetText przecież ustawia "coś" we wnętrzu kontrolki. Zwykle kopiujemy to coś z modelu właściwego budując własny framelet (nano framework) wokół Swinga. Ale sam rendering następuje na podstawie wewnętrznego modelu kontrolki.
Przyjrzyjmy się obsłudze zdarzeń... W Smaltalku zdarzenia techniczne obsługiwał kontroler. W Swingu robi to kontrolka, która deleguje odpowiedzialność do listenerów. Czyli to listenery są kontrolerem? Nie zawsze... w niektórych frameworkach Swingowych implementujemy własne kontrolery wołane przez listenery.
Więc co z tym MVC? Wygląda na to, że to kontrolka jako taka implementuje MVC (patrząc z technicznego punktu widzenia)
JSF
W JSF mamy Managed Beans... Obsługują one zdarzenia oraz decydują o nawigacji - czyli kontroler. Zawierają model do renderingu i zbierania danych z formularzy.
A co będzie, jeżeli zbindujemy jakieś kontrolki graficzne z ich komponentami UIComponent (będącymi polami Managed Beana)? Wówczas MB może sterować atrybutami kontrolek graficznych, czyli mamy logikę prezentacji, czyli widok. Znowu wszystko w jednym.
SZERSZY KONTEKST
Aby tego było mało spójrzmy na listę istniejących reifikacji (koncepcji realizacji) idei MVC zgromadzoną w tym oto poście: Interactive Application Architecture Patterns.
Na liście wiele ciekawych pozycji:
- Model-View-Presenter - w podejściu Microsoft (+ Application Controller) oraz Dolphin
- Supervising Controller
- Passive View Pattern
- Presentation-Abstraction-Control
Sam dodatkowo natknąłem się na:
- Hierarchical MVC (framework Scope, który przez parę miesięcy rozwijałem niezależnie od twórców, którzy porzucili coś tak pięknego;)
- MMVC (rozróżnienie na model prezentacyjny + model domenowy)
Wszystkie wymienione podejścia nazwami nawiązują do MVC, ale nim nie są w formie oryginalnej. Czy to źle? Oczywiście, że nie - MVC w swej oryginalnej formie nie przystaje do konstrukcji współczesnych bibliotek i zakładanego przez nie poziomu abstrakcji. Kto np dziś skupiałby się na szczegółach technicznych obsługi zdarzeń generowanych przez urządzenia.
WARSTWY
MVC jest często mylony z architekturą warstwową. Wystarczy zrobić prosty eksperyment myslowy aby uświadomić sobie błąd: zwizualizujmy sobie oba podejścia:
MVC wygląda jak trójkąt.
Natomiast warstwy są jak Ogry;) Albo raczej jak kanapka, tudzież tort.
MVC może być (i zwykle jest) użyty na szczycie stosu warstw jako wzorzec dla samej warstwy prezentacji. Natomiast pod spodem mamy "zwykłe" warstwy, które nic nie wiedzą o żadnym MVC w prezentacji.
W literaturze spotyka się jednak uproszczone podejścia, gdzie składniki trójkąta MVC utożsamiane są z warstwową architekturą, odpowiednio:
V - warstwa prezentacji
M - warstwa modelu (anemicznego najlpiej)
C - warstwa logiki aplikacji
Mówi się wówczas, że MVC jest traktowany jako wzorzec architektury aplikacji a nie jako wzorzec projektowy prezentacji.
Niech i tak będzie... ;)
Byle tylko nie twierdzić, że widok to HTML + CSS, kontroler to PHP a model to baza danych;P
//=================
Post powstał tak a'propos dzisiejszej prezentacji Marka Richardsa na JDD podczas, której wspomniał on o bezmyślnie przyjmowanych dogmatach:)
wtorek, 6 października 2009
Domain Specific Query
Zgodnie z obowiązującym trendem polegającym na prześciganiu się w publikowaniu bardziej lub jeszcze_bardziej oczywistych odkryć - dziś kolej na mnie i na coś na co wpadłem już jakiś czas temu.
Tytułowy wynalazek ma roboczą nazwę DSQ, którą triumfalnie nadałem mu kojarząc dwie modne techniki: Domain Specific Language i Command-query Separation.
NIEKRÓTKIE WYJAŚNIENIE
O CQS pisałem 2 miesiące temu w >tym< poście. W skrócie jest to styl architektoniczny, który zakłada separację API systemu na dwie wyraźne części. Jedna obsługuje (przyjmuje wysyłane do API) Command - rozkazy wykonania operacji. Druga natomiast obsługuje Query - obiekty specyfikujące kryteria pobierania/wyszukiwania danych.
W dzisiejszym poście skupimy się właśnie na tej drugiej części, czyli ogólnie mówiąc na wyszukiwarkach danych. Wyszukiwarki są dosyć proste - ot zwykłe servisy, pod którymi nie znajdują się już żadne kolejne warstwy - chyba, że potrzebujemy jeszcze "dodać coś od siebie" do pobranych z pewnego źródła danych.
DSL to koncepcja dużo bardziej popularna (w sensie ilości publikacji) niż CQS, więc nie będę się zbytnio rozpisywał na jej temat. Ogólnie rzecz ujmując chodzi o to aby kod logiki biznesowej czytać niemal jak język naturalny. Definiujemy sobie język zorientowany na konkretną domenę biznesową a później to już fraszka - program piszę się niemal sam;)
W statycznych językach typu Java nie mamy zbyt szerokiego pola do popisu jeżeli chodzi o zdefiniowanie własnego języka domenowego.
Jedyne co możemy zrobić w Javie to zastosować Fluent Interface i opisowe (samoopisujące wręcz) nazwy metod. Czyli jest to jedynie namiastka DSL, ale cieszy:)
Koncepcja Fluent Interface jest prosta: chodzi o to aby metody danej klasy zwracały instancję obiektu na rzecz którego zostały wykonane. Dzięki temu możemy zamiast pisać tradycyjnie:
możemy zmienić styl na bardziej fluent:
Jeżeli teraz zamiast nadawać metodom autystyczne nazwy (w stylu Bruce Eckela), wykażemy się odrobiną inwencji i słowiańskiej fantazji możemy pisać niemalże poetycko:
Chyba lepiej czyta się kod w takim stylu...
DO RZECZY
Po tym przydługim wstępie powinniśmy już mniej więcej czuć czym jest CQS i DSL...
Wyobraźmy sobie następujący problem :
W systemie przechowujemy dokumenty. Dokumenty mają szereg atrybutów: kilka typów statusów (związanych z przepływem dok. jak i technicznych), różnego rodzaju daty, przypisanie do różnych zasobów itp.
Domena biznesowa standardowa i może niezbyt wyszukana, ale dzięki temu wiadomo o co chodzi i możemy skupić się na samej technice.
Serwis, który miałby wyszukiwać dokumenty musiałby mieć metodę o kilkunastu parametrach. Pierwszym problem z taką metodą to wysoki ujemny współczynnik czytalności. Drugi problem to ciągłe zmiany sygnatury metody gdy pojawiają się nowe atrybuty dokumentu.
Zatem dosyć oczywiste jest, że stworzymy sobie klasę przechowującą atrybuty wyszukiwania a nasz serwis będzie miał metodę, do której przekazujemy paczkę kryteriów - po prostu jakiś rodzaj Query z CQS:
Jak do tej pory standard. (Query najpewniej będzie prędzej czy później serializowany.)
Teraz idąc dalej wg utartych szablonów dodalibyśmy do SearchDocumentsQuery kilkanaście pół prywatnych oraz oczywiście nasze ulubione gettery i settery.
Kod klasy klienckiej przy pomocy setterów ustawia kryteria wyszukiwania a kod metody DocumentsFinder.search przy pomocy getterów pobiera te parametry i klei sobie zapytanie w SQL, HQL tudzież mój ulubiony Hibernate Criteria API.
A gdzie miejsce dla DSL? No więc właśnie przy tak ordynarnym podejściu nie uświadczymy.
Załóżmy teraz, że chcemy hermetyzować kod klas klienckich (wywołujących DocumentsFinder)od szczegółów atrybutów Dokumentu.
Klient zamiast operować szczegółami:
Wyraża jedynie intencje co do chęci otrzymania dokumentów spełniających zestaw określonych specyfikacji biznesowych:
Gdzie przykładowo metody DSL wyglądają mniej więcej tak:
Jak interpretować powyższe metody ustawiające kryteria:
current - oznacza, ze życzymy sobie dokumentów będących aktualnie w obiegu.
Co to znaczy od strony modelu: że maja określony status i że wygasają np najwcześniej jutro.
contains - oznacza, że dokument ma zawierać określoną frazę. Z punktu widzenia modelu oznacza to, że tytuł lub treść dokumentu zawiera wszystkie słowa frazy.
Cała idea polega na tym, że klient nie wie nic o polach status, expiryDate, words, ...
Klient wyraża intencję.
Natomiast pola te są dostępne przez gettery dla DocumentsFinder, który to analizuje je i odpowiedzi składa zapytanie. Aby osiągnąć pożądaną widoczność getterów należałoby zdefiniować SearchDocumentsQuery jako klasę statyczną wewnętrzną w DocumentsFinder. Wówczas DocumentsFinder będzie widział metody prywatne, bo takie powinny być rozważane gettery. Statyczną ponieważ chcemy tworzyć Query bez "wywodzenia" jej z instancji Findera.
Kuszące może wydawać się dodanie do SearchDocumentsQuery metody buildEecutable(), która produkuje na podstawie stanu swoich wewnętrznych pól (statys, words,...) coś co można wykonać. Tym "czymś" może być PreparedStatement z JDBC, Criteria z Criteria API Hibenrate, Query z JPA, czy zwykły String zawierający SQL albo HQL.
Ale zastanówmy się: czy klasa, która jest widoczna przez klienta (np zdalnego) powinna mieć w sobie kod, który operuje na jakimś API np Hibernate (Criteria API) albo czy powinna wiedzieć coś o jakimś SQL? Przecież Finder może szukać w XMLu lub poprzez web service. Klient nie może tego wiedzieć - ba nie powinien.
//====================
Pomysł może mało odkrywczy i zapewne wiele osób wpadło na niego równolegle, ale jeszcze nie spotkałem nigdzie opisu.
Pomysł oczywiście nie będzie miał zastosowania w aplikacjach z małą abstrakcją pomiędzy bazą danych a GUI - tak zwanych przeglądarkach danych. W tego typu aplikacjach kontrolki na GUI (w tym pola filtrujące) odpowiadają zwykle niemal dosłownie kolumnom w bazie. W tego typu przypadkach próba abstrahowania od parametrów modelu jest niepotrzebnym uprawianiem dyscypliny pod tytułem nakładanie gaci przez głowę;)
Czekam na opinie, sugestie, krytykę - może być niekonstruktywna:)
Tytułowy wynalazek ma roboczą nazwę DSQ, którą triumfalnie nadałem mu kojarząc dwie modne techniki: Domain Specific Language i Command-query Separation.
NIEKRÓTKIE WYJAŚNIENIE
O CQS pisałem 2 miesiące temu w >tym< poście. W skrócie jest to styl architektoniczny, który zakłada separację API systemu na dwie wyraźne części. Jedna obsługuje (przyjmuje wysyłane do API) Command - rozkazy wykonania operacji. Druga natomiast obsługuje Query - obiekty specyfikujące kryteria pobierania/wyszukiwania danych.
W dzisiejszym poście skupimy się właśnie na tej drugiej części, czyli ogólnie mówiąc na wyszukiwarkach danych. Wyszukiwarki są dosyć proste - ot zwykłe servisy, pod którymi nie znajdują się już żadne kolejne warstwy - chyba, że potrzebujemy jeszcze "dodać coś od siebie" do pobranych z pewnego źródła danych.
DSL to koncepcja dużo bardziej popularna (w sensie ilości publikacji) niż CQS, więc nie będę się zbytnio rozpisywał na jej temat. Ogólnie rzecz ujmując chodzi o to aby kod logiki biznesowej czytać niemal jak język naturalny. Definiujemy sobie język zorientowany na konkretną domenę biznesową a później to już fraszka - program piszę się niemal sam;)
W statycznych językach typu Java nie mamy zbyt szerokiego pola do popisu jeżeli chodzi o zdefiniowanie własnego języka domenowego.
Jedyne co możemy zrobić w Javie to zastosować Fluent Interface i opisowe (samoopisujące wręcz) nazwy metod. Czyli jest to jedynie namiastka DSL, ale cieszy:)
Koncepcja Fluent Interface jest prosta: chodzi o to aby metody danej klasy zwracały instancję obiektu na rzecz którego zostały wykonane. Dzięki temu możemy zamiast pisać tradycyjnie:
Klasa obiekt = new Klasa();
obiekt.metoda1();
obiekt.metoda2();
obiekt.metoda3();
możemy zmienić styl na bardziej fluent:
Klasa obiekt = new Klasa();
obiekt.metoda1().metoda2().metoda3();
Jeżeli teraz zamiast nadawać metodom autystyczne nazwy (w stylu Bruce Eckela), wykażemy się odrobiną inwencji i słowiańskiej fantazji możemy pisać niemalże poetycko:
FabrykaWierszy.stworzUtwor().subtelny().zNutkaDekadencji().rymyNiechBedaCzestochowskie().strof(10);
Chyba lepiej czyta się kod w takim stylu...
DO RZECZY
Po tym przydługim wstępie powinniśmy już mniej więcej czuć czym jest CQS i DSL...
Wyobraźmy sobie następujący problem :
W systemie przechowujemy dokumenty. Dokumenty mają szereg atrybutów: kilka typów statusów (związanych z przepływem dok. jak i technicznych), różnego rodzaju daty, przypisanie do różnych zasobów itp.
Domena biznesowa standardowa i może niezbyt wyszukana, ale dzięki temu wiadomo o co chodzi i możemy skupić się na samej technice.
Serwis, który miałby wyszukiwać dokumenty musiałby mieć metodę o kilkunastu parametrach. Pierwszym problem z taką metodą to wysoki ujemny współczynnik czytalności. Drugi problem to ciągłe zmiany sygnatury metody gdy pojawiają się nowe atrybuty dokumentu.
Zatem dosyć oczywiste jest, że stworzymy sobie klasę przechowującą atrybuty wyszukiwania a nasz serwis będzie miał metodę, do której przekazujemy paczkę kryteriów - po prostu jakiś rodzaj Query z CQS:
public class SearchDocumentsQuery implements Serializable{
//atrybuty wyszukiwania + get/set
}
public class DocumentsFinder{
public Collection<Document> search(SearchDocumentsQuery criteria){
//..
}
Jak do tej pory standard. (Query najpewniej będzie prędzej czy później serializowany.)
Teraz idąc dalej wg utartych szablonów dodalibyśmy do SearchDocumentsQuery kilkanaście pół prywatnych oraz oczywiście nasze ulubione gettery i settery.
Kod klasy klienckiej przy pomocy setterów ustawia kryteria wyszukiwania a kod metody DocumentsFinder.search przy pomocy getterów pobiera te parametry i klei sobie zapytanie w SQL, HQL tudzież mój ulubiony Hibernate Criteria API.
A gdzie miejsce dla DSL? No więc właśnie przy tak ordynarnym podejściu nie uświadczymy.
Załóżmy teraz, że chcemy hermetyzować kod klas klienckich (wywołujących DocumentsFinder)od szczegółów atrybutów Dokumentu.
Klient zamiast operować szczegółami:
SearchDocumentsQuery criteria = new SearchDocumentsQuery();
criteria.setStatus(Status.ACTIVE);
criteria.setCreateDate(...)
criteria.set...
...
Collection<Document> result = documentsFinder.search(criteria);
Wyraża jedynie intencje co do chęci otrzymania dokumentów spełniających zestaw określonych specyfikacji biznesowych:
SearchDocumentsQuery criteria = new SearchDocumentsQuery();
criteria.current().contains("lorem ipsum");
Collection<Document> result = documentsFinder.search(criteria);
Gdzie przykładowo metody DSL wyglądają mniej więcej tak:
public class SearchDocumentsQuery implements Serializable{
private Status status;
private Date epiryDate;
private String[] titleWords;
private String[] contentWords;
public SearchDocumentsQuery current(){
status = Status.ACTIVE;
expiryDate = //data jutrzejsza
return this;
}
public SearchDocumentsQuery contains(String phrase){
String[] words = phrase.split(" ");//lub bardziej wyrafinowy split
titleWords = words;
contentWords = words;
return this;
}
}
Jak interpretować powyższe metody ustawiające kryteria:
current - oznacza, ze życzymy sobie dokumentów będących aktualnie w obiegu.
Co to znaczy od strony modelu: że maja określony status i że wygasają np najwcześniej jutro.
contains - oznacza, że dokument ma zawierać określoną frazę. Z punktu widzenia modelu oznacza to, że tytuł lub treść dokumentu zawiera wszystkie słowa frazy.
Cała idea polega na tym, że klient nie wie nic o polach status, expiryDate, words, ...
Klient wyraża intencję.
Natomiast pola te są dostępne przez gettery dla DocumentsFinder, który to analizuje je i odpowiedzi składa zapytanie. Aby osiągnąć pożądaną widoczność getterów należałoby zdefiniować SearchDocumentsQuery jako klasę statyczną wewnętrzną w DocumentsFinder. Wówczas DocumentsFinder będzie widział metody prywatne, bo takie powinny być rozważane gettery. Statyczną ponieważ chcemy tworzyć Query bez "wywodzenia" jej z instancji Findera.
public class DocumentsFinder{
public static class SearchDocumentsQuery implements Serializable{
private Status status;
private Date epiryDate;
private String[] titleWords;
private String[] contentWords;
//metoda contains i current - pominięto
private Status getStatus(){
return status;
}
}
public Collection<Document> search(SearchDocumentsQuery criteria){
if (criteria.getStatus() != null)//wywołanie prywatnej metody
//dołożenie do zapytania warunku na status
...
}
}
Kuszące może wydawać się dodanie do SearchDocumentsQuery metody buildEecutable(), która produkuje na podstawie stanu swoich wewnętrznych pól (statys, words,...) coś co można wykonać. Tym "czymś" może być PreparedStatement z JDBC, Criteria z Criteria API Hibenrate, Query z JPA, czy zwykły String zawierający SQL albo HQL.
Ale zastanówmy się: czy klasa, która jest widoczna przez klienta (np zdalnego) powinna mieć w sobie kod, który operuje na jakimś API np Hibernate (Criteria API) albo czy powinna wiedzieć coś o jakimś SQL? Przecież Finder może szukać w XMLu lub poprzez web service. Klient nie może tego wiedzieć - ba nie powinien.
//====================
Pomysł może mało odkrywczy i zapewne wiele osób wpadło na niego równolegle, ale jeszcze nie spotkałem nigdzie opisu.
Pomysł oczywiście nie będzie miał zastosowania w aplikacjach z małą abstrakcją pomiędzy bazą danych a GUI - tak zwanych przeglądarkach danych. W tego typu aplikacjach kontrolki na GUI (w tym pola filtrujące) odpowiadają zwykle niemal dosłownie kolumnom w bazie. W tego typu przypadkach próba abstrahowania od parametrów modelu jest niepotrzebnym uprawianiem dyscypliny pod tytułem nakładanie gaci przez głowę;)
Czekam na opinie, sugestie, krytykę - może być niekonstruktywna:)
Subskrybuj:
Posty (Atom)
