Professional Documents
Culture Documents
Evans E. - Domain-Driven Design. Zapanuj nad złożonym systemem informatycznym
Evans E. - Domain-Driven Design. Zapanuj nad złożonym systemem informatycznym
ISBN: 978-83-283-0528-1
Przedmowa ..................................................................................... 15
Wstęp ............................................................................................. 17
Podziękowania . ............................................................................... 27
Część I
Zastosowanie modelu dziedziny . .........................29
Rozdział 1. Przetwarzanie wiedzy . ................................................. 35
Elementy wydajnego modelowania ................................................40
Przetwarzanie wiedzy ......................................................................41
Ciągła nauka . ...................................................................................44
Projekt bogaty w wiedzę .................................................................45
Modele dogłębne . ...........................................................................48
7
Część II
Elementy składowe projektu
sterowanego modelem .......................................... 89
Rozdział 4. Wyizolowanie dziedziny ...............................................93
Architektura warstwowa . ............................................................... 94
Powiązanie warstw ................................................................... 99
Szkielety architektury . ............................................................. 100
To w warstwie dziedziny żyje model . ......................................... 101
Antywzorzec inteligentnego interfejsu użytkownika . ................ 102
Inne rodzaje izolacji . .................................................................... 106
8 SPIS TREŚCI
Gdzie mieści się logika niezmienników? ....................................174
FABRYKI ENCJI a FABRYKI WARTOŚCI ......................174
Odtwarzanie zachowanych obiektów .........................................175
REPOZYTORIA . .........................................................................178
Odpytywanie REPOZYTORIUM . .......................................184
Kod klienta, w przeciwieństwie do programistów,
ignoruje implementację REPOZYTORIUM .........................185
Implementacja REPOZYTORIUM ........................................186
Praca ze szkieletami architektury . .............................................188
Relacje z FABRYKAMI . ........................................................189
Projektowanie obiektów dla relacyjnych baz danych ..................190
SPIS TREŚCI 9
Część III
Refaktoryzacja ku głębszemu zrozumieniu ....... 223
Rozdział 8. Moment przełomowy ...................................................229
Historia pewnego przełomu . ....................................................... 230
Przyzwoity model, lecz wciąż... . .............................................. 230
Moment przełomowy ............................................................... 231
Model pogłębiony .................................................................... 233
Otrzeźwiająca decyzja . ............................................................ 236
Zapłata .................................................................................. 237
Możliwości . .................................................................................. 237
Koncentracja na podstawach . ....................................................... 237
Epilog — potok nowych spostrzeżeń . ......................................... 238
10 SPIS TREŚCI
Kierunki ataku . ..............................................................................321
Definiowanie poddziedzin . ......................................................321
W miarę możliwości polegaj na ustalonym formalizmie ...............322
Część IV
Projekt strategiczny . ............................................369
Rozdział 14. Utrzymywanie integralności modelu . ......................... 373
KONTEKST ZWIĄZANY ..........................................................377
Rozpoznawanie odprysków pojęciowych
w KONTEKŚCIE ZWIĄZANYM ...................................381
CIĄGŁA INTEGRACJA ..............................................................383
MAPA KONTEKSTÓW .............................................................386
Testowanie na granicach KONTEKSTU ................................393
Organizacja oraz dokumentacja MAP KONTEKSTÓW ........394
Relacje pomiędzy KONTEKSTAMI ZWIĄZANYMI ..............395
JĄDRO WSPÓŁDZIELONE ......................................................396
ZESPOŁY PROGRAMISTYCZNE
KLIENTA – DOSTAWCY ........................................................398
KONFORMISTA .........................................................................403
WARSTWA ZAPOBIEGAJĄCA USZKODZENIU ................406
Projektowanie interfejsu WARSTWY ZAPOBIEGAJĄCEJ
USZKODZENIU . ...........................................................408
Implementacja WARSTWY ZAPOBIEGAJĄCEJ
USZKODZENIU . ...........................................................408
Opowieść ku przestrodze . .........................................................412
SPIS TREŚCI 11
ODDZIELNE DROGI . .............................................................. 414
USŁUGA OTWARTEGO GOSPODARZA . .......................... 417
JĘZYK OPUBLIKOWANY . ....................................................... 419
Unifikacja słonia . .......................................................................... 422
Wybór strategii kontekstu modelu . ............................................. 426
Decyzja zespołowa lub wyższa . ............................................... 426
Stawianie siebie w kontekście . .................................................. 427
Przekształcanie granic . ............................................................ 427
Akceptacja tego, czego nie możemy zmienić
— wyznaczanie zewnętrznych systemów . .............................. 428
Relacje z systemami zewnętrznymi . .......................................... 429
System w projektowaniu . ......................................................... 430
Dostosowanie do specjalnych potrzeb przy użyciu
odrębnych modeli .................................................................. 431
Wdrożenie .............................................................................. 432
Kompromis ............................................................................. 433
Kiedy projekt już trwa . ............................................................ 433
Transformacje .
......... 434 Łączenie KONTEKSTÓW — ODDZIELNE DROGI →
JĄDRO WSPÓŁDZIELONE . ........................................ 434
Łączenie KONTEKSTÓW — JĄDRO
WSPÓŁDZIELONE → CIĄGŁA INTEGRACJA . ..... 437
Wygaszanie starego systemu . .................................................... 438
USŁUGA OTWARTEGO GOSPODARZA →
JĘZYK OPUBLIKOWANY . ........................................... 440
12 SPIS TREŚCI
SPÓJNE MECHANIZMY ..........................................................469
OGÓLNE PODDZIEDZINY
a SPÓJNE MECHANIZMY . ..........................................471
Kiedy MECHANIZM jest częścią
DZIEDZINY GŁÓWNEJ . ..............................................472
Destylacja do stylu deklaratywnego ..............................................473
RDZEŃ ODDZIELONY ............................................................474
Koszt utworzenia RDZENIA ODDZIELONEGO ..............475
Rozwijanie decyzji zespołowych . ..............................................476
RDZEŃ ABSTRAKCYJNY .........................................................481
Głęboka destylacja modelu ...........................................................482
Wybór celów refaktoryzacji ...........................................................483
SPIS TREŚCI 13
Zakończenie ........................................................ 545
Dodatek. Wykorzystanie szablonów z tej książki...............................553
Słownik . .......................................................................................559
Bibliografia . ...................................................................................565
Prawa do zdjęć . ..............................................................................567
Skorowidz . ....................................................................................569
14 SPIS TREŚCI
PRZEDMOWA
15
również napisać z programistą kod w języku Java. Jest to po części prawdą,
ponieważ nie można stworzyć przydatnego modelu koncepcyjnego bez
wzięcia pod uwagę również problemów implementacyjnych. Niemniej
jednak podstawowym powodem, dla którego koncept oraz jego implemen-
tacja przynależą do siebie, jest fakt, iż najważniejszą wartością modelu
dziedziny jest udostępnienie języka wszechobecnego, łączącego ze sobą eks-
pertów dziedzinowych z osobami technicznymi.
Kolejną nauką, jaką wyniesiesz z lektury tej książki, będzie wiedza,
że modele dziedziny nie są najpierw całkowicie modelowane, a następnie
implementowane. Podobnie jak wielu ludzi, dojrzewałem stopniowo do
odrzucenia myślenia w sposób „zaprojektuj, a następnie zbuduj”. Z doświad-
czenia Erica wynika, że najlepsze modele dziedzin ewoluują w czasie i ich
nawet najbardziej doświadczeni twórcy odkrywają, że najtrafniejsze pomy-
sły przychodzą im do głowy dopiero po wdrożeniu początkowych wersji
systemu.
Sądzę i ufam, że ta książka będzie mieć duży wpływ na czytelnika.
Pomoże ona nadać kształt i spójność bardzo niesprecyzowanej dziedzinie
oraz nauczy wielu ludzi, w jaki sposób używać licznych cennych narzędzi.
Modele dziedzinowe mogą mieć duży wpływ na nadzorowanie tworze-
nia oprogramowania — niezależnie od tego, w jakim języku oraz środo-
wisku zostaną zaimplementowane.
I na koniec ostatnia, chociaż istotna myśl. Jedną z rzeczy, które cenię
w tej książce najbardziej, jest fakt, że Eric nie boi się opisywać chwil, w któ-
rych doznał niepowodzenia. Większość autorów lubi trzymać czytelnika
w przekonaniu o swojej wszechmocy. Eric stawia sprawę jasno, że tak
jak większość z nas poznał smak zarówno sukcesów, jak i rozczarowań.
Ważne jest, aby wyciągnąć naukę z obu, a co ważniejsze — Eric może
nam ją teraz przekazać.
Martin Fowler
Kwiecień 2003 r.
16 PRZEDMOWA
WSTĘP
1
ang. domain-driven design — przyp. tłum.
17
Przyglądałem się na przykład jednemu z projektów, który szybko
osiągnął fazę produkcyjną, dostarczając przydatny i prosty system handlu
elektronicznego. Deweloperzy wykorzystywali swoją intuicję, ale to nie
przeszkodziło im w osiągnięciu celu, ponieważ proste programy mogą być
tworzone bez zwracania zbytniej uwagi na formalny projekt. Konsekwen-
cją tego początkowego sukcesu były rosnące niebotycznie oczekiwania co
do kolejnych wdrożeń programu. I to właśnie wtedy zostałem poproszony
o pomoc w realizacji jego drugiej wersji. Po dokładnym przyjrzeniu się
zauważyłem, że brakuje w tym projekcie modelu dziedziny, a nawet
wspólnego języka, co skutkowało chaosem i brakiem porządku. Osoby
zarządzające projektem nie zgodziły się z moją oceną, a ja nie przyjąłem
tej posady. Rok później cały zespół ugrzązł na dobre i nie był w stanie
dostarczyć drugiej wersji produktu. I chociaż członkowie zespołu wcale
nie używali technologii we wzorowy sposób, to jednak nie to było przy-
czyną porażki — pokonała ich logika biznesowa. W konsekwencji pierwsza
wersja ich systemu przedwcześnie zmieniła się w przestarzałe oprogra-
mowanie o wysokim koszcie utrzymania.
Zwiększanie skali złożoności wymaga poważniejszego podejścia do
projektowania logiki dziedziny. W początkowym okresie mojej kariery
miałem szczęście uczestniczyć w projektach, które zwracały szczególną
uwagę na projektowanie dziedziny. Podobny projekt w dziedzinie przy-
najmniej tak złożonej, jak poprzednio opisany, również rozpoczął się
początkowo szczęśliwie, dostarczając prostą aplikację dla osób zawodowo
zajmujących się handlem. Jednak w tym przypadku po początkowym
wdrożeniu nastąpiło przyspieszenie pracy. Każda nowa wersja otwierała
nowe pasjonujące możliwości integracji oraz rozszerzenia funkcjonalności
poprzednich. Zespół projektowy był w stanie w sposób elastyczny odpo-
wiedzieć na potrzeby użytkowników, rozszerzając tym samym funkcjonal-
ność aplikacji. Ten pozytywny trend miał bezpośredni początek w dogłęb-
nym modelu dziedziny, który był dalej udoskonalany i odzwierciedlany
w kodzie. Gdy tylko zespół odkrywał coś nowego w dziedzinie, jej model
był zmieniany. Poprawie uległa jakość komunikacji nie tylko pomiędzy
samymi deweloperami, lecz także pomiędzy nimi a ekspertami dziedzi-
nowymi. Sam zaś projekt daleki był od powodowania coraz większych
ograniczeń utrzymaniowych. Wręcz przeciwnie, stał się łatwiejszy do
modyfikacji oraz dalszego rozszerzania.
Niestety, projekty nie kończą się w taki mistrzowski sposób tylko
dlatego, że w sposób poważny traktują modelowanie. Jeden z moich
poprzednich projektów rozpoczął się od aspiracji stworzenia na podsta-
wie modelu dziedziny globalnego systemu przedsiębiorstwa. Jednakże po
latach rozczarowań zespół obniżył swoje oczekiwania i wrócił do rzeczy-
18 WSTĘP
wistości. Sam zespół miał dobre narzędzia i dobrze rozumiał biznes, a także
przykładał dużą wagę do modelowania, jednak fatalnie wybrany sposób
rozdzielenia ról deweloperskich odłączył modelowanie od implementacji.
W ten sposób projekt nie odzwierciedlał ciągłej i dogłębnej analizy. W każ-
dym razie projekt szczegółowych obiektów biznesowych nie był na tyle
rygorystyczny, aby wspierać ich zastosowanie w złożonych aplikacjach.
Powtarzające się iteracje nie spowodowały polepszenia kodu z powodu
niejednolitych umiejętności wśród programistów, niemających pojęcia
o nieformalnych konwencjach oraz technikach tworzenia obiektów opar-
tych na modelach, które mogłyby również funkcjonować w rzeczywistym,
działającym programie. Wraz z upływem czasu zespół uwikłał się w szcze-
góły złożoności i stracił swą spójną wizję systemu. Po latach wysiłku
projekt zdołał wyprodukować niewielkie przydatne oprogramowanie, jed-
nak zespół porzucił swoje ambicje, podobnie jak zaniechał przykładania
wagi do modelowania.
Wyzwanie złożoności
Wiele rzeczy może spowodować niepowodzenie projektu. Można choćby
wymienić biurokrację, niejasne oczekiwania oraz brak zasobów. Jednak
to podejście do projektowania w dużym stopniu określa to, jak złożone
może stać się oprogramowanie. Gdy złożoność wymyka się spod kontroli,
deweloperzy nie mogą już dłużej na tyle dobrze zrozumieć programu, aby
go w prosty i bezpieczny sposób zmienić lub rozszerzyć. Z drugiej strony,
dobry projekt może stworzyć dodatkowe okazje do wykorzystania tych
złożonych funkcjonalności.
Niektóre elementy projektowania związane są z technologią. Dużo
wysiłku na przykład włożono w opracowanie projektów sieci kompute-
rowych, baz danych oraz innych zagadnień technicznych związanych
z oprogramowaniem. Napisano też wiele książek o tym, jak rozwiązywać
problemy związane z tymi dziedzinami. Całe zastępy deweloperów roz-
wijały swoje umiejętności, śledząc każdą nowinkę techniczną.
Wciąż jednak największa złożoność wielu aplikacji nie jest wcale
związana z zagadnieniami technicznymi. Jest nią zazwyczaj sama dziedzina
i działalność lub rodzaj biznesu użytkownika. Jeżeli ta złożoność dzie-
dzinowa nie zostanie obsłużona w projekcie, nie będzie już mieć zna-
czenia, czy technologia zostanie później wykorzystana we właściwy spo-
sób. Poprawny projekt musi stale uwzględniać ten najważniejszy aspekt
oprogramowania.
WSTĘP 19
Książka ta niesie ze sobą dwa przesłania:
1. W przypadku większości projektów informatycznych podstawowy
nacisk położony powinien być na dziedzinę oraz jej logikę.
2. Projekty złożonych dziedzin powinny opierać się na modelu.
Projektowanie sterowane dziedziną jest zarówno sposobem myśle-
nia, jak i zestawem priorytetów mających na celu przyspieszenie tworzenia
takich projektów informatycznych, które mają do czynienia ze złożonymi
dziedzinami. Dlatego też w książce tej zaprezentowany został rozległy
zestaw praktyk, technik oraz pryncypiów projektowych.
2
ang. agile development process — przyp. tłum.
20 WSTĘP
1. Proces tworzenia jest iteracyjny. Ten sposób tworzenia oprogramowania
jest propagowany i stosowany od dekad i stanowi kamień węgielny
dla zwinnych metod programowania. W literaturze dostępnych jest
wiele dobrych pozycji opisujących zwinne metody oraz programowa-
nie ekstremalne3, np. Surviving Object-Oriented Projects (Cockburn 1998)
i Extreme Programming Explained: Embrace Change (Beck 1999).
2. Istnieje bliska relacja pomiędzy deweloperami oraz ekspertami dziedzinowymi.
Projektowanie sterowane dziedziną wkłada do modelu dużą ilość
wiedzy odzwierciedlającej dziedzinę i koncentrującej się na jej głów-
nych zagadnieniach. Jest to efekt współpracy pomiędzy tymi, którzy
znają dziedzinę, a tymi, którzy wiedzą, jak stworzyć oprogramowanie.
Ponieważ proces tworzenia jest iteracyjny, ta współpraca musi trwać
przez cały czas życia projektu.
Programowanie ekstremalne stworzone przez Kenta Becka, Warda
Cunninghama oraz pozostałych (zobacz Extreme Programming Explained:
Embrace Change [Beck 1999]) jest czołowym ze zwinnych procesów, a także
tym, z którym najwięcej pracowałem. Właśnie programowania ekstremal-
nego będę używać w tej książce, aby skonkretyzować swoje wyjaśnienia,
a także jako punktu wyjścia do dyskusji na temat współdziałania projektu
i procesu. Wszystkie zaprezentowane zasady są również w łatwy sposób
adaptowalne do innych zwinnych procesów.
3
ang. extreme programming lub w skrócie XP — przyp. tłum.
WSTĘP 21
Tego rodzaju minimalistyczne podejście okazało się bardzo potrzeb-
nym antidotum na niektóre z wybryków entuzjastów projektowania.
Projekty były spowalniane przez zawiłe dokumenty, niewnoszące zbyt
dużo wartości. Co więcej, cierpiały one również z powodu „paraliżu anali-
tycznego”, powodowanego przez członków zespołu, zbyt przerażonych
możliwością utworzenia niedoskonałego projektu, aby dokonać jakiego-
kolwiek postępu. Dlatego coś musiało ulec zmianie.
Niestety, niektóre z tych pomysłów na usprawnienie procesów mogą
być błędnie interpretowane. Każda osoba bowiem ma inną definicję tego,
co jest „najprostsze”. Ciągła refaktoryzacja kodu stanowi serię małych
zmian projektowych. Deweloperzy bez solidnych podstaw projektowania
stworzą w rezultacie kod, który będzie trudny do zrozumienia oraz mody-
fikacji. Stanowić to będzie zupełne przeciwieństwo elastyczności i zwin-
ności. I chociaż obawa przed nieprzewidzianymi wymaganiami prowadzi
często do nadmiarowej pracy, to jednak próba jej ominięcia może doprowa-
dzić do kolejnej obawy przed wykonywaniem jakiegokolwiek dogłębnego
projektu.
W rzeczywistości metody programowania ekstremalnego działają
najlepiej w przypadku deweloperów z jasnym wyczuciem projektowym.
Proces XP zakłada możliwość częstego i szybkiego usprawniania projektu
poprzez refaktoring. Jednak wcześniejsze wybory projektowe mogą uczynić
tę czynność zarówno łatwiejszą, jak i trudniejszą. Proces XP ma również
na celu usprawnienie komunikacji w zespole, jednak wybory dotyczące
modelu oraz projektu mogą tę komunikację uprościć lub zagmatwać.
W tej książce projektowanie oraz implementacja przeplatają się wza-
jemnie, ukazując, w jaki sposób projektowanie sterowane dziedziną oraz
zwinne metodologie mogą się wspierać. Zaawansowane podejście do mode-
lowania dziedzinowego w kontekście stosowania zwinnych metodologii
programistycznych spowoduje przyspieszenie fazy tworzenia kodu. Powią-
zanie procesu z rozwojem dziedziny czyni ten rodzaj podejścia bardziej
praktycznym od jakiegokolwiek innego hermetycznego akademickiego
traktowania fazy projektowania.
Struktura książki
Niniejsza książka została podzielona na cztery główne części:
Część I, „Zastosowanie modelu dziedziny”, przedstawia podstawowe
cele stosowania programowania sterowanego dziedziną. Będą one
następnie wpływać na praktyczne przykłady zastosowań w kolejnych
rozdziałach. Ponieważ istnieje bardzo wiele sposobów podejścia do
22 WSTĘP
tematu rozwoju oprogramowania, w tej części zostaną zdefiniowane
pojęcia, a także konsekwencje stosowania modelu dziedziny do
usprawnienia komunikacji oraz projektowania.
Część II, „Elementy składowe projektu sterowanego modelem”, kon-
centruje się na powiązaniu głównych najlepszych praktyk w obiek-
towym modelowaniu dziedziny w zestaw podstawowych elementów
składowych. W tej części będziemy starać się zmniejszyć przepaść
pomiędzy modelami a praktycznym działającym oprogramowaniem.
Użycie tego rodzaju standardowych wzorców również przyczynia
się do stworzenia wspólnego języka, który może być wykorzystany
przez wszystkich członków zespołu do omawiania decyzji modelo-
wych oraz projektowych.
Głównym zadaniem tej części będzie jednak skupienie się na
tych decyzjach, które scalają model z implementacją, zwiększając jed-
nocześnie ich efektywność. Tego rodzaju scalenie wymagać będzie
zwrócenia uwagi na detale poszczególnych elementów. Ta rzemieśl-
nicza praca w małej skali przyniesie deweloperom mocne podstawy,
których będą mogli użyć, stosując różne podejścia do modelowania
opisane w częściach III oraz IV.
Część III, „Refaktoryzacja ku głębszemu zrozumieniu”, wykracza poza
podstawowe elementy składowe, aby zmierzyć się z wyzwaniem
połączenia ich w bardziej praktyczne modele, które dadzą w końcu
efekty. Zamiast przeskakiwać od razu do tajemniczych zasad projek-
towania, spróbujemy podkreślić wagę procesu odkrywania. Istotne
modele nie powstają natychmiast. W zamian potrzebują one dogłęb-
nego zrozumienia dziedziny. To zrozumienie natomiast pochodzi od
stopniowego zanurzania się w tę dziedzinę, poprzez implementację
początkowego projektu, opartego prawdopodobnie na prostym, naiw-
nym modelu, a następnie jego ciągłą modyfikację. Za każdym razem
gdy zespół zdobędzie lepsze zrozumienie dziedziny, model jest prze-
kształcany w celu jej lepszego odwzorowania, a kod jest refaktoro-
wany w celu odzwierciedlenia tego modelu, co z kolei umożliwia
jego ewentualne wykorzystanie w aplikacji. W ten sposób krok po
kroku poprzez przemyślane zmiany projektu będziemy mieć moż-
liwość stworzenia zdecydowanie bardziej dogłębnego modelu. Takie
badanie dziedziny jest z natury nieskończone, jednak nie musi być
dokonywane w sposób losowy. W części III zagłębimy się w zasady
modelowania, które będą mogły nas prowadzić w procesie odkry-
wania, a także w techniki, które pomogą nam ukierunkować poszu-
kiwania.
WSTĘP 23
Część IV, „Projekt strategiczny”, zmagać się będzie z sytuacjami
mającymi miejsce w systemach złożonych, większych organizacjach,
a także w przypadku konieczności współdziałania z zewnętrznymi
oraz starszymi systemami. W tej części odkryjemy trzy główne zasady,
mające zastosowanie do całości systemu: kontekst, destylację oraz
struktury dużej skali. Strategiczne decyzje projektowe dokonywane
są przez całe zespoły, a także poprzez ich grupy. Umożliwiają one
osiągnięcie w większej skali celów opisanych w części I oraz ich
zastosowanie w dużych systemach lub aplikacjach działających w sze-
rokiej korporacyjnej sieci komputerowej.
Dyskusje umieszczone w tej książce nie zostaną zilustrowane uprosz-
czonymi „testowymi” przykładami problemów, lecz tymi realistycznymi,
przeniesionymi z rzeczywistych projektów.
Duża część tej książki została napisana w postaci zestawu „wzorców”.
Czytelnik powinien być w stanie zrozumieć cały materiał bez wnikania
w ich istotę, jednak dla osób bardziej zainteresowanych stylem oraz budową
wzorców zostaną one opisane w dodatku na końcu książki.
24 WSTĘP
oprogramowania, a tym samym posiadają już pewną wiedzę na temat pro-
jektowania obiektowego, książka ta pozwoli wypełnić ewentualne braki,
a także rzucić nowe światło na to, w jaki sposób modelowanie obiektowe
wpasowuje się w prawdziwy projekt programistyczny. Pomoże ona rów-
nież średnio zaawansowanym programistom posiąść umiejętność stoso-
wania skomplikowanych metod modelowania oraz projektowania rzeczy-
wistych zagadnień.
Zaawansowani programiści lub eksperci będą z pewnością zainte-
resowani zapoznaniem się z przedstawionym w tej książce wyczerpującym
schematem wykorzystywania dziedziny. To systematyczne podejście do
projektu pomoże liderom poprowadzić właściwą ścieżką ich własne zespoły.
Co więcej, również spójna terminologia używana w całej książce pomoże
zaawansowanym osobom w komunikacji z ich rozmówcami.
Książka ta została napisana w sposób narracyjny, co sprawia, że może
być przeczytana od początku do końca lub też od początku dowolnego
z rozdziałów. Dlatego też czytelnicy dysponujący różną wiedzą mogą
wybierać swoją własną kolejność czytania jej rozdziałów. Zachęcam jed-
nak wszystkich do rozpoczęcia lektury od wprowadzenia do części I oraz
rozdziału 1. Do kanonu lektury należeć będą prawdopodobnie również
rozdziały 2., 3., 9. oraz 14. Wytrawny czytelnik, posiadający pewną wie-
dzę oraz przyzwyczajony do przeskakiwania rozdziałów, powinien być
w stanie przyswoić sobie najważniejsze rzeczy po lekturze nagłówków oraz
wyróżnionych fragmentów tekstu. Najbardziej doświadczeni czytelnicy
mogą zechcieć pominąć części I oraz II i prawdopodobnie będą zainte-
resowani głównie częściami III oraz IV.
Oprócz głównych adresatów tej książki, z jej lektury powinni być
w stanie również skorzystać analitycy oraz średnio zaawansowani tech-
nicznie menedżerowie projektów. Ci pierwsi mogą skoncentrować się
na połączeniu modelu z projektem i wykorzystać tę wiedzę do bardziej
efektywnej pracy w projektach prowadzonych przy użyciu zwinnych
metod. Mogą oni również chcieć skorzystać z kilku zasad projektowania
strategicznego, aby lepiej skoncentrować oraz zorganizować swoją pracę.
Menedżerowie projektu powinni być za to zainteresowani naciskiem
na stworzenie bardziej efektywnego zespołu, skoncentrowanego na pro-
jektowaniu oprogramowania o dużym znaczeniu dla ekspertów bizneso-
wych oraz użytkowników. Ponieważ jednak decyzje dotyczące projekto-
wania strategicznego zależą od dobrej organizacji zespołu oraz jego stylu
pracy, w proces ich podejmowania włączone jest kierownictwo projektu,
a one same mają duży wpływ też na jego losy.
WSTĘP 25
Zespół sterowany dziedziną
Pomimo iż zrozumienie projektowania sterowanego dziedziną już nawet
pojedynczemu programiście przyniesie korzyści w postaci cennych technik
projektowania oraz odpowiedniego spojrzenia na projekt, to jednak naj-
większe może ono przynieść w przypadku, gdy podejście do projekto-
wania sterowanego dziedziną zostanie użyte przez cały zespół programi-
styczny, a model dziedziny stanie się głównym przedmiotem dyskusji tegoż
zespołu. W ten sposób członkowie zespołu będą wspólnie używać tego
samego języka, wzbogacającego ich komunikację, a także zwiększającego
ich relacje z tworzonym przez nich oprogramowaniem. W konsekwencji
spowoduje to stworzenie czystej implementacji modelu, który będzie mógł
zostać wykorzystany przez zespół programistów, jednego schematu współ-
działania różnych zespołów nad pracą projektową, a cały zespół będzie
stale skoncentrowany na tych funkcjonalnościach, które będą najbardziej
znaczące i cenne dla całej organizacji.
Projektowanie sterowane dziedziną jest trudnym wyzwaniem tech-
nicznym, które jednak może się opłacać, przynosząc korzyści w chwili, gdy
większość oprogramowania zaczyna być traktowana jako przestarzała.
26 WSTĘP
PODZIĘKOWANIA
Moje prace nad tą książką trwały w różnej postaci przez ponad cztery
lata. Podczas całej tej drogi byłem wspierany przez wielu ludzi. W tym
miejscu chciałbym podziękować tym wszystkim, którzy zapoznali się
z rękopisem książki i dodali do niego swoje komentarze. Bez nich stwo-
rzenie tej książki najzwyczajniej nie byłoby możliwe. Kilka z tych osób
poświęciło recenzji szczególnie wiele swojej uwagi.
I tak członkowie grupy Silicon Valley Patterns Group, kierowanej
przez Russa Rufera oraz Tracy Bialek, spędzili siedem tygodni, analizu-
jąc pierwszy pełny szkic książki. Z kolei członkowie grupy dyskusyjnej
Uniwersytetu Illinois, prowadzonej przez Ralpha Johnsona, spędzili kolej-
nych kilka tygodni, analizując następny szkic. Przysłuchiwanie się dłu-
gim i żywym dyskusjom prowadzonym przez obie te grupy wywarło na
mnie duży wpływ. Dodatkowo szczegółowe komentarze, wartościowe
wskazówki oraz bezcenne wsparcie duchowe dostarczyli mi (podczas węd-
kowania) Kyle Brown oraz Martin Fowler. Komentarze Warda Cunnin-
ghama pozwoliły mi dopracować kilka szczególnie słabych punktów.
Z kolei Alistair Cockburn zachęcił mnie na początku do napisania tej
książki i wraz z Hilary Evans pomógł przebrnąć przez zawiłości procesu
jej publikacji. David Siegel wraz z Eugene Wallingford pomogli mi wspól-
nie uniknąć ośmieszenia w bardziej technicznych fragmentach książki.
Dodatkowo Vibhu Mohindra oraz Vladimir Gitlevich skrupulatnie spraw-
dzili wszystkie przykłady kodu zamieszczone w książce.
Moje najwcześniejsze badania materiału zebranego do tej książki
wykonał Rob Mee, który dodatkowo później przestudiował ze mną jej szkic.
Za jeden z głównych zwrotów podczas tworzenia tej książki odpo-
wiedzialny był Josh Kerievsky. Przekonał mnie on bowiem do użycia
„aleksandrowego”1 formatu opisu wzorców, co wpłynęło na strukturę tej
książki. Pomógł mi on również w zebraniu części materiału, stanowiącego
1
Nazwa pochodzi od nazwiska architekta Christophera Alexandra. Wraz
ze swoim zespołem, któremu przewodził, stworzył on podwaliny języka opisu-
jącego wzorce — przyp. tłum.
27
teraz zawartość części II. Stało się to w czasie wyczerpujących przygotowań
do konferencji PLoP2 w roku 1999. Ten fakt stanowił zaczyn, wokół któ-
rego powstała ta książka.
Chciałbym również podziękować Awadowi Faddoulowi za setki
godzin spędzonych nad pisaniem tej książki w jego wspaniałej kawiarni.
To wyciszenie, wraz z godzinami spędzonymi na windsurfingu, pomogło
mi utrzymać formę.
Dodatkowo jestem bardzo wdzięczny Martine Jousset, Richardowi
Paselk oraz Ross Venables za stworzenie przepięknych fotografii ilustrują-
cych kilka kluczowych zagadnień.
2
PLoP (ang. Pattern Languages of Programs Conference) — cykliczna coroczna
konferencja odbywająca się w Illinois, gromadząca osoby zajmujące się teoretycznie
oraz praktycznie zagadnieniami wzorców językowych — http://www.hillside.net/plop —
przyp. tłum.
28 PODZIĘKOWANIA
I
Zastosowanie
modelu
dziedziny
Ta osiemnastowieczna chińska mapa przedstawia cały świat. W jego samym
środku znajduje się obszar Chin, zajmujący prawie całą mapę, otoczony
przez symboliczne reprezentacje innych krajów. Ten model świata był
odpowiedni dla całego społeczeństwa, które w zamierzonym celu stało się
bardzo zamknięte. Reprezentowany przez mapę widok świata na pewno
nie był pomocny w kontaktach z obcokrajowcami. Z pewnością nie przy-
służyłby się też wcale obecnym Chinom. Mapy są modelami, a każdy
model odwzorowuje pewien fragment rzeczywistości lub też pomysł,
będący przedmiotem jego zainteresowania. Model jest uproszczeniem —
pewną interpretacją rzeczywistości, która wyodrębnia niektóre jej aspekty
przydatne do rozwiązania problemu, natomiast pomija te zdecydowanie
zbyt szczegółowe.
Każdy program dotyka również pewnej czynności lub obszaru zain-
teresowania jego użytkownika. Właśnie ten obszar zastosowania programu
definiuje jego dziedzinę. Niektóre z dziedzin dotyczą naszego fizycznego
świata. Na przykład dziedzina programu do rezerwacji biletów lotniczych
dotyczy prawdziwych pasażerów wsiadających do jak najbardziej rzeczy-
wistego samolotu. Inne natomiast są niematerialne. Na przykład dziedzinę
programu księgowego stanowią pieniądze i finanse. Dziedziny oprogra-
mowania komputerowego nie mają zazwyczaj wiele wspólnego z kom-
puterami, chociaż są od tego wyjątki. Na przykład dziedziną programu
do kontroli wersji oprogramowania jest sam proces tworzenia tego opro-
gramowania.
30 CZĘŚĆ I
W celu stworzenia programu, który w przydatny sposób będzie funk-
cjonować w obszarze działań jego użytkowników, zespół programistów
musi sięgnąć do podstaw teoretycznych związanych z tymi działaniami.
Rozmiar tej wiedzy może być zniechęcający. Ilość oraz złożoność infor-
macji mogą być przygniatające. Narzędziami, które pomagają poradzić
sobie z tym przeciążeniem, są modele. Reprezentują one tę wiedzę w spo-
sób selektywny, uproszczony, a także ustrukturyzowany. Właściwy model
porządkuje informacje i skupia się na istocie zagadnienia.
Model dziedziny nie jest tylko określonym diagramem, ale przede
wszystkim przesłaniem, które ten diagram ma przenosić. Nie stanowi on
jedynie wiedzy dziedzinowej eksperta, a przede wszystkim drobiazgowo upo-
rządkowane, selektywne i abstrakcyjne odwzorowanie tej wiedzy. Diagram może
odwzorowywać i przekazywać informacje o modelu, podobnie jak popraw-
nie napisany kod programu może przekazywać informacje z języka
naturalnego.
Modelowanie dziedziny nie polega na usiłowaniu odtworzenia jak
najbardziej „realistycznego” modelu. Nawet w dziedzinie bardzo nie-
uchwytnych rzeczywistych przedmiotów nasz model jest tylko sztucz-
nym tworem. I nie jest on tylko mechanizmem programistycznym, który
daje określone rezultaty. Bardziej przypomina tworzenie filmów, które
w określonym celu luźno odwzorowują rzeczywistość. Nawet film doku-
mentalny nie ukazuje nieobrobionego rzeczywistego obrazu życia. Podob-
nie jak reżyser wybiera określone obszary rzeczywistości, a następnie przed-
stawia je w charakterystyczny dla siebie sposób, układając je w historię
lub przesłanie, tak i osoba modelująca dziedzinę wybiera do swoich celów
określony model.
Przydatność modelu
w projektowaniu sterowanym dziedziną
Na wybór modelu w projektowaniu sterowanym dziedziną wpływ mają
następujące trzy elementy:
1. Model oraz jego implementacja w postaci oprogramowania wzajemnie na siebie
wpływają. Pomiędzy modelem a jego implementacją istnieje bliski
związek, który czyni model bardziej właściwym i zapewnia, że pro-
ces analizy, który leżał u jego podstaw, będzie mieć zastosowanie
do finalnego produktu, czyli działającego programu. To powiązanie
pomaga również w trakcie utrzymania programu oraz jego dalszego
rozwoju, ponieważ kod może zostać zinterpretowany poprzez zro-
zumienie prowadzącego do niego modelu (patrz rozdział 3.).
Istota programu
Istotą programu jest jego zdolność do rozwiązywania dla użytkownika
problemów związanych z jego dziedziną przeznaczenia. Wszystkie inne
cechy tego programu, aczkolwiek mogą być istotne, to jednak wspierają
jedynie ten cel nadrzędny. W przypadku złożonej dziedziny będzie to
trudne zadanie, wymagające skupienia wysiłku utalentowanych i wykształ-
conych osób. Programiści będą musieli zagłębić się w dziedzinę i zgro-
madzić wiedzę biznesową. Będą także zmuszeni do udoskonalenia swo-
ich umiejętności modelowania, a także projektowania dziedzinowego.
Wciąż jednak nie są to najważniejsze priorytety większości projektów
programistycznych. Wielu utalentowanych programistów nie ma zbyt dużo
powodów, aby poznać dziedzinę, w której pracuje, a jeszcze mniejsza ich
liczba podejmuje wyzwanie udoskonalenia swoich umiejętności projek-
32 CZĘŚĆ I
towania dziedzinowego. Ludzie dysponujący wykształceniem technicznym
zadowalają się przeliczalnymi wymiernymi problemami, które stanowią
wyzwanie dla ich technicznych umiejętności. Praca nad dziedziną jest
nieprzyjemna i wymaga dużej ilości skomplikowanej nowej wiedzy, która
nie wydaje się zwiększać umiejętności programistów.
W zamian osoby z talentem technicznym skłaniają się do poszuki-
wania rozwlekłych struktur i wzorców, usiłując w ten sposób pokonać
problemy dziedzinowe przy użyciu technologii. Kwestia poznania mode-
lowania dziedziny jest pozostawiana innym. Niestety, ujarzmienie złożo-
ności istoty programu musi być priorytetem od początku. W przeciwnym
razie ryzykuje się jego nieprzydatność.
34 CZĘŚĆ I
ROZDZIAŁ 1.
Przetwarzanie wiedzy
Rysunek 1.1.
1
ang. printed-circuit board, w skrócie PCB — przyp. tłum.
35
Rysunek 1.2.
2
ang. ref-des — skrót od „reference destination” — przyp. tłum.
Rysunek 1.3.
Rysunek 1.5.
Projektant: Jedyną zagadką dla mnie jest wciąż, skąd pochodzą te „pro-
pagacje”. Czy musimy umieszczać dane w każdym elemencie
składowym?
Ekspert 2: Będą one identyczne dla każdego z elementów składowych.
Projektant: W takim razie rodzaj elementu określa rodzaj propagacji.
Będą one identyczne dla każdego z elementów?
Ekspert 2: Nie jestem dokładnie pewien, co ten rysunek oznacza, ale
mógłbym sobie wyobrazić, że przechowywanie propagowanego
sygnału w każdym z elementów wyglądałoby podobnie do czegoś
w tym rodzaju.
Rysunek 1.7.
Przetwarzanie wiedzy
Analitycy finansowi przetwarzają liczby. Przesiewają je, łączą i rozdzie-
lają, poszukując w nich ukrytych znaczeń oraz sposobu ich prostej pre-
zentacji, który pozwoliłby wydobyć z nich to, co najważniejsze, czyli ich
zrozumienie, stanowiące podstawę decyzji finansowych.
PRZETWARZANIE WIEDZY 41
Osoby potrafiące tworzyć wydajne modele dziedzinowe są przetwa-
rzaczami wiedzy. Biorą oni na warsztat całe potoki wiedzy, które sondują
i zamieniają w istotne strużki. Wypróbowują jeden pomysł za drugim,
poszukując prostego widoku, zrozumiałego dla dużej liczby ludzi. Wiele
modeli jest testowanych, a następnie odrzucanych lub zmienianych. Suk-
ces przychodzi wraz z wyodrębnieniem zestawu abstrakcyjnych pojęć,
istotnych w każdym ze swych szczegółów. Proces destylacji jest wnikli-
wym wyrażeniem określonej wiedzy w sposób, który okazał się najbar-
dziej odpowiedni.
Przetwarzanie wiedzy nie jest procesem osamotnionym. Polega na
współpracy projektantów z ekspertami dziedzinowymi, a sam proces kie-
rowany jest zazwyczaj przez tych pierwszych. Razem są oni w stanie zobra-
zować informacje i nadać im przydatną postać. Surowy materiał pochodzi
z wiedzy ekspertów dziedzinowych, od użytkowników rzeczywistych
systemów, z wcześniejszego doświadczenia zespołu technicznego z pracy
ze starym programem czy wreszcie z innego projektu w tej samej dzie-
dzinie. Ma on postać dokumentów stworzonych dla projektu lub uży-
wanych przez użytkowników biznesowych, a także wielu rozmów. Wczesne
wersje prototypów przekazują doświadczenie ponownie do zespołu i zmie-
niają swoje interpretacje.
PRZETWARZANIE WIEDZY 43
Ciągła nauka
Kiedy rozpoczynamy tworzenie oprogramowania, nigdy nie wiemy wystarczająco
dużo. Wiedza w projekcie jest rozdrobniona i rozrzucona pomiędzy wiele
osób i dokumentów. Poza tym jest ona wymieszana z innymi informa-
cjami, dlatego nawet nie wiemy, jakich jej fragmentów będziemy w rze-
czywistości potrzebować. Szczególnie zwodnicze mogą okazać się dzie-
dziny mniej techniczne — nie będziemy nawet wiedzieć, ilu rzeczy nie
wiemy. Ignorancja będzie prowadzić do błędnych założeń.
Dodatkowo wszystkim projektom brakuje wiedzy. Ludzie, którzy
nauczą się czegoś wartościowego, będą odczuwać tego efekty. Reorgani-
zacja zespołu go podzieli, a wiedza znów zostanie rozproszona. Istotne
podsystemy zostaną oddane w utrzymanie innym firmom, co będzie skut-
kować dostarczeniem im kodu, lecz nie wiedzy. A w typowym podejściu
do projektowania kod oraz dokumentacja nie przekazują ciężko zdobytej
wiedzy w sposób możliwy do późniejszego jej łatwego wykorzystania.
Z tego powodu, jeżeli później z jakichkolwiek przyczyn zostanie przerwany
tradycyjny słowny przepływ wiedzy, zostanie ona utracona.
Bardzo wydajne zespoły cały czas zwiększają swoją wiedzę, stosując
w tym celu ciągłą naukę. Dla programistów oznacza to zwiększanie ich wie-
dzy technicznej wraz z ogólnymi umiejętnościami modelowania dzie-
dziny. Dodatkowo zakłada poważne studiowanie specyfiki dziedziny,
w której działają.
Tego rodzaju samodzielnie doszkalający się członkowie zespołu two-
rzą stabilną elitę ludzi skupiających się na zadaniach obejmujących naj-
bardziej krytyczne obszary (więcej informacji na ten temat znajduje się
w rozdziale 15.). Wiedza zgromadzona w umysłach elity zespołu czyni
ich osobami przetwarzającymi wiedzę w sposób bardziej wydajny.
Przykład
Wydobywanie ukrytego pojęcia
Zacznijmy od prostego przykładu modelu dziedziny, który mógłby stać się
podstawą aplikacji do rezerwowania miejsca dla ładunku na rejs statku.
Rysunek 1.9.
3
ang. overbooking — przyp. tłum.
Rysunek 1.10.
4
ang. policy — przyp. tłum.
Modele dogłębne
Przydatne modele bardzo rzadko istnieją na powierzchni. W trakcie pona-
wiania dziedziny oraz potrzeb aplikacji zazwyczaj odrzucamy powierz-
chowne elementy modelu, które wydawały się na początku istotne, lub
też zmieniamy ich perspektywę. Pojawiają się subtelne abstrakcje, które
nie były nam znane na początku, a stanowią istotę zagadnienia.
Przedstawiony wcześniej przykład jest w luźny sposób oparty na
jednym z projektów, którego będę używać w licznych przykładach w dalszej
części tej książki: systemie do transportu kontenerów. Wszystkie przykłady
będą zrozumiałe również dla osób niemających wiedzy w tej dziedzinie.
Jednak w wielu prawdziwych przykładach, w których członkowie zespołu
podlegają ciągłej nauce, w celu wykonania jasnych i przydatnych modeli
potrzebne jest od samego początku posiadanie pewnego wyrafinowania
zarówno w samej dziedzinie, jak i modelujących ją technikach.
MODELE DOGŁĘBNE 49
50 ROZDZIAŁ 1. PRZETWARZANIE WIEDZY
ROZDZIAŁ 2.
Komunikacja
i użycie języka
1
ang. Unified Modeling Language, czyli zunifikowany język modelowania —
przyp. tłum.
51
Język wszechobecny
Więc zdanie najpierw stwórz
By sedno zeń wykroić
Przesuwaj słowa, sortuj już
Daj szansę zniknąć im
Kolejność fraz — to pełny luz
Zupełnie bez znaczenia
— Lewis Carroll, „Poeta Fit, Non Nascitur”
JĘZYK WSZECHOBECNY 53
używany w komunikacji zarówno pomiędzy programistami a ekspertami
dziedzinowymi, jak również pomiędzy samymi ekspertami rozmawiają-
cymi ze sobą o wymaganiach, planowaniu prac oraz funkcjonalnościach
systemu. Im powszechniej stosowany jest język, tym lepsze będzie wza-
jemne zrozumienie w zespole.
Wszystko, co zostało opisane powyżej, jest przynajmniej wspólnym
celem, jednak model może nie być początkowo wystarczająco dobry, aby
spełniać wszystkie te role. Może brakować mu bogactwa semantycznego
specjalistycznego żargonu stosowanego w dziedzinie. Niestety, żargonu
tego nie da się zastosować bezpośrednio, ponieważ może zawierać nie-
jasności i sprzeczności. Może mu też brakować subtelnych aktywnych
funkcji, które programiści stworzyli w kodzie, ponieważ nie myśleli
o nich jak o części modelu lub styl kodowania był proceduralny i tylko
pośrednio odzwierciedlał pojęcia dziedziny.
Pomimo pozornej cykliczności całej procedury proces przetwarzania
wiedzy może stworzyć bardziej przydatne modele, jeżeli tylko cały zespół
będzie zobligowany do używania języka opartego na modelu. Stałe uży-
wanie JĘZYKA WSZECHOBECNEGO ujawni słabe strony modelu.
Dzięki temu zespół będzie mógł eksperymentować i znajdować inne
postaci niejasnych pojęć oraz ich połączeń. I w miarę jak w języku będą
odnajdywane kolejne braki, do dyskusji będą też wprowadzane nowe
słowa. Te zmiany samego języka będą traktowane jako zmiany modelu dziedziny
i będą naprowadzać zespół na konieczność uaktualnienia diagramów klas,
zmiany ich nazw oraz metod w kodzie, a nawet zmiany całego działania
tego kodu, w przypadku gdy zmianie ulegnie też znaczenie danego pojęcia.
Programiści zobligowani do stosowania języka w implementacji będą
w stanie odkryć niespójności oraz sprzeczności, zmuszając ekspertów dzie-
dzinowych do stworzenia działających zamienników.
Oczywiście, eksperci dziedzinowi, aby wyjaśnić pewne zagadnienia
lub nadać im szerszy kontekst, będą poza projektem używać też swojego
języka. Jednak w obrębie modelu powinni oni używać JĘZYKA i zgła-
szać swoje zastrzeżenia, gdy tylko uznają, że jest on dziwny, niekom-
pletny lub niewłaściwy. Dzięki wszechobecnemu stosowaniu języka opar-
tego na modelu i jego ciągłemu udoskonalaniu dojdziemy do kompletnego
i wyczerpującego modelu, stworzonego z prostych elementów, które
w połączeniu ze sobą wyrażać będą złożone pojęcia.
Dlatego też:
Wykorzystaj model jako szkielet języka. Zmobilizuj zespół
do stałego testowania tego języka w trakcie całej komunikacji
zespołowej, a także omawiania kodu. Tego samego języka uży-
waj też na diagramach, w dokumentacji, a zwłaszcza w mowie.
Przykład
Rozpracowywanie tworzenia trasy ładunku
W poniższych dwóch dialogach występują subtelne, lecz istotne różnice.
W każdym z tych scenariuszy przyjrzyjmy się temu, w jaki sposób roz-
mówcy opisują znaczenie programu dla biznesu, a w jaki on rzeczywiście
działa. Czy użytkownik oraz programista mówią w tym samym języku?
Czy jest on wystarczająco bogaty, aby zostać wykorzystany w dyskusji
o przeznaczeniu aplikacji?
JĘZYK WSZECHOBECNY 55
Scenariusz 1. Minimalna abstrakcja dziedziny
Rysunek 2.1.
Użytkownik: Jeżeli zmienimy punkt odpraw celnych (ang. customs cle-
arance), będziemy musieli zmienić cały plan trasy (ang. routing plan).
Programista: Zgadza się. Usuniemy wszystkie wiersze z tabeli ładunku
z daną wartością identyfikatora (ang. cargoID), następnie przekażemy do
usługi trasowania — Routing Service — nowe miejsce przeznaczenia
(ang. destination), pochodzenia (ang. origin) oraz punkt odpraw celnych,
co spowoduje ponowne uzupełnienie danych w tabeli. W obiekcie
reprezentującym ładunek — Cargo — będziemy mieć zmienną
logiczną, co sprawi, że będziemy wiedzieć, czy w tabeli są dane.
Użytkownik: Usuniemy wiersze? W porządku, nieważne. Tak czy ina-
czej, jeżeli wcześniej nie zdefiniowaliśmy żadnego punktu odpraw
celnych, musimy zrobić dokładnie to samo.
Programista: Oczywiście. W każdym przypadku zmiany (lub też zdefi-
niowania po raz pierwszy) miejsca pochodzenia, przeznaczenia lub
punktu odpraw celnych sprawdzimy, czy dane są w tabeli ładunku,
usuniemy je i pozwolimy usłudze trasowania — Routing Service
— ponownie je utworzyć.
Użytkownik: Naturalnie w przypadku, gdy stare dane były poprawne,
nie będziemy chcieli tego robić.
Programista: Żaden problem. Po prostu łatwiej jest pozwolić usłudze
trasowania — Routing Service — wypełniać tabelę za każdym razem.
Użytkownik: Zgoda, lecz utworzenie wszystkich planów wspierających
nową trasę wymagać będzie od nas dodatkowej pracy. Dlatego też
dopóki nie ma takiej konieczności, nie chcemy zmieniać trasy.
Rysunek 2.2.
JĘZYK WSZECHOBECNY 57
Programista: Żaden problem. Po prostu łatwiej jest pozwolić usłudze
Routing Service inicjalizować obiekt Itinerary za każdym razem.
Użytkownik: Zgoda, lecz utworzenie wszystkich planów wspierających
nową trasę wymagać będzie od nas dodatkowej pracy. Dlatego też
dopóki nie ma takiej konieczności, nie chcemy zmieniać trasy.
Programista: W porządku. W takim razie będziemy musieli dodać pewną
funkcjonalność do obiektu Route Specification. Wtedy w przy-
padku zmiany czegokolwiek w tym obiekcie sprawdzimy, czy stan
obiektu Itinerary wciąż odpowiada specyfikacji podróży. Jeżeli nie,
wtedy obiekt Routing Service odświeży zawartość Itinerary.
Użytkownik: Nie musisz przejmować się miejscem pochodzenia oraz
przeznaczenia, ponieważ inicjalizacja Itinerary zawsze je zmieni.
Programista: Dobrze, lecz dokonywanie sprawdzenia za każdym razem
będzie dla nas prostsze. Obiekt Itinerary będzie tworzony tylko
w przypadku, gdy nie będzie odpowiadać zawartości Route Spe-
cification.
W drugim dialogu przekazywanych jest więcej informacji na temat
zamiaru eksperta dziedzinowego. W obu przypadkach użyto określenia
„plan podróży” (ang. itinerary), jednak za drugim razem wskazywało ono
na obiekt, który mógł być przez obie strony opisany w bardziej precyzyjny
i konkretny sposób. Dlatego możliwe było bezpośrednie opisanie „specy-
fikacji podróży” (ang. route specification), bez odwoływania się do jej atry-
butów oraz procedur.
Oba dialogi z pewnością były bardzo do siebie podobne. W rzeczy-
wistości pierwszy z nich byłby bardziej rozwlekły, pełen wyjaśnień funk-
cjonalności oczekiwanej po aplikacji oraz nieporozumień. Terminologia
oparta na modelu dziedziny, wykorzystana w drugim przykładzie, uczy-
niła go bardziej zwięzłym.
Modelowanie na głos
Oddzielenie mowy od pozostałych form komunikacji byłoby zdecydo-
waną stratą, ponieważ jako ludzie posiadamy geniusz posługiwania się
językiem mówionym. Niestety, w czasie komunikowania się ze sobą ludzie
zazwyczaj nie używają języka opartego na modelu dziedziny.
Takie stwierdzenie może początkowo nie wydawać się prawdziwe
i rzeczywiście są od niego odstępstwa. Jednak, drogi Czytelniku, kiedy
będziesz brać udział w kolejnej dyskusji poświęconej projektowi lub wyma-
ganiom, zachęcamy Cię do uważnego przysłuchania się jej. Z pewnością
2
Książka nie została jeszcze wydana w Polsce — przyp. tłum.
MODELOWANIE NA GŁOS 59
zadania. W trakcie rozmowy ludzie w sposób naturalny odkrywają róż-
nice w interpretacji oraz znaczenie słów, a następnie je usuwają. Znajdują
trudne obszary w języku i je wygładzają.
Kiedyś w szkole średniej pobierałem intensywne nauki języka hisz-
pańskiego. Zasadą panującą na lekcjach był zakaz mówienia w języku
angielskim. Na początku było to niezmiernie frustrujące. Wszystko wyda-
wało się nienaturalne i wymagało wiele samodyscypliny. Ostatecznie jed-
nak wraz z kolegami z klasy uzyskałem taki poziom płynności wymowy,
którego nigdy nie osiągnąłbym ćwiczeniami na papierze.
Także w sytuacji, gdy w dyskusjach (szczególnie w tych, w których
programiści rozmawiają z ekspertami dziedzinowymi o scenariuszach
i wymaganiach) używany jest JĘZYK WSZECHOBECNY, poznajemy
go bardziej i możemy nauczyć się nawzajem jego niuansów. W naturalny
sposób częściej będziemy używać języka, którym rozmawiamy, niż dia-
gramów i dokumentów.
Wprowadzenie JĘZYKA WSZECHOBECNEGO do projektu infor-
matycznego jest trudne i w tym celu musimy w pełni wykorzystać nasze
naturalne zdolności. Zupełnie w ten sam sposób, w który zmysły wzroku
i orientacji przestrzennej umożliwiają nam błyskawiczne przetwarzanie
i przyswajanie informacji graficznych, w celu stworzenia modelu możemy
również wykorzystać nasze wrodzone zdolności do używania sensownego
gramatycznego języka.
Z tego powodu, jako uzupełnienie wzorca JĘZYKA WSZECH-
OBECNEGO:
W trakcie opisywania systemu pobaw się modelem. Opisz na
głos scenariusze, używając do tego elementów oraz zawartych
w nim interakcji, łącząc różne pojęcia w sposób przez niego
dozwolony. Znajdź prostszy sposób opisu tego, co chcesz powie-
dzieć, a następnie sprowadź te nowe pojęcia z powrotem do postaci
diagramów oraz kodu.
DOKUMENTY I DIAGRAMY 63
Zachowanie obiektu może być przekazane za pomocą nazw operacji
i w sposób pośredni zademonstrowane w postaci diagramów interakcji
(lub sekwencji), jednak nie może być wyrażone bezpośrednio. W tym celu
trzeba posiłkować się dodatkowym tekstem lub rozmową. Innymi słowy,
diagram UML nie może przekazać dwóch najbardziej istotnych aspektów
modelu, tj. znaczenia reprezentowanych przez niego pojęć oraz przezna-
czenia obiektów. Nie powinno to jednak zbytnio nas martwić, gdyż
ostrożne zastosowanie języka angielskiego (hiszpańskiego lub dowolnego
innego) może tę lukę uzupełnić.
Język UML nie jest też satysfakcjonującym językiem programowania.
Żadne próby generowania kodu bezpośrednio z narzędzi do modelowania,
które widziałem, nie były zbyt przydatne. Jeżeli będziesz, Użytkowniku,
ograniczony możliwościami języka UML, wtedy bardzo często będziesz
zmuszony odrzucić najbardziej istotne elementy modelu, ponieważ ich
niedopasowanie bezpośrednio do diagramu będzie stanowić regułę. Oczy-
wiście, również generator kodu nie będzie w stanie wykorzystać wszyst-
kich przypisów tekstowych. Jeżeli jednak będziesz używać technologii
umożliwiającej tworzenie programów wykonywalnych w języku diagra-
mów przypominających UML, wtedy taki diagram będzie jedynie ogra-
niczony do jeszcze jednego widoku samego programu, a właściwe zna-
czenie słowa „model” zostanie w tym przypadku utracone. Jeżeli będziesz
używać UML w charakterze swojego języka implementacyjnego, wciąż
będziesz potrzebować innych sposobów na komunikację niezaśmieconego
niczym modelu.
Diagramy są narzędziami do komunikacji i wyjaśniania, ułatwiającymi
wymianę poglądów. W takim charakterze sprawdzają się te najprostsze
i najbardziej zminimalizowane. Złożone diagramy całego modelu obiek-
towego nie będą w stanie niczego zakomunikować ani wyjaśnić. Przeciążą
one czytelnika ilością szczegółów i brakiem znaczenia. To oddala nas
znacznie od obejmujących wszystko diagramów modeli obiektowych lub
nawet repozytorium bazodanowego w UML, a skłania do użycia uprosz-
czonych diagramów pojęciowo ważnych części modelu obiektowego, istot-
nych w celu zrozumienia sensu projektu. Diagramy umieszczone w tej
książce będą zazwyczaj zgodne z tymi, które wykorzystuję w projektach.
W trakcie omawiania znaczenia obiektów będą one upraszczać, wyjaśniać,
a nawet wykorzystywać trochę niestandardową notację. Będą pokazywać
ograniczenia projektu, lecz nie będą szczegółowymi specyfikacjami pro-
jektowymi. Diagramy te reprezentować będą zarys pomysłów.
Istotne szczegóły projektu są zachowane w kodzie. Dobrze napisana imple-
mentacja powinna być przejrzysta, przekazując obraz modelu leżący u jej
podstaw (zapewnienie tego będzie tematem kolejnego rozdziału i w dużej
DOKUMENTY I DIAGRAMY 65
Programowanie ekstremalne koncentruje się wyłącznie na aktywnych
elementach programu oraz testach wykonania. Nawet komentarze doda-
wane do kodu nie wpływają na jego zachowanie, dlatego zawsze mogą
one stracić synchronizację z aktywnym kodem i sterującym nim mode-
lem. Zewnętrzne dokumenty oraz diagramy również nie wpływają na
zachowanie programu, więc podlegają tej samej zasadzie. Z drugiej strony,
język mówiony oraz ulotne diagramy na tablicy nie istnieją tak długo,
aby wprowadzać dodatkowe zamieszanie. Poleganie na kodzie jako medium
komunikacyjnym motywuje programistów do zachowania jego czystości
oraz przejrzystości.
Jednak użycie kodu w charakterze dokumentu projektowego ma
swoje ograniczenia. Może on przytłoczyć czytelnika szczegółami. Cho-
ciaż jego działanie jest jednoznaczne, nie znaczy to wcale, że jest oczy-
wiste. A znaczenie tego zachowania może być trudne do przekazania.
Inaczej mówiąc, dokumentacja wyłącznie przy użyciu kodu natrafia na te
same podstawowe problemy, co użycie szczegółowych diagramów UML.
Oczywiście, dodatkowa obfita komunikacja w zespole przy użyciu języka
mówionego nadaje kontekst i prowadzi poprzez kod, jednak jest ona
ulotna i bardzo lokalna. Dodatkowo programiści nie są jedyną grupą ludzi
zainteresowaną zrozumieniem modelu.
Dokument nie powinien usiłować spełniać tego, co kod już dobrze zapewnia.
Kod programu już sam w sobie opisuje szczegóły. Stanowi on dokładną
specyfikację działania aplikacji.
Pozostałe dokumenty powinny uwypuklić znaczenie modelu, dając
wgląd w struktury dużej skali, a także ukierunkować uwagę na jego
istotne elementy. W przypadku, gdy kod programu nie umożliwia prostej
implementacji pomysłu, dokumenty mogą wyjaśnić zamiary leżące u pod-
staw projektu. Dokumenty powinny stanowić dopełnienie kodu programu
oraz języka mówionego.
DOKUMENTY I DIAGRAMY 67
Wykonywalna podstawa
Przyjrzyjmy się teraz wyborowi dokonanemu przez społeczność programo-
wania ekstremalnego (i nie tylko), aby prawie całkowicie polegać na kodzie
wykonywalnym oraz związanych z nim testach. W większej części tej książki
opisujemy sposoby nadania kodowi programu znaczenia poprzez użycie
PROJEKTOWANIA STEROWANEGO MODELEM (patrz rozdział 3.).
Dobrze napisany kod programu może być bardzo opisowy, jednak komuni-
kowany przez niego przekaz nie musi być bardzo precyzyjny. Oczywiście,
działanie programu spowodowane przez fragment kodu jest jednoznaczne.
Jednak nazwa metody może być wieloznaczna i myląca lub też sama metoda
może być nieaktualna, gdy porówna się zawartość jej ciała z modelem. Aser-
cje w teście są rygorystyczne, w przeciwieństwie do historii opowiadanej
przez nazwy zmiennych oraz sposób organizacji kodu. Dobry styl progra-
mowania w najlepszy możliwy sposób zachowuje połączenie opisu kodu
z działaniem programu, jednak jego użycie wciąż będzie wymagało ćwicze-
nia samodyscypliny. Trzeba być bardzo pedantycznym, aby napisać kod,
który nie tylko działa poprawnie, lecz także przekazuje właściwe informacje.
Eliminacja tego rodzaju rozbieżności jest jednym z głównych atu-
tów sprzedażowych specjalizowanych praktyk w rodzaju projektowania
deklaratywnego (opisanego w rozdziale 10.), w których określenie prze-
znaczenia elementu programu definiuje jednocześnie jego rzeczywiste
działanie w programie. Pragnienie zachowania takiej spójności jest głów-
nym czynnikiem motywującym do automatycznego tworzenia kodu pro-
gramów z modelu UML, jednak w praktyce nie działa to zbyt dobrze.
Pomimo ryzyka, że kod może być mylący, wciąż jednak będzie on
bliżej rzeczywistości niż inne dokumenty. Uwspólnianie ze sobą działania
kodu, jego przeznaczenia, a także przekazywanego przezeń komunikatu
przy użyciu obecnych standardów technologicznych wymaga zdyscypli-
nowania i szczególnego sposobu myślenia o projektowaniu (omówionego
w szczegółach w części III). Aby kod mógł przekazywać informacje w spo-
sób efektywny, musi być oparty na tym samym języku, co ten użyty do
tworzenia wymagań — tym samym, którego programiści używają do
komunikacji między sobą oraz z ekspertami dziedzinowymi.
Modele objaśniające
Jednym z podstawowych założeń tej książki jest przekonanie, że jeden
wspólny model powinien stanowić podstawę implementacji, projektu oraz
komunikacji w zespole. Używanie w każdym z tych oddzielnych celów
innego modelu stanowi duże ryzyko.
Przykład
Morskie operacje logistyczne i trasy
Wyobraźmy sobie aplikację, która będzie śledzić ładunki firmy zajmującej
się logistyką morską. Model będzie zawierać szczegółowy obraz tego,
w jaki sposób poszczególne operacje portowe oraz podróże statków składają
się na plan operacyjny transportu ładunku (jego „trasę”). Jednak dla osób
niezwiązanych z branżą diagram klas nie musi być wcale olśniewający.
W takim przypadku model objaśniający może pomóc członkom
zespołu zrozumieć właściwe znaczenie tego diagramu. To inne spojrze-
nie na te same zagadnienia.
Każdy wiersz na rysunku 2.5 reprezentuje operację portową (łado-
wanie lub rozładowanie ładunku), moment przechowywania ładunku
MODELE OBJAŚNIAJĄCE 69
Rysunek 2.4.
Diagram klas
dla trasy podróży
Rysunek 2.5.
Model objaśniający
dla trasy podróży
w magazynie na brzegu lub też przewożenie ładunku w ładowni na statku
podczas rejsu. Taki opis nie odpowiada w szczegółach diagramowi klas,
jednak wzmacnia przekaz dotyczący najważniejszych elementów dziedziny.
Tego rodzaju diagram, wraz z przekazanymi w języku naturalnym
wyjaśnieniami dotyczącymi reprezentowanego przez niego modelu,
może pomóc programistom oraz ekspertom dziedzinowym zrozumieć
bardziej rygorystyczne diagramy modelu. Będzie je zdecydowanie łatwiej
zrozumieć wspólnie niż każdy z osobna.
Związanie modelu
z implementacją
71
Pomimo iż model był „poprawny” i stanowił wynik wyczerpującej
współpracy pomiędzy analitykami technicznymi a ekspertami biznesowymi,
programiści doszli jednak do wniosku, że obiekty oparte na pojęciach
z modelu nie mogą stanowić podstawy ich projektu. Dlatego też zaczęli
naprędce przygotowywać projekt tymczasowy. W konsekwencji ich projekt
używał wprawdzie kilku identycznych nazw klas oraz atrybutów w war-
stwie danych, jednak nie był oparty na istniejącym ani żadnym innym
modelu.
Projekt wprawdzie miał model dziedziny, ale na ile dobry jest model
na papierze, jeżeli nie pomaga bezpośrednio w tworzeniu działającego opro-
gramowania?
Kilka lat później byłem świadkiem identycznego wyniku w zupełnie
innym procesie. Projekt miał na celu zastąpić istniejącą aplikację napisaną
w języku C++ nową aplikacją utworzoną w języku Java. Stara aplikacja
została zaprojektowana bez podejścia obiektowego. Jej projekt (jeżeli jaki-
kolwiek istniał) dopuścił tworzenie kolejnych funkcjonalności aplikacji na
bazie istniejącego kodu, bez jakiegokolwiek zauważalnego podejścia do
uogólnienia i abstrakcji obiektów.
Najstraszniejsze było to, że rezultat końcowy obu tych procesów był
bardzo podobny. Oba miały pewną funkcjonalność, lecz były rozdmuchane,
bardzo trudne do zrozumienia i w efekcie końcowym prawie niemożliwe
do utrzymania. Pomimo iż w niektórych miejscach ich implementacja
zakładała określony kierunek, to jednak czytając kod aplikacji, nie można
było zdobyć zbyt dużo wiedzy o przeznaczeniu systemu. Żaden z tych
dwóch procesów nie skorzystał z dostępnego paradygmatu obiektowości,
no może poza użyciem fantazyjnych struktur danych.
Średniowieczny
komputer
astronomiczny
Astrolabium zostało
wynalezione przez
Astrolabium, używane do wyznaczania pozycji gwiazd, starożytnych Greków,
jest mechaniczną implementacją modelu nieba a następnie
udoskonalone przez
Ścisłe powiązanie kodu z modelem nadaje programowi znaczenie i spra- średniowiecznych
arabskich naukowców.
wia, że model jest bardziej adekwatny do rzeczywistości.
Obracające się koło
*** (nazywane rete)
stanowiło reprezentację
Projekty, które nie posiadają zupełnie modelu dziedziny, lecz po położenia ustalonych
prostu tworzą kod dostarczający kolejne funkcjonalności, nie zyskują wiele gwiazd na sferze
korzyści z procesu przetwarzania wiedzy oraz komunikacji opisanych niebieskiej.
Wymienne płyty,
w poprzednich dwóch rozdziałach. Nie poradzą sobie one wcale ze zło- na których wyryte były
żoną dziedziną. lokalne współrzędne
Z drugiej strony, wiele złożonych projektów podejmuje pewne próby astronomiczne,
tworzenia modelu dziedziny, lecz nie utrzymuje ścisłego związku pomiędzy reprezentowały widoki
z różnych szerokości
modelem i kodem. Model utworzony przez te projekty być może będzie geograficznych.
spełniać pomocniczą funkcję w procesie odkrywania dziedziny, lecz póź- Przesuwanie koła rete
niej stanie się od niej zupełnie oderwany i może nawet wprowadzać po płycie umożliwiało
w błąd. Cała troska poświęcona modelowi nie będzie stanowić żadnego wykonanie obliczeń
pozycji ciał niebieskich
zabezpieczenia, ponieważ będzie się on różnić od kodu. w dowolnym
Połączenie może rozpaść się na wiele sposobów, lecz bardzo często momencie roku.
jest to wynikiem świadomego wyboru. Wiele metodologii projektowych W przypadku
zaleca stosowanie modelu analitycznego odmiennego od samego projektu, posiadania pozycji
gwiazdy lub Słońca
często nawet prowadzonego przez innych ludzi. Model ten nazywany można było
jest analitycznym, ponieważ jest wynikiem analizy dziedziny biznesowej też obliczyć czas
w celu uporządkowania jej pojęć, pomijającej rolę, jaką będzie on póź- astronomiczny.
niej pełnić w oprogramowaniu. Model analityczny stanowi w zamierze- Astrolabium stanowiło
mechaniczną
niu narzędzie służące jedynie zrozumieniu dziedziny. Rozpatrywanie implementację
w tym momencie ewentualnych problemów związanych z implementacją obiektowego
może tylko zaciemnić jego obraz. Następnie powstaje projekt, który może modelu nieba.
Paradygmaty modelowania
i narzędzia wspierające
Aby opłaciło się stosowanie PROJEKTOWANIA STEROWANEGO
MODELEM, wspomniana wcześniej zgodność modeli musi być dosłowna,
pominąwszy jedynie ludzkie błędy. Aby utworzyć taki bliski związek
modelu i projektu, niemal niezbędne staje się wykorzystanie paradyg-
matu modelowania wspieranego przez narzędzia umożliwiające utworze-
nie bezpośrednich analogii pojęć stosowanych w modelu.
Programowanie obiektowe jest tak potężnym narzędziem, ponieważ
opiera się na paradygmacie modelowania i udostępnia implementacje
abstrakcyjnych modeli. Z punktu widzenia programisty obiekty istnieją
w pamięci, są związane z innymi obiektami i zorganizowane w klasy, zaś
ich zachowanie sterowane jest komunikatami. Chociaż wielu programi-
Przykład
Od podejścia proceduralnego
do sterowanego modelem
Jak wspomnieliśmy w rozdziale 1., płytka obwodów drukowanych (ang.
printed circuit board — PCB) może być traktowana jako zbiór przewodników
elektrycznych, zwanych sieciami lub ścieżkami (ang. nets), łączących nóżki
w różnych elementach. Często istnieją dziesiątki tysięcy takich połączeń.
Specjalne oprogramowanie do projektowania obwodów drukowanych
znajduje najlepsze połączenie dla wszystkich ścieżek, aby żadne z nich
nie krzyżowały się ze sobą ani nie przeszkadzały w inny sposób. Osiąga to
dzięki optymalizacji ścieżek spełniających niewyobrażalną liczbę ograniczeń
dotyczących ich położenia, które zostały nałożone przez projektantów.
Chociaż narzędzia do projektowania obwodów drukowanych są bardzo
zaawansowane, to wciąż mają pewne ograniczenia.
Jednym z problemów jest to, że każda z tych tysięcy ścieżek może
mieć swoje reguły dotyczące ułożenia. Projektanci obwodów wiele z nich
traktują jako przynależne do naturalnej grupy, która powinna współdzielić
te same reguły. Na przykład niektóre z sieci ścieżek tworzą szyny.
Rysunek 3.2.
Diagram objaśniający
szyny i sieci
Projekt mechaniczny
Zdesperowani inżynierowie stworzyli w narzędziu obejście tego ogranicze-
nia, definiując skrypty, które przetwarzają plik z danymi programu i wpisują
reguły bezpośrednio do niego, za jednym zamachem dla całej szyny.
Program przechowuje listę połączeń w pliku ścieżek, który wygląda
podobnie do tego poniżej.
Nazwa Ścieżki Element.Nóżka
------------- -------------
Xyz0 A.0, B.0
Xyz1 A.1, B.1
Xyz2 A.2, B.2
...
Set assignedRules() {
return rules;
}
}
Set assignedRules() {
Set result = new HashSet();
result.addAll(super.assignedRules());
result.addAll(bus.assignedRules());
return result;
}
}
Klasa Rola
Repozytorium ścieżek Pozwala wyszukać ścieżkę po nazwie
(NetRepository)
Fabryka powiązanych szyn Otrzymując kolekcję ścieżek, używa konwencji
(InferredBusFactory) nazewniczej do wyszukania szyn
Repozytorium szyn Pozwala wyszukać szynę po nazwie
(BusRepository)
assertTrue(a0.assignedRules().contains(minWidth4));
assertEquals(minWidth4, a0.getRule(MIN_WIDTH));
assertEquals(minWidth4, a1.getRule(MIN_WIDTH));
}
1
Ten przykład podał mi Brian Maric.
MODELOWANIE PRAKTYCZNE 87
podoba, czy nie, programiści także tworzą model. Lepiej więc od początku
skonfigurować projekt w ten sposób, aby programiści mogli dobrze wyko-
nać to zadanie modelowania.
Dlatego:
Każda osoba techniczna mająca wkład do modelu musi spę-
dzić pewną ilość czasu z kodem, jakkolwiek wysokopoziomowej
roli w projekcie by nie pełniła. Każdy, kto jest odpowiedzialny
za zmianę kodu, powinien nauczyć się, w jaki sposób wyrażać
model w kodzie. Każdy programista musi być na pewnym pozio-
mie zaangażowany w dyskusję na temat modelu i mieć kontakt
z ekspertami dziedzinowymi. Wszystkie osoby wnoszące na różne
sposoby wkład w projekt muszą świadomie zachęcać tych, którzy
bezpośrednio dotykają kodu, do dynamicznej wymiany zagad-
nień z modelu przy użyciu JĘZYKA WSZECHOBECNEGO.
***
Ostre oddzielenie modelowania od programowania nie działa dobrze,
chociaż duże projekty wciąż potrzebują technicznych liderów, którzy
koordynują projekt oraz modelowanie na wysokim poziomie, a także
pomagają w wypracowaniu najtrudniejszych lub najbardziej istotnych
decyzji. Tego rodzaju decyzje opiszemy w części IV, „Projekt strategiczny”.
Zawarte w niej treści powinny stymulować powstawanie pomysłów na
bardziej produktywne sposoby definiowania ról oraz odpowiedzialności
dla osób o wysokopoziomowym profilu technicznym.
1
Ten rodzaj projektowania obiektowego zmienia paradygmat myślenia
o programach jako obiektach z algorytmami na rzecz postrzegania tych pierwszych
przez pryzmat ich ról oraz odpowiedzialności w projekcie. Ten typ projektowa-
nia ma swoje korzenie w Smalltalku, w którym wszystko jest postrzegane jako
obiekt, zaś aplikacja jest niczym więcej niż tylko połączeniem odpowiednio dopa-
sowanych, współpracujących ze sobą obiektów — przyp. tłum.
2
Rebecca Wirfs-Brock jest jednym z pionierów projektowania obiektowego.
Swój paradygmat projektowania sterowanego odpowiedzialnością zdefiniowała
po raz pierwszy, pracując w Textronixie w 1990 r. Jest założycielką i właścicielką
Wirfs-Brock Associates (http://www.wirfs-brock.com) — przyp. tłum.
3
Bertrand Meyer, ur. w 1950 r., jest francuskim teoretykiem i profesorem
nauk ścisłych. Od samego początku był wielkim orędownikiem programowania
obiektowego. Jest twórcą języka Eiffel, od podstaw wspierającego projektowanie
kontraktowe, zakładające, iż systemy informatyczne współpracują ze sobą na
zasadzie kontraktu oraz zobowiązań. Jest to daleko posunięta metafora standar-
dowego podejścia biznesowego, w którym klient oraz dostawca ustalają kontrakt
dotyczący współpracy — przyp. tłum.
4
Craig Larman, ur. w 1958 r., jest kanadyjskim profesorem specjalizują-
cym się w metodologiach zwinnych oraz analizie i projektowaniu obiektowym —
przyp. tłum.
90 CZĘŚĆ II
Gdy projekt napotka mniejsze lub większe problemy, programiści
mogą znaleźć się w sytuacji, w której opisane pryncypia mogą wydawać
się niemożliwe do zastosowania. Aby więc uchronić proces projektowania
sterowanego dziedziną przed wykolejeniem, muszą oni dobrze zrozumieć,
w jaki sposób dobrze znane podstawowe idee wspierają PROJEKTOWA-
NIE STEROWANE MODELEM.
Materiał zawarty w kolejnych trzech rozdziałach został zorganizo-
wany w celu udostępnienia „języka wzorców” (patrz dodatek A), ukazują-
cego, w jaki sposób subtelne różnice w modelach oraz decyzjach projekto-
wych mogą wpływać na proces projektowania sterowanego dziedziną.
Diagram u góry następnej strony będzie stanowić mapę nawigacyjną.
Prezentuje on wzorce przedstawione w tej części oraz kilka sposobów,
w jakie wzorce te wpływają na siebie.
Współdzielenie tych standardowych wzorców wprowadza do pro-
jektu porządek oraz ułatwia członkom zespołu wzajemne zrozumienie
swojej pracy. Stosowanie standardowych wzorców wzbogaca JĘZYK
WSZECHOBECNY, który może być wykorzystywany przez wszystkich
członków zespołu do omawiania modelu oraz decyzji projektowych.
Utworzenie dobrego modelu dziedziny jest sztuką. Jednak prak-
tyczny projekt oraz implementacja poszczególnych elementów modelu
mogą być całkiem uporządkowaną praktyką. Wyizolowanie projektu dzie-
dziny z szeregu innych zagadnień współdzielonych przez systemy infor-
matyczne wyjaśni w dużym stopniu związek projektu z modelem. Zde-
finiowanie elementów modelu w zgodzie z ich pewnymi wyróżnikami
znacznie wyostrzy ich przekaz. Stosowanie uznanych wzorców do projek-
towania poszczególnych elementów pomaga stworzyć model, który będzie
praktyczny podczas jego implementacji.
Bardzo rozbudowane modele mogą sobie poradzić ze złożonością
dziedziny jedynie w sytuacji, kiedy dużo uwagi poświęcono ich składnikom
podstawowym, które dzięki swojej szczegółowości będą mogły zostać
z pewnością później wykorzystane przez zespół.
92 CZĘŚĆ II
ROZDZIAŁ 4.
Wyizolowanie
dziedziny
93
Architektura warstwowa
1
Frank Buschman jest współautorem książki Pattern-Oriented Software Archi-
tecture, opisującej szczegółowo zastosowanie wzorców w projektowaniu archi-
tektury — przyp. tłum.
ARCHITEKTURA WARSTWOWA 95
oraz konwencje przemysłowe. Chociaż istnieje wiele typów warstw, to
jednak najbardziej skuteczne architektury używają pewnych odmian nastę-
pujących czterech warstw pojęciowych:
Przykład
Podział na warstwy w przypadku funkcjonalności
internetowej systemu bankowego
Aplikacja udostępnia szereg funkcjonalności zarządzania kontami banko-
wymi. Jedną z nich jest przelew środków, gdzie użytkownik podaje numery
dwóch rachunków oraz kwotę, a następnie zatwierdza przelew.
Aby ten przykład było łatwiej zrozumieć, pominę większość tech-
nicznych aspektów, a w szczególności zabezpieczenia. Projekt dziedziny
zostanie również uproszczony (rzeczywista złożoność zwiększyłaby jedynie
potrzebę posiadania architektury warstwowej). Co więcej, ta konkretna
infrastruktura zaprezentowana w tym przykładzie miała w zamierzeniu
być prosta oraz oczywista, co dotyczyć miało również samego przykładu —
nie jest to żaden sugerowany projekt. Odpowiedzialności związane z pozo-
stałymi funkcjonalnościami systemu będą podzielone na warstwy w sposób
przedstawiony na rysunku 4.1.
Zauważ, że to warstwa dziedziny, a nie warstwa aplikacji jest odpowie-
dzialna za podstawowe reguły biznesowe — w tym przypadku reguła mówi:
„Każde uznanie na rachunku musi mieć odpowiadające mu obciążenie”.
ARCHITEKTURA WARSTWOWA 97
Rysunek 4.1.
Aplikacja nie czyni żadnych założeń co do źródła pochodzenia żądania
Obiekty wypełniają
odpowiedzialności przelewu. Interfejs użytkownika zawierać będzie prawdopodobnie pola do
zgodne z warstwą, wprowadzania numerów kont oraz wartości przelewu, a także przyciski
do której przynależą, odpowiedzialne za wydawanie poleceń. Mógłby on jednak — bez wpływu
i są bardziej na warstwę aplikacji oraz jakąkolwiek z niższych warstw — zostać zastąpiony
związane z innymi żądaniem przelewu w postaci dokumentu XML. Takie rozdzielenie warstw
obiektami w tej występuje nie dlatego, że w projektach interfejsy użytkowników są często
samej warstwie zastępowane dokumentami XML, lecz dlatego, że spójne rozdzielenie zadań
czyni projekt każdej warstwy łatwym do zrozumienia i utrzymania.
W rzeczywistości sam rysunek 4.1 jedynie w sposób umiarkowany
ilustruje problem braku izolacji dziedziny. Ponieważ musiał on obejmować
wszystko, począwszy od żądania przelewu, do nadzorowania transakcji,
warstwa dziedziny musi zostać uproszczona w taki sposób, aby cała inter-
akcja z systemem była dość prosta do zrozumienia. Gdybyśmy skoncen-
trowali się na projekcie wyizolowanej warstwy dziedziny, zostawilibyśmy
miejsce na stronie oraz w naszych głowach na model, który w lepszy
sposób reprezentowałby reguły tej dziedziny. Być może dołączylibyśmy
pełną rachunkowość, obiekty obciążeń i uznań rachunków albo też obiekty
reprezentujące transakcje pieniężne.
ARCHITEKTURA WARSTWOWA 99
połączony z serwerem pocztowym, faksowym lub dowolnym innym aktu-
alnie dostępnym. Jednak najważniejsza korzyść związana jest z uprosz-
czeniem warstwy aplikacji, która jest wąsko skoncentrowana na swoim
zadaniu i wie, kiedy ma wysłać wiadomość, lecz nie przejmuje się tym,
w jaki sposób to wykonać.
Warstwy aplikacji oraz dziedziny polegają na USŁUGACH dostar-
czanych przez warstwę infrastruktury. Jeżeli tylko zakres USŁUGI został
poprawnie wybrany, a jej interfejs prawidłowo zaprojektowany, wtedy
obiekt wywołujący może pozostać luźno związany, bez uwzględniania
skomplikowanej logiki kryjącej się pod interfejsem USŁUGI.
Jednak nie cała infrastruktura przykryta jest USŁUGAMI możliwymi
do wywołania z warstw wyższych. Niektóre komponenty techniczne są
zaprojektowane tak, aby w bezpośredni sposób wspierać podstawowe
funkcje innych warstw (na przykład poprzez udostępnienie abstrakcyj-
nych klas bazowych dla obiektów dziedziny) oraz dostarczać mechanizm do
ich powiązania (na przykład implementacje wzorca MVC i podobnych).
Tego rodzaju „szkielet architektury” ma zdecydowanie większy wpływ
na projekt innych części programu.
Szkielety architektury
W sytuacji, gdy infrastruktura udostępniana jest w postaci USŁUG wywo-
ływanych przez interfejsy, zrozumienie podziału na warstwy oraz sposobu
ich utrzymania w sposób luźno związany ze sobą jest dość intuicyjne.
Jednak niektóre problemy techniczne wymagają bardziej zachłannej formy
infrastruktury. Struktury integrujące wiele potrzeb związanych z infra-
strukturą wymagają często, aby inne warstwy były zaimplementowane
w określony sposób, na przykład jako podklasa klasy szkieletowej lub
z uporządkowanymi sygnaturami metod (wydawać by się mogło bardzo
nieintuicyjne, aby podklasa była w warstwie wyższej niż klasa nadrzędna,
ale miejmy na uwadze, która z klas zawiera więcej wiedzy o drugiej).
Najlepszy szkielet architektury rozwiązuje złożone problemy techniczne,
pozwalając programiście zajmującemu się dziedziną skoncentrować się na
wyrażeniu modelu. Niemniej jednak szkielet ten może także stanąć na
drodze programisty, tworząc na przykład zbyt wiele założeń ograniczają-
cych wybór projektu dziedziny lub też tak ciężką implementację, że będzie
to opóźniać programowanie.
Pewnego rodzaju szkielet architektury jest zazwyczaj niezbędny (cho-
ciaż niekiedy zespoły wybierają taki, który niezbyt dobrze im służy). W trak-
cie stosowania szkieletu zespół musi skoncentrować się na swoim zadaniu,
czyli stworzeniu implementacji wyrażającej model dziedziny, którego używa
Antywzorzec inteligentnego
interfejsu użytkownika
Tak właśnie wygląda powszechnie akceptowany wzorzec ARCHITEK-
TURY WARSTWOWEJ dla aplikacji obiektowych. Wprawdzie bardzo
często próbuje się rozdziału interfejsu użytkownika od aplikacji oraz dzie-
dziny, jednak zbyt rzadko jest on w pełni osiągalny. Dlatego odstępstwa
od tego wzorca wymagają odrębnej dyskusji.
Wiele projektów informatycznych podejmuje próbę stosowania znacz-
nie mniej wyszukanego podejścia projektowego, nazywanego przeze mnie
INTELIGENTNYM INTERFEJSEM UŻYTKOWNIKA. Jest to jednak
tylko alternatywna, rozłączna odnoga w rozwoju, niezgodna z podejściem
wymaganym przez projektowanie sterowane dziedziną. Jeżeli ktoś pójdzie
tą drogą, większość treści tej książki nie będzie możliwa do zastosowania.
Najczęściej interesują mnie sytuacje, w których wzorzec INTELIGENT-
NEGO INTERFEJSU UŻYTKOWNIKA nie powinien być stosowany,
dlatego z przekąsem nazywam go „antywzorcem”. Omówienie go w tym
miejscu będzie przydatnym orzeźwieniem i pomoże we wskazaniu przy-
czyn, które w dalszej części tej książki decydują o wyborze trudniejszej
ścieżki projektowania.
***
Projekt ma za zadanie dostarczyć prostą funkcjonalność, zdomino-
waną przez wprowadzanie oraz wyświetlanie danych, z jedynie kilkoma
regułami biznesowymi. W skład zespołu projektowego nie wchodzą osoby
z dużym doświadczeniem w modelowaniu obiektowym.
W przypadku, gdy niedoświadczony zespól pracujący nad
prostym projektem zdecyduje się wypróbować PROJEKTOWANIE
STEROWANE MODELEM z ARCHITEKTURĄ WARSTWOWĄ,
stanie przed koniecznością podjęcia szybkiej i wymagającej nauki.
Członkowie zespołu będą zmuszeni opanować zaawansowane
nowe technologie oraz dać sobie radę z nauką modelowania
obiektowego (co jest wyzwaniem nawet z pomocą tej książki).
Wyrażenie modelu
w programie
107
Czy obiekt reprezentuje cokolwiek, co ma pewną tożsamość lub cią-
głość — coś, czego różne stany lub nawet różne implementacje muszą
być śledzone? Czy może jest atrybutem opisującym stan czegoś innego?
Odpowiedzi na te pytania będą stanowić podstawowe rozróżnienie pomię-
dzy ENCJĄ a WARTOŚCIĄ. Definiowanie obiektów, które w wyraźny
sposób implementują taki czy inny wzorzec, sprawia, że są one bardziej
jednoznaczne, i tworzy podwaliny pod solidny projekt.
Ostatecznie istnieją również pewne aspekty dziedziny, które mogą
być bardziej przejrzyście wyrażone w postaci akcji lub operacji, a nie
obiektu. Chociaż jest to odstępstwo od tradycji modelowania obiekto-
wego, to często takie elementy modelu najlepiej jest wyrazić w postaci
USŁUG, zamiast na siłę dodawać odpowiednią operację do ENCJI lub
WARTOŚCI. USŁUGA jest czynnością wykonywaną na żądanie klienta.
W technicznych warstwach aplikacji istnieje wiele USŁUG. Występują
one również w dziedzinie, w sytuacji gdy modelowana jest aktywność,
która odpowiada czemuś, co program ma wykonać, lecz nie odpowiada to
żadnemu jego stanowi.
Istnieją nieuniknione sytuacje, w których czystość modelu obiek-
towego musi być nadszarpnięta, jak chociażby w przypadku procesu zapisu
do relacyjnych baz danych. W tym rozdziale przedstawione zostaną wska-
zówki, które umożliwią nawigację w tych zagmatwanych realiach i utrzy-
manie się na kursie.
Na koniec dyskusja o MODUŁACH skłoni nas do wniosku, że każda
decyzja projektowa powinna być inspirowana przemyśleniami wynikającymi
z dziedziny. Do projektu można dołączyć również techniczne metryki
definiowane przez pojęcia dużej spójności (ang. high-cohesion) oraz słabego
związania (ang. loose-coupling) obiektów. W PROJEKCIE STEROWANYM
MODELEM MODUŁY stanowią część tego modelu i powinny odzwier-
ciedlać pojęcia zawarte w dziedzinie.
W tym rozdziale zajmiemy się wspólnie wszystkimi tymi elementami
składowymi, tworzącymi model programu. Zyskały już one pewną popu-
larność i dlatego ich oddziaływanie na modelowanie oraz projekt zostały
dobrze opisane dużo wcześniej. Jednak umieszczenie ich w tym kontekście
pomoże programistom stworzyć szczegółowe komponenty, służące nad-
rzędnym ideom projektowania sterowanego dziedziną, co przyda się w przy-
padku problemów wynikających z konieczności tworzenia większych
modeli oraz projektów. Zrozumienie tych podstawowych pryncypiów
pomoże również programistom podążać wytyczonym szlakiem, pomimo
konieczności podejmowania decyzji o nieuniknionych kompromisach.
1
Czyli na przykład zmianę relacji wiele do wielu na jeden do wielu —
przyp. tłum.
ASOCJACJE 109
zmniejsza wzajemną zależność i upraszcza projekt. Zrozumienie dzie-
dziny może spowodować odkrycie naturalnych kierunków zależności.
Podobnie jak w wielu innych krajach, również w Stanach Zjednoczo-
nych było już wielu prezydentów. Jest to zwykła dwukierunkowa relacja
jeden do wielu. Chociaż z drugiej strony, bardzo rzadko zdarzyłoby się
nam wymienić nazwisko „Jerzy Waszyngton” i zadać pytanie: „Jakiego
kraju był on prezydentem?”. Dlatego z przyczyn praktycznych moglibyśmy
ograniczyć tę relację do postaci jednokierunkowej asocjacji, skierowanej
od kraju (ang. country) do prezydenta (ang. president). Takie usprawnienie
w rzeczywistości oddaje naturę dziedziny i usprawnia projekt. Przekazuje
też informację, że jeden kierunek asocjacji ma znacznie większe znacze-
nie niż drugi. W ten sposób zachowuje klasę „Person” (ang. osoba), nie-
zależną od podstawowych założeń klasy „President” (ang. prezydent).
Rysunek 5.1.
Niektóre kierunki
interpretacji
odzwierciedlają
naturalne
właściwości
dziedziny
Rysunek 5.2.
Ograniczenie
asocjacji pozwala
przekazać większą
liczbę informacji
i przyczynia
się do tworzenia
bardziej
praktycznych
projektów
Przykład
Asocjacje w modelu konta inwestycyjnego
Rysunek 5.3.
ASOCJACJE 111
return investments;
}
}
Tabela: BROKERAGE_ACCOUNT
ACCOUNT_NUMBER CUSTOMER_SS_NUMBER
Tabela: CUSTOMER
SS_NUMBER NAME
Tabela: INVESTMENT
Rysunek 5.4.
ASOCJACJE 113
+ customerSocialSecurityNumber + "'";
return QueryService.findSingleCustomerFor(sqlQuery);
}
public Investment getInvestment(String stockSymbol) {
String sqlQuery = "SELECT * FROM INVESTMENT "
+ "WHERE BROKERAGE_ACCOUNT='" + accountNumber + "'"
+ "AND STOCK_SYMBOL='" + stockSymbol +"'";
return QueryService.findInvestmentFor(sqlQuery);
}
}
***
Właścicielka domu pozwała mnie, oskarżając o rzekome poważne
zniszczenia jej własności. Przedstawione mi dokumenty opisywały apar-
tament z dziurami w ścianach, plamami na dywanie, obrzydliwymi substan-
cjami w zlewie, które sprzyjały rozwojowi grzybów, powodujących z kolei
odpadanie tapet kuchennych. Dokumenty sądowe wskazywały mnie jako
lokatora odpowiedzialnego za zniszczenia, identyfikując moją osobę z imie-
nia i nazwiska oraz wskazując mój obecny adres. Było to dla mnie dużym
zaskoczeniem, ponieważ nigdy dotąd nie byłem w pobliżu tego zniszczo-
nego apartamentu.
Po chwili zdałem sobie sprawę, że musi to być przypadek pomylonej
tożsamości. Zadzwoniłem więc do powódki z wyjaśnieniami, lecz wcale
mi nie uwierzyła. Poprzedni najemca od miesięcy umykał jej bezkarnie.
Jak mógłbym więc udowodnić jej, że nie byłem tą osobą, która naraziła
ją na tak kosztowne straty? Przecież byłem jedynym Erikiem Evansem
w książce telefonicznej.
Cóż, ostatecznie to właśnie książka telefoniczna okazała się moim
wybawieniem. Ponieważ od dwóch lat mieszkałem w obecnym aparta-
mencie, spytałem powódkę o to, czy wciąż ma książkę z poprzedniego
2
ENCJA w modelu nie jest tożsama z „ziarnem encyjnym” (ang. entity bean)
występującym w języku Java. U podstaw zdefiniowania ziaren leżało mniej więcej
stworzenie szkieletu do implementacji ENCJI, chociaż ostateczne efekty były różne.
Większość ENCJI jest implementowana w postaci zwykłych obiektów. Nieza-
leżnie jednak od ich sposobów implementacji ENCJE są podstawowym wyróż-
nikiem w modelu dziedziny.
3
Autor odwołuje się do operacji czekowych, dość popularnych w USA.
Odpowiednikiem w Polsce byłyby operacje na tradycyjnych kartach kredyto-
wych, dla których daty obciążeń także różnią się między sobą. Każdy czek ma
również niepowtarzalny numer blankietu — przyp. tłum.
Modelowanie ENCJI
W trakcie modelowania obiektu naturalne wydaje się myślenie o jego atry-
butach, a ważne jest również myślenie o jego zachowaniu. Niemniej jed-
nak najbardziej podstawową odpowiedzialnością ENCJI jest zagwaranto-
wanie ciągłości w taki sposób, aby to zachowanie było jasne i przewidywalne.
Dlatego funkcjonują one najlepiej, gdy są dość proste. Zamiast koncen-
trować się na ich atrybutach lub nawet zachowaniu, warto zminimali-
zować definicję obiektu ENCJI do jej najbardziej naturalnej charaktery-
styki — szczególnie do tych cech, które będą ją identyfikować lub też są
powszechnie używane do jej odszukania lub porównania z innymi obiek-
tami. Należy dodawać tylko tę logikę, która jest niezbędna do zamodelowa-
nia pojęcia, i tylko te atrybuty, które są przez tę logikę wymagane. Poza
tym warto wykonać tę samą czynność w stosunku do logiki oraz atry-
butów innych obiektów związanych z podstawową ENCJĄ. Niektóre
z nich same mogą być ENCJAMI. Inne mogą być WARTOŚCIAMI, które
zostaną omówione jako kolejny wzorzec w tym rozdziale. Oprócz roz-
strzygania problemów z tożsamością ENCJE zazwyczaj wykonują powie-
rzone im zadania, koordynując operacje na zawartych w sobie obiektach.
Na rysunku 5.5 atrybut customerID jest jedynym identyfikatorem
w ENCJI reprezentującej klienta (Customer). Jednak do odnalezienia
klienta bardzo często byłyby również używane atrybuty reprezentujące
jego numer telefonu (ang. contact phone) oraz adres (ang. contact address).
Atrybut określający nazwisko (ang. name) nie stanowi tożsamości osoby,
jest jednak często używany jako jeden ze środków służących do jej okre-
ślenia. W tym przykładzie do klasy Customer zostały dodane atrybuty
phone oraz address, jednak w prawdziwym projekcie wybór, czy postą-
pić w ten sposób, czy inny, zależałby od tego, w jaki sposób najczęściej
klienci należący do tej dziedziny są porównywani lub rozróżniani. Jeżeli
na przykład klient (Customer) ma wiele telefonów kontaktowych do róż-
nych celów biznesowych, wtedy atrybut contact phone nie byłby skoja-
rzony z tożsamością klienta, lecz powinien pozostać w klasie określającej
kontakty przedstawiciela handlowego (Sales Contact).
4
Autor ma na myśli powszechnie stosowaną serializację, czyli przekształcenie
stanu wszystkich atrybutów obiektu do postaci ciągu alfanumerycznego w celu
zapisania do bazy lub przesłania przez sieć — przyp. tłum.
WARTOŚCI 125
analityczny musi koncentrować się jedynie na zdefiniowaniu znaczących
tożsamości oraz wypracowaniu odpornych na błędy metod śledzenia
obiektów w systemach rozproszonych lub bazach danych. Traktowanie
wszystkich sztucznych tożsamości identycznie jest mylące. W ten sposób
zaciemnia się obraz modelu, wrzucając wszystkie obiekty do tego samego
worka.
Czy „Adres”
jest WARTOŚCIĄ? Śledzenie tożsamości ENCJI jest zasadnicze, jednak dołącza-
A kto pyta? nie jej do innych obiektów może znacząco obniżyć wydajność sys-
W programie dla firmy
temu, dodać pracy analitykom oraz zaciemnić model, sprawiając,
wysyłającej zamówienia
pocztą adres potrzebny że wszystkie obiekty będą wyglądać identycznie.
jest w celu Projektowanie oprogramowania jest stałą walką ze złożo-
potwierdzenia
nością. Musimy podejmować decyzje, aby takie specjalne trakto-
płatności kartą
kredytową oraz wanie było stosowane jedynie tam, gdzie jest niezbędne.
zaadresowania paczki. Jeżeli jednak będziemy traktować tę nową kategorię obiek-
Jeżeli jednak
tów jedynie poprzez pryzmat braku tożsamości, wtedy nie dodamy
współlokator również
zamówi paczkę wiele do naszego słownika. W rzeczywistości WARTOŚCI (ang.
z tej samej firmy, value objects) mają w modelu swoją własną charakterystykę i swoje
nie będzie mieć
własne znaczenie. To właśnie te obiekty opisują rzeczy.
znaczenia, że obie
osoby mieszkają pod Obiekt reprezentujący opisowy aspekt dziedziny bez żadnej tożsa-
tym samym adresem. mości z nim związanej nazywany jest WARTOŚCIĄ. Obiekty te two-
Adres będzie
rzone są jedynie w celu reprezentacji elementów projektu, które mają dla
WARTOŚCIĄ.
W programie nas znaczenie tylko z tego powodu, jakie są, a nie kim lub czym są.
dla poczty, mającym Na przykład kolory mogą być WARTOŚCIAMI istniejącymi w pod-
na celu wyznaczenie stawowej bibliotece wielu współczesnych środowisk programistycznych,
trasy doręczenia,
kraj mógłby być podobnie jak cyfry oraz łańcuchy znakowe (ostatecznie nie martwisz się,
zorganizowany której cyfry „4” lub litery „Q” używasz). To są proste przykłady, jednak
w hierarchię WARTOŚCI nie zawsze są proste. Na przykład program do mieszania
regionów, miast,
obszarów dla danego
kolorów mógłby posiadać bogaty model, w którym rozszerzone obiekty
kodu pocztowego kolorów mogłyby być łączone ze sobą w celu otrzymania innych kolo-
i bloków. Hierarchia rów. Mogłyby one dysponować złożonymi algorytmami tworzącymi nowe
ta kończyłaby się
wynikowe WARTOŚCI.
konkretnym adresem.
W takiej sytuacji Sama WARTOŚĆ mogłaby też być złożeniem innych obiektów.
obiekty reprezentujące Na przykład w programie do projektowania planów domów dla każdego
adresy dziedziczyłyby
stylu okna mógłby zostać utworzony niezależny obiekt. Z kolei „styl okna”
swój kod pocztowy
z rodzica w hierarchii, mógłby zostać wpisany do obiektu „okno” wraz z jego szerokością oraz
a gdyby poczta wysokością, jak również regułami sterującymi sposobem, w jaki te atry-
zdecydowała zmienić buty mogłyby być ze sobą łączone oraz zmieniane. Okna są skompliko-
strefy dla kodów
pocztowych, wtedy wanymi WARTOŚCIAMI złożonymi z innych WARTOŚCI. Same okna
zmiana ta dotknęłaby z kolei mogłyby zostać włączone do większych elementów planu, jak cho-
wszystkie adresy w tej ciażby obiektów „ścian”.
strefie. Tutaj adres
byłby ENCJĄ.
Rysunek 5.6.
WARTOŚĆ
może przekazywać
informacje o ENCJI.
Pod względem
konceptualnym
powinna
ona stanowić
spójną całość
***
8
Wzorzec WARTOŚCI CAŁKOWITEJ (ang. whole value) zdefiniowany przez
Warda Cunninghama.
WARTOŚCI 127
Projektowanie OBIEKTÓW WARTOŚCI
Właściwie nie musimy się przejmować, której instancji danej WARTOŚCI
używamy. Ten brak ograniczeń daje nam wolność w projektowaniu, którą
możemy wykorzystać, aby uprościć cały projekt i zoptymalizować jego
wydajność. Obejmuje to wolny wybór pod względem kopiowania, współ-
dzielenia oraz niezmienności tych obiektów.
Jeżeli dwie osoby mają takie samo nazwisko, nie będzie to czynić
ich tą samą osobą ani sprawiać, że będzie można ich używać wymiennie.
Jednak sam obiekt reprezentujący nazwisko może być wymienny, ponie-
waż zajmuje się on tylko kolejnością liter w łańcuchu. Dlatego obiekt
Name może być skopiowany z pierwszego obiektu Person do drugiego.
W rzeczywistości dwa obiekty Person mogą nawet nie potrzebować
własnych instancji obiektu reprezentującego nazwisko. Ten sam obiekt
Name mógłby być współdzielony przez dwa obiekty Person (każdy miałby
wskaźnik do tej samej instancji obiektu Name) bez zmiany ich zachowania
lub tożsamości. A właściwie ich zachowanie byłoby poprawne, dopóki do
nazwiska jednej z osób nie zostałaby wprowadzona jakakolwiek zmiana.
Wtedy nazwisko drugiej osoby również uległoby zmianie! Aby zapobiec
takiej sytuacji i umożliwić bezpieczne współdzielenie obiektów, muszą być
one niezmienne (ang. immutable), tzn. nie mogą ulec zmianie, za wyjąt-
kiem sytuacji, w której zostaną w pełni zastąpione nowymi.
Ta sama sytuacja występuje, gdy obiekt przekazuje jeden ze swoich
atrybutów innemu obiektowi jako argument lub wartość zwracaną. W chwili
gdy jest on poza kontrolą swojego właściciela, wędrującemu w ten sposób
obiektowi mogłoby się przytrafić prawie wszystko. Na przykład WAR-
TOŚĆ mogłaby ulec zmianie w sposób zniekształcający właściciela, cho-
ciażby przez naruszenie reguł jego niezmienników (ang. invariants). Temu
problemowi można zapobiec, definiując przekazywane obiekty jako nie-
zmienne lub przekazując ich kopię.
Przyjrzenie się dodatkowym możliwościom optymalizacji wydajności
może być istotne, ponieważ WARTOŚCI mogą być bardzo liczne. Na
przykład w dużym stopniu polega na nich oprogramowanie do tworzenia
projektów domów. Gdy każde przyłącze elektryczne jest osobną WAR-
TOŚCIĄ, w jednej wersji danego planu domu mogą być ich setki. Jeżeli
jednak potraktujemy wszystkie przyłącza jako wymienne, wtedy mogli-
byśmy współdzielić tylko jedną instancję, do której moglibyśmy odwo-
ływać się setki razy (przykład wzorca pyłku, czyli FLYWEIGHT, zapre-
zentowanego w książce Gammy z roku 1995). W dużych systemach tego
rodzaju pozytywny efekt mógłby zostać wzmocniony tysiąckrotnie, a taka
optymalizacja może być wyróżnikiem pomiędzy systemem przydatnym
WARTOŚCI 129
Jeżeli WARTOŚCI oznaczona jako niezmienna, programiści będą mogli skoncentrować się
nie są zbyt wiele razy w pełni jedynie na technicznych aspektach, takich jak kopiowanie czy
współdzielone
współdzielenie, ponieważ będą mieć pewność, że aplikacja nie polega na
lub można z tego
zrezygnować w celu konkretnych instancjach obiektów.
usprawnienia Zdefiniowanie WARTOŚCI jako niezmiennej podlega następującej
grupowania ogólnej zasadzie: Unikanie zbędnych ograniczeń w modelu pozwala pro-
lub z innych przyczyn
gramistom na wykonywanie czysto technicznych ulepszeń wpływających na
technicznych.
wydajność. Bezpośrednie zdefiniowanie istotnych podstawowych ograni-
Powtórzmy jeszcze raz.
Jeżeli implementacja czeń pozwala programistom ulepszyć projekt przy zachowaniu pewności,
WARTOŚCI że nie zmienią jego istotnego zachowania. Tego rodzaju ulepszenia pro-
ma dopuszczać jektowe są często silnie związane z rodzajem technologii wykorzystywa-
jej zmiany, wtedy
nej w danym projekcie.
nie może być ona
współdzielona.
Niezależnie od tego, Przykład
czy zakładasz
współdzielenie,
Optymalizacja bazy danych
czy nie, wszędzie
gdzie jest to możliwe,
za pomocą WARTOŚCI
staraj się projektować W najniższej warstwie implementacji bazy danych muszą przechowywać
WARTOŚCI jako dane w fizycznej lokalizacji na dysku. Przesunięcie elementów dysku
niezmienne.
i odczytanie danych zajmuje więc pewną ilość czasu. Zaawansowane
systemy bazodanowe starają się grupować fizyczne adresy w ten sposób,
aby powiązane ze sobą dane mogły być odczytane z dysku w pojedynczej
fizycznej operacji odczytu.
Jeżeli do danego obiektu istnieje wiele referencji z innych obiektów,
niektóre z tych obiektów nie będą znajdować się w pobliżu (na tej samej
stronie), co w przypadku operacji odczytu będzie wymagać dodatkowych
fizycznych działań. Dzięki utworzeniu kopii każdej WARTOŚCI zamiast
przekazywania referencji do jej pojedynczej instancji obiekt taki, funk-
cjonujący jako atrybut wielu ENCJI, może być umieszczany na tej samej
stronie co ENCJA, która go wykorzystuje. Taka technika przechowywania
wielu kopii tych samych danych nazywa się denormalizacją i jest często
stosowana, gdy czas dostępu do danych ma większe znaczenie niż prze-
strzeń, jaką one zajmują, lub łatwość ich utrzymania.
W relacyjnej bazie danych mógłbyś chcieć umieścić daną WARTOŚĆ
w tabeli reprezentującej ENCJĘ, do której ta wartość należy, zamiast two-
rzyć asocjację z oddzielną tabelą. W systemie rozproszonym przechowy-
wanie referencji do WARTOŚCI na innym serwerze spowolniłoby prawdo-
podobnie odpowiedzi na komunikaty. W zamian do każdego z serwerów
powinna być przekazywana kopia całego obiektu. Kopie takie możemy
tworzyć bezkarnie, ponieważ mamy do czynienia z WARTOŚCIAMI.
WARTOŚCI 131
USŁUGI
USŁUGI 133
2. Interfejs jest zdefiniowany na warunkach innych elementów z mo-
delu dziedziny.
3. Operacja jest bezstanowa.
Bezstanowość oznacza w tym miejscu, że dowolny klient może uży-
wać dowolnej instancji danej USŁUGI bez względu na historię danej kon-
kretnej instancji. Podczas swojego działania USŁUGA będzie korzystać
z informacji dostępnych globalnie, a nawet może je zmieniać (to znaczy
może mieć efekty uboczne). Niemniej jednak USŁUGA nie posiada swo-
jego własnego stanu, który miałby wpływ na jej działanie, w przeciwień-
stwie do większości obiektów dziedziny.
W sytuacji, gdy znaczący proces lub przekształcenie w dzie-
dzinie nie jest naturalną odpowiedzialnością ENCJI lub WAR-
TOŚCI, dodaj do modelu operację w postaci samodzielnego inter-
fejsu zadeklarowanego jako USŁUGA. Zdefiniuj interfejs na
warunkach określonych przez język modelu i upewnij się, że
nazwa operacji stanowi część JĘZYKA WSZECHOBECNEGO.
Spraw, aby USŁUGA była bezstanowa.
***
USŁUGI 135
Podział usług na poszczególne warstwy
Ziarnistość
Chociaż omawiając ten wzorzec, skoncentrowaliśmy się na uwypukle-
niu zasad modelowania USŁUGI, to możemy go również wykorzystać
w charakterze środka do kontrolowania ziarnistości (ang. granularity) inter-
fejsów w warstwie dziedziny, jak również do oddzielenia klientów od
ENCJI oraz WARTOŚCI. Bezstanowe USŁUGI o średnim poziomie
ziarnistości mogą być łatwiej wykorzystywane w dużych systemach, ponie-
waż hermetyzują ważną funkcjonalność, udostępniając jednocześnie prosty
interfejs. Natomiast w systemach rozproszonych obiekty o dużym roz-
drobnieniu mogą prowadzić do nieefektywnej komunikacji.
Jak omówiliśmy to już poprzednio, drobne obiekty dziedziny mogą
powodować wycieki wiedzy z tej warstwy do warstwy aplikacji, w której
teoretycznie nadzorowane jest działanie obiektów dziedziny. Złożone
i szczegółowe interakcje są zazwyczaj obsługiwane w warstwie aplikacji,
co może spowodować wyciek wiedzy dziedzinowej do kodu aplikacji lub
interfejsu użytkownika, gdzie wiedza ta zostanie oddzielona od warstwy
dziedziny.
Rozważne dodanie usług dziedziny mogłoby pomóc w utrzymaniu
jasnego podziału pomiędzy warstwami. Omawiany wzorzec przedkłada
prostotę interfejsu nad kontrolę przez klienta oraz wszechstronność imple-
mentacji. Definiuje funkcjonalność o średniej ziarnistości, bardzo przy-
Dostęp do USŁUG
Architektury systemów rozproszonych w rodzaju J2EE czy CORBA udo-
stępniają dla USŁUG specjalny mechanizm publikacji, konwencje stoso-
wania, a także metody dostępu do nich oraz ich dystrybucji. Jednak tego
rodzaju szkielety aplikacji nie zawsze są dostępne w projekcie, a nawet
gdyby były, to i tak będą stanowić zbyt duży narzut w przypadku, gdy
chodzi nam jedynie o logiczne rozdzielenie pojęć.
Środki dostępu do USŁUG nie są aż tak istotne, jak decyzje projek-
towe o podziale określonych odpowiedzialności w systemie. Satysfakcjo-
nującą implementację interfejsu USŁUGI może stanowić zwyczajny obiekt
„wykonujący zadanie”. Do udostępnienia USŁUGI może być wykorzy-
stany prosty wzorzec SINGLETON (książka Gammy z roku 1995). Kon-
wencje kodowania mogą w prosty sposób zasugerować, że takie obiekty
będą jedynie służyć dostarczeniu interfejsów USŁUGI, a nie będą mieć
znaczenia jako obiekty dziedziny. Rozbudowane architektury powinny być
wykorzystywane jedynie w przypadku, gdy istnieje rzeczywista potrzeba
utworzenia systemu rozproszonego lub innego wykorzystania możliwości
takiego szkieletu aplikacji.
USŁUGI 137
MODUŁY
(zwane również PAKIETAMI)
MODUŁY (ang. modules) stanowią stary i dobrze poznany element projekto-
wy. Istnieje wiele technicznych powodów ich stosowania, jednak podstawową
przyczyną ich użycia jest zazwyczaj przeciążenie rozmiarem i złożonością ko-
du, uniemożliwiające jego dobre zrozumienie. MODUŁY oferują dwa wido-
ki modelu. Mogą być użyte do spojrzenia na szczegóły funkcjonalności za-
wartej w MODULE bez konieczności analizy reszty systemu lub też służyć
do analizy relacji pomiędzy MODUŁAMI bez wnikania w ich szczegóły.
W warstwie dziedziny MODUŁY powinny pojawiać się jako istotne
części modelu, przekazujące pewną historię z dziedziny ujętą w większej
skali.
***
Wszyscy stosują MODUŁY, lecz tylko nieliczni traktują je
jako pełnoprawną część modelu. W trakcie trwania projektu kod
jest zazwyczaj dzielony na różnego rodzaju kategorie, począwszy
od wydzielenia różnych technicznych aspektów architektury,
a skończywszy na zadaniach przypisanych do poszczególnych
programistów. Fakt podziału kodu na MODUŁY we wczesnej
fazie projektu jest nawet dobrze postrzegany przez samych pro-
gramistów.
Stwierdzenie, że MODUŁY powinny być bardzo spójne i słabo
ze sobą związane, jest naturalnie truizmem. Wprawdzie defini-
cje związania i spójności elementów modelu wydają się brzmieć jak
techniczne metryki, oceniane jedynie według podziału i rozmiesz-
czenia asocjacji oraz interakcji pomiędzy obiektami, jednak należy
mieć na uwadze, iż to nie sam kod jest dzielony na MODUŁY,
a pojęcia dziedziny. Istnieje naturalna granica, definiująca liczbę
rzeczy, o których człowiek może myśleć w danym momencie
(stąd bierze się słabe związanie), a niespójne fragmenty różnych
zagadnień są trudne do zrozumienia pośród innych (stąd duża
spójność).
Ogólnie rzecz biorąc, słabe związanie oraz wysoka spójność są ogól-
nymi pryncypiami projektowymi i mają takie samo zastosowanie do
poszczególnych obiektów, co do MODUŁÓW, jednak na wyższym pozio-
mie ziarnistości modelowania oraz projektowania są one bardziej potrzebne.
Oba te określenia znane są od dłuższego czasu. Jedno z ich wyjaśnień w po-
staci wzorca projektowego dostępne jest w książce Larmana z roku 1998.
Przykład
Standardy kodowania pakietów w Javie
W języku Java dyrektywy import (definiujące zależności) muszą być zade-
klarowane w konkretnej klasie. Osoba tworząca model będzie z pewno-
ścią rozważać pakiety zależne od innych pakietów, jednak taki model nie
może być wyrażony w Javie. Powszechnie stosowane standardy kodowania
zachęcają do importowania określonych klas, co skutkuje powstaniem kodu,
jak poniżej:
ClassA1
import packageB.ClassB1;
import packageB.ClassB2;
import packageB.ClassB3;
import packageC.ClassC1;
import packageC.ClassC2;
import packageC.ClassC3;
. . .
Utrzymywanie PROJEKTU
STEROWANEGO MODELEM
w przypadku łączenia paradygmatów
Silniki reguł stanowią dobry przykład technologii, która jest niekiedy dołą-
czana do projektu aplikacji obiektowej. Model dziedziny bogaty w wiedzę
sam prawdopodobnie zawiera bezpośrednie reguły, chociaż paradygmat
obiektowy nie ma specjalnych konstrukcji semantycznych do wyrażania
reguł oraz ich współdziałania. Pomimo że reguły mogą być zamodelo-
wane w postaci obiektów, co często udaje się zrobić nawet z dobrym
skutkiem, to jednak hermetyzacja właściwa obiektom utrudnia stosowanie
globalnych reguł, możliwych do użycia w całym systemie. Technologia
wykorzystująca silnik reguł wygląda zachęcająco, ponieważ obiecuje dostar-
czać bardziej naturalny i deklaratywny sposób ich definiowania, pozwa-
lając skutecznie łączyć paradygmat regułowy z obiektowym. Paradygmat
logiczny jest dobrze rozwinięty i ma dużo możliwości, a także wydaje się
dobrym uzupełnieniem dla mocnych i słabych stron obiektów.
Niemniej jednak ludzie nie zawsze otrzymują to, czego odczekują od
silników reguł. Niektóre produkty najzwyczajniej nie działają dość dobrze.
Innym brak jest przejrzystego widoku, który mógłby pokazać związki
pojęć modelu występujące pomiędzy tymi dwoma środowiskami imple-
mentacyjnymi. Bardzo częstym efektem takich działań jest aplikacja podzie-
lona na dwie części, tzn. statyczny system do przechowywania danych,
używający obiektów, oraz doraźną aplikację do przetwarzania reguł, pozba-
wioną praktycznie jakichkolwiek połączeń z modelem obiektowym.
Cykl życia
obiektu dziedziny
153
Rysunek 6.1.
Cykl życia
obiektu dziedziny
AGREGATY 155
propagacji potencjalnych efektów takiej dokonanej zmiany. Odświeżanie
każdego obiektu w systemie tylko dlatego, że istnieje jakaś zależność, nie
jest zbyt praktyczne.
Tego rodzaju problem staje się bardzo poważny w systemie umożli-
wiającym jednoczesny dostęp do tego samego obiektu przez wielu klientów.
Mając wielu użytkowników odwołujących się do wielu różnych obiektów
w systemie i je zmieniających, musimy zapobiec jednoczesnym zmianom
współzależnych od siebie obiektów. Ustalenie niewłaściwego zakresu może
mieć poważne konsekwencje.
Bardzo trudno jest zagwarantować spójność zmian do obiek-
tów w modelu ze złożonymi asocjacjami. Konieczne będzie także
utrzymywanie niezmienników, które będą stosować się do blisko
powiązanych grup obiektów, a nie jedynie do konkretnych z nich.
Przezorne zablokowanie schematów może spowodować wchodze-
nie sobie w drogę przez wielu użytkowników, co spowoduje, że
system stanie się bezużyteczny.
Postawmy sprawę inaczej: skąd wiemy, w którym miejscu kończy
się lub zaczyna obiekt złożony z innych obiektów? W każdym systemie
umożliwiającym trwałe zachowywanie danych musi istnieć zakres trans-
akcji je zmieniających, a także sposób na utrzymanie ich spójności (tzn.
utrzymywanie ich niezmienników). Bazy danych udostępniają wiele róż-
nych schematów blokowania, a dodatkowo można zaprogramować testy.
Jednak tego rodzaju tymczasowe rozwiązania odciągają uwagę od modelu,
a w bardzo szybkim tempie mogą też prowadzić do tworzenia różnych
obejść i skrótów.
W rzeczywistości odnalezienie zrównoważonego rozwiązania tego
rodzaju problemów wymaga głębszego zrozumienia dziedziny, tym razem
prowadzącego do odpowiedzi na pytania w rodzaju: jaka jest częstotli-
wość zmian instancji poszczególnych klas? Musimy odnaleźć taki model,
który będzie zmniejszać liczbę miejsc o dużej rywalizacji, a wzmacniać
obszary bezpośrednich niezmienników.
Chociaż sam problem ujawnił się w charakterze technicznych kło-
potów z transakcjami bazodanowymi, to jednak jest on zakorzeniony
w modelu — brak w nim zdefiniowanych granic. Rozwiązanie oparte na tym
modelu uczyni go bardziej zrozumiałym, a sam projekt będzie łatwiejszy
do przekazania. W czasie modyfikacji modelu zmiany będą znajdować też
swoje miejsce w implementacji.
W celu zdefiniowania relacji posiadania w modelu konieczne będzie
utworzenie schematów. Poniższy prosty, jednak rygorystyczny system
1
Ten system odkrył i wykorzystał David Siegel w projektach w latach 90.,
jednak nigdzie go nie opublikował.
AGREGATY 157
strony, blok silnika (ang. engine) ma wygrawerowany numer seryjny i bywa
też niekiedy śledzony niezależnie od samochodu. W niektórych aplikacjach
silnik mógłby więc być korzeniem swojego własnego AGREGATU.
Rysunek 6.2.
Tożsamość lokalna Relacje pomiędzy elementami AGREGATU będą określać niezmien-
a globalna niki (ang. invariants), stanowiące reguły spójności, które muszą być utrzy-
oraz referencje mywane przy każdej zmianie danych. Nie oczekuje się, aby reguła, która
do obiektów
rozciąga się na AGREGAT, była spełniona w każdej dowolnej chwili. Inne
dowolne zależności mogą być rozwiązywane w dowolnym czasie przez
proces przetwarzania komunikatów, przetwarzania wsadowego lub inny.
Jednak każdy z niezmienników zdefiniowanych w AGREGACIE będzie
musiał być spełniony po zakończeniu każdej transakcji.
Aby przekształcić ten pojęciowy AGREGAT w prawdziwą imple-
mentację, musimy w tym momencie utworzyć zestaw reguł stosujących
się do wszystkich transakcji:
ENCJA stanowiąca korzeń AGREGATU ma globalną tożsamość i jest
ostatecznie odpowiedzialna za sprawdzanie jego niezmienników.
ENCJE korzenia mają globalne tożsamości, ENCJE wewnątrz gra-
nicy mają tożsamości lokalne, niepowtarzalne jedynie wewnątrz
AGREGATU.
Nic poza granicą AGREGATU nie może przechowywać referencji do
niczego wewnątrz, za wyjątkiem ENCJI stanowiącej jego korzeń.
Ta ENCJA z kolei może przekazywać referencje do wewnętrznych
ENCJI lub innych obiektów, jednak te obiekty mogą używać ich
jedynie tymczasowo i nie mogą polegać na tych referencjach. Korzeń
AGREGATY 159
jego granic poprzez korzeń. Pozwól zewnętrznym obiektom
utrzymywać jedynie referencje do korzenia. Tymczasowe refe-
rencje do wewnętrznych członków AGREGATU mogą być prze-
kazywane jedynie do chwilowego użycia w pojedynczej operacji.
Ponieważ to korzeń nadzoruje dostęp, nie może być on zaskoczony
żadnymi zmianami do wewnętrznych obiektów. Taka konfiguracja
pomaga usprawnić wymuszanie niezmienników dla obiektów
w AGREGACIE oraz dla samego AGREGATU podczas dowolnej
zmiany stanu.
Przykład
Integralność procesu zakupowego
Zajmijmy się potencjalnymi problemami w uproszczonym systemie obsłu-
gującym proces zakupowy.
Rysunek 6.4.
Model dla systemu
obsługi zamówienia
Rysunek 6.5.
Początkowy stan
zamówienia (PO)
w bazie danych
AGREGATY 161
Rysunek 6.6.
Jednoczesna edycja
Dla obojga użytkowników oraz ich aplikacji wszystko wydaje się być
w różnych w porządku, ponieważ w trakcie transakcji będą oni ignorować zmiany
transakcjach do pozostałych obiektów w bazie danych, a w ich własnych zmianach nie
będą uczestniczyć żadne zablokowane składniki.
Rysunek 6.7.
Ostateczne
zamówienie narusza
zatwierdzony limit
(naruszony
niezmiennik)
Rysunek 6.9.
Przesadne
blokowanie
może utrudniać
pracę ludzi
AGREGATY 163
Utrudnienia wzrastają, ponieważ dla instrumentów („części”) istnieje
wiele zależności. Ostatecznie:
Rysunek 6.10.
Zakleszczenie
Rysunek 6.11.
Cena (ang. price)
jest kopiowana
do składnika
(ang. Line Item)
zamówienia
(ang. Purchase Order).
Umożliwia
to wymuszenie
niezmiennika
AGREGATU
***
AGREGATY zaznaczają zakres, w którym niezmienniki muszą być utrzy-
mywane na każdym etapie cyklu życia. Kolejne wzorce, tj. FABRYKI oraz
REPOZYTORIA, będą działać na AGREGATACH, przesłaniając złożo-
ność konkretnych zmian w cyklu życia.
AGREGATY 165
FABRYKI
W sytuacji, gdy tworzenie obiektu lub całego AGREGATU staje się zbyt
skomplikowane lub odkrywa zbyt wiele tajników wewnętrznej struktury,
przydatne stają się FABRYKI, umożliwiające jej hermetyzację.
***
Duża część potęgi obiektów wynika z ich wewnętrznej konfiguracji,
budowy lub asocjacji. Obiekt powinien być ulepszany do takiego momentu,
kiedy poza nim nie pozostanie już nic, co należałoby do jego znaczenia
lub wspomagałoby go podczas działania. W trakcie swojego cyklu życia ma
on dużo odpowiedzialności. Problem powstaje, gdy złożone obiekty prze-
ciążone zostaną odpowiedzialnością za tworzenie samych siebie.
Silnik jest przykładem skomplikowanej maszynerii z dziesiątkami
części współdziałających ze sobą w celu spełnienia jego odpowiedzialności,
tj. przekręcenia korbowodu. Można sobie wyobrazić projekt bloku silnika,
który sam wziąłby zestaw tłoków i umieścił je w cylindrach lub świecy
zapłonowej, która odszukałaby swoje gniazdo i sama się wkręciła. Jednak
wydaje się mało prawdopodobne, aby tak skomplikowana maszyna była
tak niezawodna i wydajna, jak typowe silniki. Zamiast tego zgadzamy się
na to, że coś innego poskłada nam elementy silnika. Być może będzie to
człowiek, a może robot przemysłowy. W rzeczywistości zarówno robot,
FABRYKI 167
obiektu wcale nie mają dla niej znaczenia. Są one niezbędne jedynie
z punktu widzenia implementacji. Aby rozwiązać ten problem, musimy
dodać do projektu dziedziny struktury, które nie będą ani ENCJAMI, ani
WARTOŚCIAMI, ani USŁUGAMI. Będzie to odstępstwo od poprzed-
niego rozdziału i w tym miejscu istotne jest, aby postawić sprawę jasno:
dodajemy do projektu elementy, które nie będą odpowiadać żadnej struk-
turze w modelu, jednak będą mimo to stanowić odpowiedzialność war-
stwy dziedziny.
Każdy język obiektowy zawiera mechanizm tworzenia obiektów
(na przykład konstruktory w Javie lub C++ czy metody klas tworzące
instancje w Smalltalku), jednak potrzebny jest bardziej abstrakcyjny mecha-
nizm do tworzenia obiektów, który będzie rozłączny z innymi obiektami.
Element programu, którego odpowiedzialnością jest tworzenie innych
obiektów, nosi nazwę FABRYKI.
Rysunek 6.12.
Podobnie jak interfejs obiektu powinien hermetyzować jego imple-
Proste interakcje
z FABRYKĄ mentację, umożliwiając klientowi używanie logiki obiektu bez wiedzy,
jak on działa, tak FABRYKA hermetyzuje wiedzę niezbędną do tworzenia
złożonych obiektów lub AGREGATU. Udostępnia ona interfejs repre-
zentujący zadania klienta oraz abstrakcyjny obraz tworzonego obiektu.
Dlatego:
Przenieś odpowiedzialność za tworzenie instancji złożonych
obiektów oraz AGREGATÓW do oddzielnego obiektu, który może
sam nie mieć żadnej odpowiedzialności modelu dziedziny, jed-
nak będzie wciąż częścią jej projektu. Udostępnij interfejs, który
będzie hermetyzować cały złożony proces montażu oraz nie będzie
wymagać od klienta odwoływania się do konkretnych klas tworzo-
nych obiektów. Twórz całe AGREGATY jako całość, wymuszając
ich niezmienniki.
Istnieje wiele sposobów projektowania FABRYK. Niektóre służące
do tego wzorce ogólnego przeznaczenia, np. METODA WYTWÓRCZA
(ang. factory method), FABRYKA ABSTRAKCYJNA (ang. abstract factory)
FABRYKI 169
Rysunek 6.13.
METODA
gdy dane lub prawdopodobnie również reguły jednego obiektu domi-
WYTWÓRCZA
hermetyzuje
nują w procesie tworzenia innego obiektu, takie podejście uchroniłoby
rozszerzenie przed wyciekiem informacji z obiektu tworzącego i ich użyciem poza nim.
AGREGATU Co więcej, taki model przekazywałby również specjalny związek pomiędzy
obiektem tworzącym inny produkt a tym produktem.
Na rysunku 6.14 obiekt Trade Order (zlecenie transakcji) nie
jest częścią tego samego AGREGATU co Brokerage Account (konto
inwestycyjne), ponieważ od początku będzie w interakcji z aplikacją wyko-
nującą zlecenia, której obiekt Brokerage Account stałby tylko na drodze.
Nawet wówczas jednak wydawałoby się naturalne, aby to obiekt Broke-
rage Account nadzorował tworzenie obiektów Trade Order. To właśnie
obiekt konta inwestycyjnego zawiera w sobie informacje, które zostaną
przekazane do zlecenia transakcji (począwszy od jego własnej tożsamości),
a także reguły definiujące, które operacje maklerskie są dopuszczalne.
Ukrycie implementacji zlecenia mogłoby również przynieść nam inne
korzyści. Na przykład obiekt ten mógłby zostać zrefaktorowany do postaci
hierarchii z oddzielnymi podklasami Buy Order (transakcja kupna) oraz
Sell Order (transakcja sprzedaży). FABRYKA umożliwia utrzymanie
klienta z dala od konkretnych klas.
Rysunek 6.15.
Samodzielna
Kiedy potrzebujesz jedynie konstruktora FABRYKA tworzy
Widziałem już zbyt wiele kodu, w którym wszystkie instancje tworzone AGREGAT
były poprzez bezpośrednie wywołanie konstruktorów klas lub jakiejkol-
wiek innej podstawowej metody tworzenia instancji dostępnej w danym
języku programowania. Wprowadzenie FABRYK niesie ze sobą wiele
korzyści, które zazwyczaj nie są w pełni wykorzystywane. Chociaż nie-
kiedy bezpośredniość konstruktora czynić będzie z niego najlepszy wybór,
w rzeczywistości FABRYKI mogą zaciemnić proste obiekty, które nie będą
wymagać polimorfizmu.
FABRYKI 171
W następujących okolicznościach warto pójść na kompromis i użyć
prostych publicznych konstruktorów:
Klasa jest typem. Nie stanowi ona części żadnej interesującej hierarchii
i nie jest używana polimorficznie do implementacji interfejsu.
Klient troszczy się o implementację, być może jako jeden ze sposo-
bów wyboru STRATEGII.
Wszystkie atrybuty obiektu dostępne są dla klienta, dlatego w kon-
struktorze wystawionym do klienta nie mieści się żadna logika two-
rzenia obiektu.
Proces tworzenia nie jest skomplikowany.
Konstruktor publiczny musi spełniać te same reguły co FABRYKA.
Musi być atomową operacją spełniającą wszystkie niezmienniki two-
rzonego obiektu.
Unikaj wywoływania konstruktorów wewnątrz innych konstrukto-
rów klas. Konstruktory powinny być bardzo proste. Złożone konstrukcje,
szczególnie w przypadku AGREGATÓW, wymagają FABRYK. Nie
potrzeba wiele, aby użyć prostej METODY WYTWÓRCZEJ.
Biblioteka klas Javy zawiera ciekawe przykłady. Wszystkie kolekcje
implementują interfejsy oddzielające klienta od konkretnej implementacji.
Wciąż jednak wszystkie one są tworzone poprzez bezpośrednie wywołania
konstruktorów. Hierarchia kolekcji mogłaby zostać zahermetyzowana przez
FABRYKĘ. Metody tej FABRYKI mogłyby umożliwić klientowi odpytanie
o funkcjonalności, których będzie on potrzebować, ale to jednak ostatecznie
FABRYKA mogłaby wybierać, z której konkretnie klasy skorzystać. W takim
przypadku kod tworzący kolekcje byłby bardziej wymowny, a nowe klasy
kolekcji mogłyby być dodawane bez konieczności modyfikowania każ-
dego programu Javy.
Istnieje jednak przypadek faworyzujący bezpośrednie konstruktory.
Po pierwsze, dokonany wybór implementacji może mieć wpływ na wydaj-
ność dla wielu aplikacji, dlatego też to aplikacja mogłaby chcieć kontro-
lować cały proces (chociaż naprawdę sprytna FABRYKA mogłaby wciąż
zająć się takimi przypadkami). Ostatecznie nie istnieje aż tak wiele klas
kolekcji, więc wybór nie jest bardzo trudny.
Pomimo braku FABRYKI abstrakcyjne typy kolekcji wciąż zacho-
wują pewne wartości, ponieważ istnieją specjalne wzorce ich użycia. Bardzo
często kolekcje tworzone są w jednym miejscu, a używane w innym.
Oznacza to, że klient, który będzie ostatecznie używać kolekcji (dodając,
usuwając i pobierając jej zawartość), wciąż będzie mógł komunikować się
Projektowanie interfejsu
Podczas projektowania sygnatury metody FABRYKI, czy to w przypadku
samodzielnej FABRYKI, czy też METODY WYTWÓRCZEJ, należy mieć
na uwadze następujące dwa czynniki:
Każda operacja musi być atomowa. Wszystkie informacje potrzebne do
utworzenia kompletnego produktu muszą być przekazane podczas
jednego odwołania do FABRYKI. Należy również zdecydować
o zachowaniu w sytuacji wystąpienia błędu podczas tworzenia obiektu,
jeżeli pewne niezmienniki nie zostaną spełnione. W takim przypadku
można zwrócić wyjątek lub po prostu wartość null. W celu zacho-
wania spójności należy rozważyć użycie standardu kodowania do
obsługi błędów w FABRYCE.
FABRYKA będzie związana ze swoimi argumentami. Jeżeli nie zachowasz
ostrożności podczas wyboru parametrów wejściowych, możesz stwo-
rzyć całą sieć zależności. Stopień dowiązania będzie zależny od tego,
co zrobisz z argumentami. Jeżeli zostaną one po prostu przekazane
do produktu, wtedy zależność będzie umiarkowana. Jeżeli jednak
wybierzesz część argumentów, aby użyć ich w trakcie tworzenia,
wtedy zależność będzie wzrastać.
Najbezpieczniejszymi parametrami są te z najniższej warstwy projek-
towania. Nawet w danej warstwie istnieje naturalny podział, w którym
prostsze obiekty są używane przez obiekty wyższego poziomu (tego rodzaju
podziały będą omówione na różne sposoby w rozdziale 10., „Projekt
elastyczny”, oraz ponownie w rozdziale 16., „Struktury dużej skali”).
Inną dobrą strategią dla parametrów jest użycie obiektu blisko zwią-
zanego w modelu z oczekiwanym produktem w taki sposób, aby nie była
dodawana żadna nowa zależność. W poprzednim przykładzie zamówienia
zakupowego (ang. Purchase Order) METODA WYTWÓRCZA przyjmuje
jako argument obiekt Catalog Part (ang. artykuł), który stanowi podsta-
wową asocjację ze składnikiem zamówienia (ang. item). Taka implementacja
tworzy bezpośrednią zależność pomiędzy klasami Purchase Order oraz
Part. Jednak wszystkie te trzy obiekty tworzą pojęciową grupę. AGREGAT
zamówienia i tak zawierał już przecież obiekt Catalog Part. Dlatego też
oddanie kontroli do korzenia AGREGATU i hermetyzacja jego wewnętrz-
nej struktury są dobrym kompromisem.
FABRYKI 173
Używaj abstrakcyjnych typów argumentów, a nie ich konkretnych
klas. FABRYKA jest i tak związana z konkretnymi klasami produktów, więc
nie musi być także związana z konkretnymi parametrami.
FABRYKI 175
Na rysunkach 6.16 oraz 6.17 przedstawione są dwa rodzaje odtwa-
rzania obiektu. W przypadku odtwarzania z baz danych część usług może
być dostarczana przez technologie mapowania obiektowo-relacyjnego, co
samo w sobie jest bardzo wygodne. Niezależnie od tego, czy istnieje
widoczna złożoność w odtwarzaniu obiektu z innego medium, użycie
FABRYKI wydaje się dobrym wyborem.
Rysunek 6.16.
Odtwarzanie
ENCJI pobranej
z relacyjnej bazy
danych
Rysunek 6.17.
Odtwarzanie
ENCJI przekazanej
jako XML
FABRYKI 177
REPOZYTORIA
REPOZYTORIA 179
technologii. Jednak w chwili, gdy programista utworzy zapytanie SQL,
przekaże je do usługi w warstwie infrastruktury, otrzyma zestaw rezul-
tatów oparty na wierszach z tabeli, wyciągnie z nich niezbędne informacje
i przekaże je do konstruktora lub FABRYKI, model prawdopodobnie już
zniknie z widoku. Naturalne wydaje się traktowanie obiektów jako konte-
nerów dla danych zwracanych przez zapytania, a cały projekt nabiera cech
właściwych dla stylu przetwarzania danych. Szczegóły technologiczne mogą
się zmieniać, jednak pozostaje podstawowy problem, wynikający z faktu,
że klient używa technologii, a nie pojęć modelu. Bardzo pomocne mogą
okazać się dodatki do infrastruktury w rodzaju WARSTW MAPOWANIA
METADANYCH (Fowler 2003), ponieważ ułatwiają przekształcenie
rezultatu zapytania do obiektów. Niemniej jednak programista wciąż
będzie myśleć nad mechanizmami technologicznymi, a nie dziedziną.
Co gorsze, ponieważ kod klienta używa bazy danych, programistów w bez-
pośredni sposób może kusić chęć obejścia takich konstrukcji modelu jak
AGREGATY lub nawet rezygnacja z hermetyzacji oferowanej przez
obiekty i w zamian bezpośrednie odczytywanie i obróbka danych, których
potrzebują. Powoduje to, że coraz więcej reguł dziedziny jest zachowy-
wanych w kodzie zapytania i najzwyczajniej traconych. Obiektowe bazy
danych eliminują wprawdzie problem konwersji danych, jednak ich mecha-
nizmy przeszukiwania są wciąż bardzo techniczne, co sprawia, że progra-
miści są wciąż kuszeni możliwością bezpośredniego odczytania obiektów,
których potrzebują.
Klient wymaga praktycznych środków pozwalających na
otrzymanie referencji do istniejących wcześniej obiektów dzie-
dziny. Jeżeli rozwiązania infrastrukturalne umożliwiają to zbyt
łatwo, wtedy programiści klienta mogą dodać więcej asocjacji,
zaciemniając model. Z drugiej strony, mogą oni używać zapytań,
aby odczytać z bazy danych bezpośrednio te dane, których potrze-
bują, lub też odtworzyć tylko kilka konkretnych obiektów, zamiast
przeszukiwać AGREGAT, począwszy od jego korzenia. Logika
dziedziny przenoszona jest do kodu zapytań oraz klienta, a ENCJE
oraz WARTOŚCI stają się zaledwie kontenerami dla danych. Zwy-
kła złożoność techniczna związana z dostępem do większości baz
danych bardzo szybko zagmatwa kod klienta, co będzie skłaniać
programistów do ograniczenia warstwy dziedziny, a to z kolei
uczyni model nieistotnym.
Korzystając z omówionych do tej pory pryncypiów projektowych,
możemy nieco zmniejszyć zakres problemów z dostępem do obiektów,
o ile znajdziemy taką metodę dostępu, która będzie utrzymywać na tyle
dużą wyrazistość modelu, aby te pryncypia zastosować. Na początku nie
REPOZYTORIA 181
oraz AGREGATÓW. Udostępnienie technicznych mechanizmów
dostępu do bazy danych komplikuje klienta i zaciemnia PROJEKT
STEROWANY MODELEM.
Istnieją też techniki ratunkowe, które pomagają w radzeniu sobie
z wyzwaniami technicznymi dostępu do bazy danych. Należą do nich na
przykład: hermetyzacja zapytań SQL w OBIEKTACH ZAPYTAŃ lub
wykonywanie tłumaczenia pomiędzy obiektami a tabelami przy użyciu
WARSTW MAPOWANIA METADANYCH (Fowler 2003). FABRYKI
mogą pomóc w odtworzeniu zachowanych obiektów (jak omówiliśmy
to wcześniej w tym rozdziale). Te oraz wiele innych technik pomagają
w trzymaniu złożoności w ryzach.
Jednak mimo to zwróć uwagę na wszystko, co utraciliśmy. Nie
myślimy już przecież o pojęciach z naszego modelu dziedziny. Nasz kod
nie będzie przekazywał żadnych informacji biznesowych. Będzie on jedynie
manipulował technologią służącą do odczytywania danych. Wzorzec
REPOZYTORIUM jest prostym szkieletem pojęciowym, pozwalającym
na hermetyzację wszystkich tych rozwiązań i ponowne skupienie się na
modelu.
REPOZYTORIUM reprezentuje wszystkie obiekty danego typu,
traktowane jako jeden pojęciowy zestaw (zazwyczaj emulowany). Działa on
jako kolekcja, za wyjątkiem bardziej rozbudowanej możliwości tworzenia
zapytań. Obiekty określonego typu są dodawane i usuwane, a rozwiązania
stosowane w REPOZYTORIUM wstawiają je do bazy danych lub z niej
usuwają. Taka definicja gromadzi spójny zestaw odpowiedzialności, udo-
stępniający dostęp do korzeni AGREGATÓW od bardzo wczesnej fazy
cyklu życia do jego końca.
Klient żąda obiektów z REPOZYTORIUM przy użyciu metod zapy-
tań, które z kolei wybierają obiekty na podstawie kryteriów określonych
przez klienta, zazwyczaj wartości pewnych atrybutów. REPOZYTORIUM
odczytuje żądane obiekty, hermetyzując całą mechanikę zapytań do bazy
danych oraz mapowania metadanych. REPOZYTORIA mogą implemento-
wać szereg różnych zapytań, pozwalających na wybór obiektów na podstawie
jakichkolwiek kryteriów, których klient sobie zażyczy. Mogą one nawet
zwracać wyniki obliczeń, jak na przykład całkowitą sumę numerycznego
atrybutu, obliczoną na podstawie wszystkich dopasowanych obiektów.
REPOZYTORIUM zdejmuje z klienta wielki ciężar, ponieważ może
on teraz komunikować się prostym, odzwierciedlającym intencje progra-
misty interfejsem i odpytywać o wszystko, czego potrzebuje w kontekście
modelu. Wszystkie te operacje będą wymagać wiele złożonych działań
na infrastrukturze, jednak interfejs będzie prosty i pojęciowo związany
z dziedziną.
REPOZYTORIA 183
Odpytywanie REPOZYTORIUM
Wszystkie repozytoria udostępniają metody pozwalające klientowi na zażą-
danie obiektów spełniających pewne kryteria. Istnieje jednak cały szereg
sposobów projektowania takiego interfejsu.
Najprostsze możliwe do utworzenia REPOZYTORIUM dysponuje
jedynie zakodowanymi na stałe zapytaniami z określonymi parametrami.
Te zapytania mogą być różne, na przykład: odczytywanie ENCJI przy
użyciu jej tożsamości (to udostępniają zazwyczaj prawie wszystkie REPO-
ZYTORIA), odczytywanie kolekcji obiektów z określoną wartością atry-
butu lub złożoną kombinacją parametrów, wybór obiektów na podstawie
zakresu wartości (na przykład dat), a nawet wykonywanie pewnych obli-
czeń, które należą do ogólnej odpowiedzialności REPOZYTORIUM
(szczególnie bazując na operacjach wspieranych przez leżącą poniżej bazę
danych).
Chociaż większość zapytań zwraca obiekt lub kolekcję obiektów, to
jednak poprawne jest również zwrócenie pewnych rodzajów wyników
obliczeń, jak na przykład liczby obiektów lub też sumy atrybutów nume-
rycznych, które osoba tworząca model uznała za istotne.
Rysunek 6.19.
Zakodowane Zakodowane na stałe zapytania mogą zostać utworzone na prawie
na sztywno dowolnej infrastrukturze i bez konieczności ponoszenia dużych wydatków,
zapytania ponieważ i tak będą one wykonywać to samo, co robiłby klient.
w prostym
W przypadku projektów mających wiele zapytań może zostać utwo-
REPOZYTORIUM
rzony szkielet REPOZYTORIUM, umożliwiający wykonywanie bardziej
elastycznych zapytań. Będzie to wymagać zespołu zaznajomionego z daną
technologią i może być bardzo wspierane przez możliwości wybranej
infrastruktury.
Jednym z bardziej trafnych uogólnień REPOZYTORIÓW w postaci
szkieletu jest użycie zapytań opartych na SPECYFIKACJI. Umożliwia ona
klientowi opisanie (to znaczy wyspecyfikowanie) tego, czego będzie on
potrzebować, bez martwienia się, w jaki sposób może to zostać uzyskane.
W tym procesie tworzony jest obiekt, który w rzeczywistości wprowadza
w życie daną selekcję. Ten wzorzec zostanie szerzej omówiony w roz-
dziale 9.
REPOZYTORIA 185
i odkrył problem. W jednym miejscu program podsumowywał pewną
informację o każdym elemencie fabryki. Programista zaimplementował
to w zapytaniu nazwanym „wszystkie obiekty”, które miało za zadanie
utworzenie każdego z obiektów, a następnie odczytanie z niego potrzeb-
nych informacji. Konsekwencją działania tego kodu było jednoczesne
wczytanie do pamięci całej bazy danych. Problem nie został wykryty
w trakcie testów, ponieważ danych testowych było niewiele.
To oczywiście niedopuszczalna praktyka, lecz poważne problemy
mogą sprawić nawet bardziej subtelne przeoczenia. Programiści muszą
zrozumieć konsekwencje używania zahermetyzowanej logiki. Oczywiście,
nie musi to oznaczać szczegółowej znajomości implementacji. Dobrze
zaprojektowane komponenty mogą być opisane (to będzie jeden z głów-
nych tematów rozdziału 10., „Projekt elastyczny”).
Jak wspominaliśmy już w rozdziale 5., wybrana technologia bazowa
może ograniczać dostępne możliwości podczas modelowania. Na przy-
kład relacyjna baza danych może nałożyć praktyczny limit na struktury
obiektowe o dużej złożoności. Dokładnie tak samo musi istnieć sposób
przepływu informacji do programistów w obu kierunkach z obszaru
pomiędzy REPOZYTORIUM a fizyczną implementacją zapytań.
Implementacja REPOZYTORIUM
W zależności od technologii wybranej do zapewniania trwałości danych
oraz posiadanej infrastruktury, implementacja REPOZYTORIUM może
znacznie się różnić. Idealnym rozwiązaniem byłoby ukrycie wszystkich
wewnętrznych szczegółów przed klientem (jednak nie przed programistą
tego klienta) w taki sposób, aby jego kod był niezmienny, niezależnie od
tego, czy dane są zachowywane w obiektowej bazie danych, relacyjnej bazie
danych, czy najzwyczajniej w pamięci. W celu wykonania zadania REPO-
ZYTORIUM będzie polegać na odpowiednich usługach infrastruktury.
Hermetyzacja mechanizmu składowania, odczytywania i odpytywania jest
najbardziej podstawową funkcjonalnością dla każdej implementacji
REPOZYTORIUM.
Pojęcie REPOZYTORIUM może być zaadaptowane do wielu różnych
sytuacji. Możliwości implementacji są tak rozległe, że mogę w tym miejscu
wymienić tylko kilka zagadnień, o których należy pamiętać:
Utwórz abstrakcję typu. REPOZYTORIUM „zawiera” wszystkie
instancje określonego typu, jednak to nie oznacza, że dla każdej klasy
będziemy potrzebować jednego REPOZYTORIUM. Typ mógłby być
REPOZYTORIA 187
dysponuje kontekstem umożliwiającym poprawne rozpoczynanie oraz
kończenie jednostek pracy. Zarządzanie transakcjami będzie prostsze,
jeżeli REPOZYTORIUM będzie trzymać się od tego z daleka.
Zazwyczaj zespoły dodają do warstwy infrastruktury szkielet wspie-
rający implementację REPOZYTORIÓW. Oprócz współpracy z niższą
warstwą komponentów infrastruktury klasa bazowa REPOZYTORIUM
mogłaby implementować proste zapytania (szczególnie wtedy, gdy sto-
sowane są uniwersalne zapytania). Niestety, w systemie typów, takim jak
Java, tego rodzaju podejście zmuszałoby do zrzucenia obiektów typowa-
nych jako „Object”, pozostawiając klientowi zadanie rzutowania go na
typ danych przechowywany w REPOZYTORIUM. Oczywiście, w Javie
i tak musiałoby to być zrobione w zapytaniach zwracających kolekcje.
Pewne wskazówki na temat implementacji REPOZYTORIÓW,
a także kilka wspierających je technicznych wzorców w rodzaju OBIEKTU
ZAPYTAŃ znaleźć można w książce Fowlera (2003 r.).
Relacje z FABRYKAMI
FABRYKA zajmuje się początkiem życia obiektu, a REPOZYTORIUM
pomaga obsłużyć jego środek oraz koniec. Kiedy obiekty są trzymane
w pamięci lub przechowywane w obiektowej bazie danych, wszystko jest
dość proste. Jednak zazwyczaj konieczne jest przechowywanie przynajm-
niej niektórych z nich w relacyjnych bazach danych, plikach lub innych
systemach nieobiektowych. W takich przypadkach odczytane dane muszą
być odtworzone do postaci obiektowej.
Ponieważ w takim przypadku REPOZYTORIUM tworzy obiekty na
podstawie danych, wiele osób uznaje je za FABRYKĘ i z technicznego
punktu widzenia ma rację. Jednak najbardziej przydatne okazuje się posił-
kowanie się modelem, w którym — tak jak wspomnieliśmy wcześniej —
odtworzenie zachowanego obiektu nie jest tworzeniem nowego. W tego
rodzaju sterowanym dziedziną widoku projektu FABRYKI oraz REPOZY-
TORIA mają inne odpowiedzialności. FABRYKA tworzy nowe obiekty,
a REPOZYTORIUM odnajduje stare. Klient REPOZYTORIUM powi-
nien mieć wrażenie, że obiekty znajdują się w pamięci. Może zaistnieć
konieczność odtworzenia obiektu (tak, będzie to powodowało utworzenie
nowej instancji), jednak wciąż będzie to ten sam znaczeniowo obiekt,
nadal znajdujący się w środku swojego cyklu życia.
Te dwa widoki pojęciowe mogą być uwspólniane poprzez oddelegowanie
przez REPOZYTORIUM tworzenia obiektów do FABRYKI, co (w teorii,
lecz rzadko w praktyce) mogłoby być używane również do tworzenia
obiektów od początku.
Rysunek 6.22.
REPOZYTORIUM
używa FABRYKI
do odtworzenia
istniejącego
wcześniej obiektu
REPOZYTORIA 189
Taki jasny podział zadań uwolni też FABRYKI od jedynej odpowie-
dzialności za dbanie o trwałość obiektów. Zadaniem FABRYKI jest prze-
cież utworzenie potencjalnie złożonego obiektu z danych. Jeżeli produkt
jest nowym obiektem, wtedy klient będzie o tym wiedział i może dodać go
do REPOZYTORIUM, które z kolei będzie hermetyzować proces zacho-
wania obiektu w bazie danych.
Rysunek 6.23.
Klient używa
REPOZYTORIUM
do zachowania
nowego obiektu
Projektowanie obiektów
dla relacyjnych baz danych
Najbardziej powszechnym nieobiektowym elementem obiektowych sys-
temów jest relacyjna baza danych. Rzeczywistość odkrywa wiele przypad-
ków łączenia paradygmatów (patrz rozdział 5.). Jednak baza danych jest
Użycie języka
— przykład
rozszerzony
195
1. Śledzenie kluczowych etapów obsługi ładunku klienta.
2. Wcześniejsza rezerwacja ładunku.
3. Automatyczne wysyłanie faktur do klientów w chwili, gdy ładunek
osiąga określony etap obsługi.
W rzeczywistym projekcie dojście do tego rodzaju przejrzystości
modelu zabiera trochę czasu. W części III tej książki zajmiemy się szcze-
gółowo odkrywaniem tego projektu. W tym miejscu jednak rozpoczniemy
od modelu, który posiada już wszystkie powyższe zagadnienia przedsta-
wione w rozsądnej postaci, a następnie skoncentrujemy się na dopraco-
waniu szczegółów wspierających jego późniejsze utrzymanie.
Rysunek 7.1.
Diagram klas
przedstawiający
model dziedziny
logistyki morskiej
Izolowanie dziedziny
— wprowadzenie aplikacji
Aby zapobiec mieszaniu się odpowiedzialności dziedziny z tymi z innych
części systemu, do wyznaczenia warstwy dziedziny skorzystajmy z ARCHI-
TEKTURY WARSTWOWEJ.
Projektowanie asocjacji
w dziedzinie logistyki morskiej
Żadna z asocjacji na oryginalnym diagramie nie narzuca kierunku relacji,
jednak te z nich, które są dwukierunkowe, będą dość problematyczne.
Kierunek działania asocjacji wychwytuje często wiedzę z dziedziny, tym
samym pogłębiając jej model.
Jeżeli klient (Customer) ma bezpośrednią referencję do każdego
ładunku (Cargo), który wysłał, to w przypadku długoterminowych powra-
cających klientów może się to okazać kłopotliwe. Także pojęcie klienta nie
jest specyficzne dla ładunku. W dużych systemach klienci mogą mieć
role wykorzystujące wiele obiektów. Najlepiej więc nie wiązać się kon-
kretnymi odpowiedzialnościami. Jeżeli potrzebowalibyśmy możliwości
odnalezienia ładunków klienta, mogłoby to zostać udostępnione przez zapy-
tanie do bazy danych. Do tego problemu powrócimy w dalszej części roz-
działu, w podrozdziale opisującym REPOZYTORIA.
Gdyby nasza aplikacja śledziła poszczególne statki, wtedy przejście
od operacji spedycyjnej do operacji obsługi byłoby istotne. Jednak nasz
model biznesowy zakłada jedynie śledzenie ładunku. To zrozumienie
biznesu wychwytuje jednokierunkowa asocjacja od operacji obsługi (Han-
dling Event) do operacji spedycyjnej (Carrier Movement). Ogranicza
to również implementację do prostej referencji do obiektu, ponieważ
kierunek został już narzucony przez operator mnogości na diagramie.
Uzasadnienie dalszej części dyskusji wynika z rysunku 7.2
Granice AGREGATU
Klasy Customer, Location oraz Carrier Movement mają swoje własne
tożsamości i są współdzielone przez wiele obiektów klasy Cargo, dlatego
też muszą stanowić korzeń własnych AGREGATÓW, które będą z kolei
zawierać ich atrybuty lub być może nawet inne obiekty, niewystępujące
w dyskusji na tym poziomie szczegółowości.
Obiekt Cargo jest również oczywistym korzeniem AGREGATU,
jednak musimy chwilę zastanowić się, gdzie narysować jego granicę.
AGREGAT Cargo mógłby obejmować wszystko, co istnieje, jednak co
zrobić z konkretnym ładunkiem, który zawiera historię podróży (Delivery
History), specyfikację podróży (Delivery Specification) oraz operacje
obsługi (Hadling Events)? Do ładunku pasowałaby historia podróży, gdyż
przecież nikt nie przeszukiwałby jej bez posiadania referencji do samego
obiektu ładunku. W granice ładunku wpasowuje się również ładnie klasa
Delivery History, która nie wymaga globalnej metody dostępu, a jej toż-
samość jest po prostu odziedziczona z klasy Cargo. Nie musi ona być
również korzeniem agregatu. Obiekt Delivery Specification jest WAR-
TOŚCIĄ, więc nie istnieją żadne przeciwwskazania dotyczące włączenia
jej do AGREGATU ładunku.
Klasa Handling Event (operacja obsługi) jest zupełnie innym przy-
padkiem. Poprzednio rozważaliśmy dwa potencjalnie zapytania, które
mogłyby odszukiwać obiekty tej klasy. Pierwsze z nich wyszukiwało
obiekty Handling Events dla danego obiektu Delivery History (historia
podróży) i było potencjalną alternatywą dla kolekcji, umieszczoną lokal-
nie w AGREGACIE Cargo. Drugie byłoby stosowane do odnalezienia
wszystkich operacji załadunku i przygotowania określonej operacji spe-
dycyjnej (Carrier Movement). W tym drugim przypadku wydaje się, że
obsługa ładunku ma też pewne znaczenie nawet w przypadku jej rozpa-
trywania poza samą klasą Cargo. Dlatego klasa Handling Event powinna
stanowić korzeń swojego własnego AGREGATU.
Wybór REPOZYTORIÓW
W projekcie występuje pięć ENCJI będących korzeniami AGREGATÓW,
dlatego też nasze rozważania ograniczyć możemy wyłącznie do nich,
ponieważ żaden inny obiekt nie może posiadać REPOZYTORIÓW.
Aby zdecydować, który z tych kandydatów powinien w rzeczywi-
stości posiadać REPOZYTORIUM, musimy cofnąć się do wymagań apli-
kacji. W celu dokonania rezerwacji w aplikacji do rezerwacji (Booking
Application) użytkownik musi wybrać klienta(ów) odgrywającego(ych)
różne role (nadawca, odbiorca itp.). Dlatego też będziemy potrzebować
repozytorium klientów (Customer Repository). W celu określenia
miejsca przeznaczenia ładunku (Cargo) będziemy również musieli odnaj-
dować lokalizacje (Location), dlatego też utworzymy ich repozytorium
(Location Repository).
Aplikacja do rejestrowania incydentów musi umożliwiać użytkow-
nikowi wejrzenie do szczegółów operacji spedycyjnej (Cargo Movement),
w której uczestniczy dany ładunek (Cargo). Dlatego też będziemy potrze-
bować repozytorium operacji spedycyjnych (Carrier Movement Repo-
Rysunek 7.4.
W tej chwili nie będzie repozytorium dla operacji obsługi (Handling
REPOZYTORIA
Event Repository), ponieważ zdecydowaliśmy, aby w pierwszym podej- udostępniają
ściu zaimplementować asocjację z klasą Delivery History w postaci wybrane korzenie
kolekcji, i nie mamy wymagania do aplikacji, które oczekiwałoby od- AGREGATÓW
szukiwania tego, co zostało załadowane w danej operacji spedycyjnej.
Każdy z tych powodów mógłby ulec zmianie. W takim przypadku dodali-
byśmy REPOZYTORIUM.
Tworzenie obiektów
FABRYKI oraz konstruktory klasy Cargo
Nawet gdybyśmy stworzyli dla obiektów klasy Cargo wyszukaną FA-
BRYKĘ lub też używali w jej charakterze innego obiektu Cargo, tak jak
w scenariuszu „powtórzenie operacji”, to wciąż jednak potrzebowalibyśmy
prostego konstruktora. Chcielibyśmy, aby tworzył on przynajmniej obiekt
spełniający swoje niezmienniki, a w przypadku ENCJI nie zmieniał tożsa-
mości. Uwzględniając te założenia, moglibyśmy utworzyć w klasie Cargo
następującą metodę FABRYKI:
public Cargo copyPrototype(String newTrackingID)
Rysunek 7.10.
Klasa Allocation
Checker działa jako
WARSTWA
ZAPOBIEGAJĄCA
USZKODZENIU,
udostępniając
wybiórczy interfejs
do systemu
zarządzania sprzedażą
wyrażony w pojęciach
naszego modelu
dziedziny
Rysunek 7.11.
Odpowiedzialności
dziedziny
przeniesione
z Booking
Application
do Allocation
Checker
Poprawa wydajności
Chociaż interfejs Allocation Checker jest jedyną częścią, którą dostrzega
pozostała część projektu dziedziny, to jednak jego wewnętrzna imple-
mentacja może w przypadku ich wystąpienia powodować trudności
w rozwiązywaniu problemów wydajnościowych. Jeżeli na przykład system
zarządzania sprzedażą działa na innym serwerze (a nawet w innej lokali-
zacji), bardzo istotny może okazać się narzut związany z komunikacją,
zaś do każdorazowego sprawdzenia przypisania konieczna jest wymiana
Ostateczna wersja
O to właśnie chodzi. Przedstawiony przykład integracji mógłby spowo-
dować zmianę prostego i spójnego pojęciowego projektu w bałagan cha-
otycznych połączeń. Jednak dzięki wykorzystaniu WARSTWY ZAPO-
BIEGAJĄCEJ USZKODZENIU, USŁUGI oraz kilku SEGMENTÓW
PRZEDSIĘBIORSTWA w sposób przejrzysty zintegrowaliśmy funkcjo-
nalność systemu zarządzania sprzedażą do naszego systemu rezerwacji,
wzbogacając jednocześnie jego dziedzinę.
Poziomy refaktoryzacji
Refaktoryzacja polega na zmianie projektu oprogramowania w sposób,
który nie zmienia jego funkcjonalności. Zamiast podejmować od razu
rozległe decyzje projektowe, programiści rozwijają zazwyczaj program
poprzez szereg stałych zmian dokonywanych w projekcie. Żadna z nich
nie zmienia funkcjonalności, co czyni projekt bardziej uniwersalnym oraz
łatwiejszym do zrozumienia. Stosunkowo bezpieczne eksperymentowanie
z kodem umożliwia zestaw zautomatyzowanych testów jednostkowych.
Cały ten proces uwalnia programistów od konieczności zbytniego wybiega-
nia w przyszłość.
1
Wzorce jako cel refaktoryzacji zostały pobieżnie omówione w książce Gammy
(1995 r.). Całą tę ideę rozwinął do postaci bardziej dojrzałej oraz przydatnej w użyciu
Joshua Kerievsky (w książce z 2003 r.).
Modele dogłębne
Tradycyjny sposób wyjaśniania analizy obiektowej obejmuje identyfika-
cję rzeczowników oraz czasowników w dokumentach z wymaganiami,
a następnie stosowanie ich do budowy podstawowych wersji obiektów oraz
metod. Jest to zdecydowane uproszczenie, które może być wprawdzie
przydatne w trakcie nauki modelowania obiektowego przez osoby począt-
kujące. Jednak początkowe modele są zazwyczaj bardzo naiwne i powierz-
chowne, gdyż bazują jedynie na płytkiej wiedzy.
Pracowałem kiedyś na przykład nad aplikacją do transportu morskiego,
w której początkowo miałem pomysł na model obiektowy używający repre-
zentacji statków oraz kontenerów. Statki przepływały z miejsca na miejsce.
Kontenery były dodawane oraz usuwane w operacjach załadunku oraz
rozładunku. To dość dobry opis pewnych fizycznych czynności w tej
branży. Jednak nie okazał się on zbyt przydatnym modelem dla oprogra-
mowania wykorzystywanego w branży okrętowej.
Ostatecznie po miesiącach pracy z ekspertami z tej dziedziny oraz
po wielu iteracjach opracowaliśmy całkiem inny model. Był on wprawdzie
mniej oczywisty dla laika, lecz bardziej odpowiedni dla ekspertów. Kon-
centrował się na biznesie związanym z dostarczaniem ładunku.
Oczywiście, statki wciąż były w nim obecne, jednak w tym przy-
padku bardziej w postaci „podróży morskiej”, zdefiniowanej ogólnie jako
podróż statkiem, pociągiem lub innym rodzajem transportu. Statek w tym
przypadku odgrywał rolę drugorzędną i mógł być w ostatniej chwili zastą-
piony w celu przeprowadzenia naprawy serwisowej, podczas gdy podróż
morska odbywała się zgodnie z planem. Kontener prawie zniknął z modelu.
W aplikacji do obsługi ładunku był on obsłużony w innej, bardziej złożonej
postaci, jednak w kontekście pierwotnej aplikacji stanowił szczegół ope-
racyjny. Fizyczne przesunięcia ładunku ustąpiły miejsca przeniesieniom
odpowiedzialności prawnej za ten ładunek. Pojawiły się też mniej oczy-
wiste obiekty, w rodzaju „kwitu załadunkowego”.
Jakie były zawsze pierwsze sugestie nowych osób zajmujących się
modelowaniem, które pojawiały się w projekcie? Oczywiście, aby dodać
brakujące klasy: statek oraz kontener. Byli to mądrzy ludzie, jednak naj-
zwyczajniej nie przeszli jeszcze przez proces odkrywania dziedziny.
Moment
przełomowy
229
Czym jest „linia lub modelu udostępnia programistom bardziej przejrzysty obraz. Ta przej-
kredytowa”? W tym rzystość tworzy potencjał dla przełomowych odkryć. Wiele zmian pro-
kontekście nie jest ona
wcale określeniem
wadzi do bardziej realistycznego modelu, odpowiadającego na priorytety
geometrycznym. użytkowników. Nagle zwiększa się jego uniwersalność oraz siła wyja-
W przypadku śniania dziedziny, a jednocześnie zmniejsza się złożoność.
większości projektów Tego rodzaju moment przełomowy nie jest techniką, a raczej zda-
specjalistyczna
terminologia
rzeniem. Wyzwanie stanowi jego rozpoznanie i zdecydowanie, w jaki
od ekspertów sposób go obsłużyć. Aby przekazać prawdziwe odczucie z tym związane,
dziedzinowych opowiem teraz prawdziwą historię projektu, nad którym pracowałem przed
przenika do języka kilkoma laty, oraz sposobu, w jaki doszliśmy do bardzo cennego modelu
i staje się częścią
JĘZYKA WSZECH-
dogłębnego.
OBECNEGO.
W dziedzinie
bankowości
inwestycyjnej
Historia pewnego przełomu
„linia kredytowa”
Po długiej zimie spędzonej na refaktoryzacji w Nowym Jorku doszliśmy
jest zobowiązaniem firmy
do pożyczenia pieniędzy. do modelu, który wychwytywał kluczową wiedzę dziedziny oraz pro-
Twoja karta kredytowa jektu wykonującego dla aplikacji pewne rzeczywiste zadania. Pracowa-
będzie linią kredytową, liśmy nad utworzeniem głównej części dużej aplikacji do zarządzania
umożliwiającą Ci
pożyczkami konsorcjalnymi w banku inwestycyjnym.
pożyczenie środków
do wysokości wcześniej Jeśli na przykład firma Intel chciałaby wybudować fabrykę kosztu-
ustalonego limitu jącą miliard dolarów, potrzebowałaby w tym celu pożyczki zbyt dużej dla
z wcześniej ustaloną pojedynczej firmy pożyczającej pieniądze. Dlatego też firmy te musiałyby
wielkością procentu.
utworzyć syndykat, który połączyłby posiadane zasoby i umożliwił udzie-
W przypadku użycia
karty zakładana jest lenie kredytu (patrz informacje na marginesie). Bank inwestycyjny działa
kwota wymagalnej zazwyczaj w charakterze firmy przewodniczącej takiemu syndykatowi,
pożyczki, a każde koordynując transakcje oraz inne usługi. Zadaniem naszego projektu było
kolejne obciążenie
utworzenie oprogramowania do śledzenia oraz wspierania całego procesu.
kartą będzie
wycofaniem środków
z linii kredytowej
na jej rzecz. Ostatecznie Przyzwoity model, lecz wciąż...
jednak bankowi będzie
trzeba oddać kwotę Mieliśmy wszyscy dobre samopoczucie. Cztery miesiące wcześniej byliśmy
rzeczywistej pożyczki. w poważnych tarapatach. Przed utworzeniem spójnego PROJEKTU STE-
Dodatkowo możesz ROWANEGO MODELEM dysponowaliśmy jedynie zupełnie niedziała-
być obciążany opłatą
jącym odziedziczonym kodem bazowym.
roczną. Będzie to
opłata za przywilej Model przedstawiony na rysunku 8.1 znacznie upraszcza zastany
posiadania karty problem. Inwestycja pożyczkowa (Loan Investment) jest obiektem repre-
(linii kredytowej), zentującym wkład poszczególnego inwestora (Investor) do wspólnej
naliczana zupełnie
niezależnie
pożyczki (Loan), proporcjonalnie do posiadanych przez niego udziałów
od fizycznej pożyczki. w „linii kredytowej” (Facility).
Moment przełomowy
Niespodziewanie po tygodniu przyszedł nam do głowy powód problemu.
Nasz model w sposób niewłaściwy dla biznesu powiązał ze sobą razem obiekty
Facility (linia kredytowa) oraz udziały w obiekcie Loan (pożyczka). To
Rysunek 8.3.
Wykres obrazujący Diagram przekazuje informację o tym, że pożyczkobiorca zdecy-
podział wypłaty dował się wykorzystać początkowe 50 milionów ze 100 milionów do-
proporcjonalnie stępnych w linii kredytowej. Trzej pożyczkodawcy w sposób proporcjo-
do posiadanych nalny do posiadanych udziałów muszą wspólnie zasilić linię kredytową
udziałów w linii pobraną kwotą.
kredytowej
Rysunek 8.4.
Kiedy pożyczkobiorca zacznie spłacać pożyczkę, pieniądze będą dzie- Pożyczkodawca B
lone pomiędzy pożyczkodawców według ich udziałów w pożyczce, a nie wycofuje się
w linii kredytowej. W podobny sposób będą dzielone pomiędzy nich z drugiej wypłaty
odsetki od rat kapitałowych.
Z drugiej strony, jeżeli pożyczkobiorca zapłaci opłatę za utrzyma-
nie linii kredytowej, pieniądze dzielone będą zgodnie z udziałami w tej-
że linii, niezależnie od tego, kto właściwie pożyczył pieniądze. Te opłaty
z kolei nie wpływają na samą pożyczkę. Istnieją nawet scenariusze, w któ-
rych pożyczkodawcy handlują udziałami w opłatach niezależnie od udzia-
łów w odsetkach itd.
Model pogłębiony
W tej chwili dysponowaliśmy dwoma pogłębionymi wnioskami z dzie-
dziny. Pierwszy z nich polegał na zrozumieniu, że nasze „inwestycje” oraz
„pożyczka” były jedynie dwoma specjalnymi przypadkami ogólnego oraz
podstawowego pojęcia, czyli udziałów. Udziały w linii kredytowej, udziały
w pożyczce, udziały w dystrybucji spłaty kapitału. Wszędzie tylko te udziały...
Udziały w każdej wartości możliwej do podziału. Po kilku burzliwych
dniach naszkicowałem model udziałów, korzystając z języka używanego
w dyskusjach z ekspertami oraz wspólnie przeanalizowanych scenariuszach.
Rysunek 8.6.
Płatności opłat
za linię kredytową
są zawsze dzielone
proporcjonalnie
do udziałów w linii
kredytowej
Rysunek 8.7.
Abstrakcyjny
model udziałów
Zapłata
Tajemnicze i nagłe zmiany wymagań ustały. Logika zaokrągleń, chociaż
nigdy właściwie nie była prosta, to jednak zaczęła być stabilna i wydawała się
wreszcie mieć pewien sens. Dostarczyliśmy pierwszą wersję systemu i by-
liśmy gotowi na pracę nad drugą. Również moje nerwy uległy uspokojeniu.
Kiedy powstała druga wersja programu, rozkład udziałów (Share Pie)
stał się spajającym akcentem całej aplikacji. Eksperci techniczni oraz biz-
nesowi używali go podczas omawiania funkcjonalności systemu. Także
osoby z działu marketingu wykorzystywały ten pomysł do wyjaśnienia funkcjo-
nalności przyszłym klientom. Klienci przyswajali sobie wiedzę błyskawicznie
i używali jej w dalszej dyskusji. To pojęcie stało się rzeczywiście elementem
JĘZYKA WSZECHOBECNEGO, ponieważ wychwyciło istotę syndy-
katu zajmującego się pożyczkami.
Możliwości
W chwili wystąpienia momentu przełomowego i odkrycia pogłębionego
modelu towarzyszy nam często strach. Z taką zmianą wiążą się większe
ryzyko i większe potencjalne korzyści niż w przypadku dużej części zmian
refaktoryzacyjnych. Również moment może być niesprzyjający.
Chociaż chcielibyśmy, aby było odwrotnie, to jednak cena postępu
może być wysoka. Przejście do naprawdę dogłębnego modelu jest zasadni-
czą zmianą w sposobie myślenia i wymaga dużej zmiany projektu. W przy-
padku wielu projektów najważniejsze postępy w modelowaniu oraz pro-
jektowaniu wiążą się właśnie z takimi przełomowymi momentami.
Koncentracja na podstawach
Niech nie paraliżuje Cię usiłowanie doprowadzenia do przełomu. Możli-
wości pojawiają się zazwyczaj po wielu średnich zmianach refaktoryza-
cyjnych. Większość czasu poświęcanego jest na fragmentaryczne udosko-
nalenia, a z każdym takim kolejnym ulepszeniem pojawiać się będą nowe
spostrzeżenia w stosunku do modelu.
Aby wcześniej przygotować podłoże do wystąpienia przełomu, skon-
centruj się na przetwarzaniu wiedzy oraz doskonaleniu solidnego JĘZYKA
MOŻLIWOŚCI 237
WSZECHOBECNEGO. Staraj się zgłębić istotne pojęcia dziedziny i ujaw-
niaj je w modelu (w sposób pokazany w rozdziale 9.). Zmieniaj projekt,
aby uczynić go bardziej elastycznym (patrz rozdział 10.). Destyluj również
model (patrz rozdział 15.). Używaj tych bardziej przewidywalnych metod,
zwiększających przejrzystość modelu. Prowadzą one zazwyczaj do momen-
tów przełomowych w projekcie.
Nie powstrzymuj się również przed niewielkimi udoskonaleniami,
które będą w sposób stopniowy pogłębiać model, nawet wtedy, gdy będą
one dotyczyć tego samego szkieletu pojęciowego. Nie obawiaj się spojrzenia
zbyt daleko do przodu. Po prostu uważaj na pojawiające się możliwości.
Odkrywanie
pojęć niejawnych
241
Wyciąganie pojęć
Programiści powinni wyczulić się na sygnały, które odkrywają przed nimi
czające się dotychczas ukryte pojęcia, a niekiedy muszą być gotowi nawet
proaktywnie ich poszukać. Szereg tego rodzaju odkryć bierze się z przy-
słuchiwania się językowi używanemu przez zespół, analizy wykrytych
niedogodności w projekcie, wskazywania sprzeczności w wypowie-
dziach ekspertów, przeszukiwania literatury poświęconej dziedzinie, a także
wykonywania szeregu eksperymentów.
Nasłuchiwanie języka
Możesz przypominać sobie następujące doświadczenie: Użytkownicy zaw-
sze opowiadali o pewnym elemencie raportu. Był on utworzony z atry-
butów różnych obiektów, a może nawet był wynikiem bezpośredniego
zapytania do bazy danych. Ten sam zestaw danych jest również przygo-
towywany w innej części aplikacji w celu zaprezentowania go w raporcie
lub wykorzystania w innych obliczeniach. Nigdy jednak nie widziałeś
potrzeby definiowania w tym celu obiektu. Być może nigdy nawet nie
rozumiałeś, co użytkownicy tak naprawdę rozumieli przez dane pojęcie,
i nie zdałeś sobie sprawy z jego istoty.
Nagle jednak doznajesz olśnienia. Nazwa elementu w raporcie określa
ważne pojęcie dziedziny. O tym odkryciu z podnieceniem rozmawiasz
ze swoimi ekspertami. Być może ujrzysz u nich odczucie ulgi na wieść,
że w końcu na to wpadłeś. A być może ziewanie, ponieważ od dłuższe-
go czasu przyjmowali to za pewnik. Niezależnie od tego zaczynasz ry-
sować na tablicy diagramy modelu, zastępujące wykonywane wcześniej
niedoskonałe gestykulacje rąk. Oczywiście, użytkownicy mogą popra-
wiać Cię w szczegółach połączeń modelu, jednak sam możesz przyznać,
że w dyskusji nastąpiła zmiana jakościowa. Od tego czasu rozumiecie się
z użytkownikami nawzajem w sposób bardziej precyzyjny, a demon-
strowanie interakcji w modelu, prowadzących do rozwiązywania okre-
ślonych scenariuszy, staje się bardziej naturalne. Język modelu dzie-
dziny stał się potężniejszy. Możesz już zrefaktoryzować kod w taki
sposób, aby odzwierciedlał ten nowy model, a dostrzeżesz, że projekt
stał się o wiele bardziej przejrzysty.
Przysłuchuj się językowi używanemu przez ekspertów dzie-
dzinowych. Czy występują w nim pojęcia w sposób zwięzły obja-
śniające coś skomplikowanego? Czy eksperci poprawiają Twoje
słowa (być może nawet w sposób dyplomatyczny)? Czy zdziwiony
Przykład
Nasłuchiwanie brakującego pojęcia
w modelu firmy spedycyjnej
Zespół programistów stworzył już działającą aplikację, która mogła słu-
żyć do rezerwacji ładunku. Zaczął właśnie tworzyć kolejną aplikację, do
„wsparcia operacyjnego”, która pomogłaby w przerzucaniu zleceń ładunku
oraz rozładunku towarów w punkcie pochodzenia lub docelowym, a także
przenoszeniu ładunków pomiędzy statkami.
W celu zaplanowania trasy ładunku aplikacja do rezerwacji używała
silnika trasowania. Każdy etap podróży (Leg) był przechowywany w wier-
szu tabeli w bazie danych, zawierającym identyfikator podróży morskiej
(konkretnej podróży danym statkiem) odpowiedzialnej za przewóz ładunku,
lokalizację jego załadunku oraz rozładunku.
Przysłuchajmy się przez chwilę konwersacji pomiędzy programistą
a ekspertem od logistyki morskiej (rozmowa została znacznie skrócona).
Programista: Chciałbym jedynie upewnić się, czy tabela „cargo bookings”
zawiera wszystkie dane, których będzie potrzebować aplikacja do
wsparcia operacyjnego.
Ekspert: Użytkownicy będą z pewnością potrzebować całego planu podróży
dla ładunku (Cargo). Jakie informacje są teraz w tej tabeli?
Rysunek 9.2.
Przykład
Trudne pobieranie odsetek
Kolejna opowieść dotyczyć będzie hipotetycznej firmy z branży finanso-
wej, inwestującej środki w pożyczki komercyjne oraz inne produkty przy-
noszące zyski. Aplikacja śledząca te inwestycje oraz zyski ewoluowała
Rysunek 9.4.
Skrypt uruchamiany wsadowo w nocy przeszukiwał każde z aktywów
Dziwny model
(Asset), nakazując im obliczenie odsetek dla danego dnia przy użyciu funkcji
calculateInterestForDate(). Następnie skrypt pobierał zwracaną war-
tość (zarobioną kwotę) i przekazywał ją wraz z nazwą odpowiedniej
księgi rachunkowej do USŁUGI udostępniającej publiczny interfejs do
programu księgowego. Ten program z kolei wysyłał tę kwotę do wska-
zanej księgi. Podobny proces skrypt przechodził w celu obliczenia dla
każdego z aktywów dziennych opłat i przekazywał je do innej księgi.
Jedna z programistek musiała zmagać się z rosnącą złożonością obli-
czania odsetek. Dlatego też zaczęła podejrzewać, że istnieje możliwość
stworzenia modelu lepiej dopasowanego do tego zadania. Poprosiła więc
swojego ulubionego eksperta dziedzinowego, aby pomógł jej zagłębić się
w problem.
Programistka: Nasz kalkulator odsetkowy (Interest Calculator) wymyka
się spod kontroli.
Ekspert: To skomplikowana część. Wciąż mamy kilka przypadków, których
nie obsługiwaliśmy.
Programistka: Wiem. Możemy dodać nowe rodzaje procentów, zastę-
pując go innym kalkulatorem odsetkowym (Interest Calculator).
Jednak obecnie najwięcej problemów sprawiają nam te specjalne
sytuacje, w których odsetki nie są wypłacane zgodnie z harmono-
gramem.
Ekspert: To właściwie nie są specjalne przypadki. Sposób wpłacania rat
przez ludzi zezwala na dużą swobodę.
Rysunek 9.5.
Rysunek 9.6.
Ekspert: Pewnie! Wygląda dobrze. Nie jestem pewien, dlaczego ten sposób
byłby dla Ciebie prostszy. Jednak mówiąc w uproszczeniu, to właśnie
suma, jaką dana pozycja aktywów może dodać przyrostowo dla odse-
tek, opłat itp., czyni ją wartościową.
Programistka: Powiedziałeś, że opłaty działają w ten sam sposób? Dlaczego
w takim razie nie dodać ich do innych ksiąg?
Programistka: W przypadku takiego modelu dysponujemy logiką obli-
czania odsetek, lub bardziej logiką obliczania sum przyrostowych,
która będzie w kalkulatorze odsetkowym (Interest Calculator)
oddzielona od śledzenia kwot. Aż do tej chwili nie byłam świadoma,
Rysunek 9.8.
Głębszy model
po refaktoryzacji
Czytanie książki
W trakcie poszukiwania pojęć do modelu postaraj się nie przegapić rze-
czy oczywistych. W wielu dziedzinach możesz znaleźć dostępne książki
wyjaśniające podstawowe idee oraz powszechną wiedzę. Wciąż będziesz
musiał pracować ze swoimi ekspertami dziedzinowymi w celu destylacji
tych części, które są właściwe problemowi, i przetworzyć tę wiedzę
w coś, co będzie pasowało do oprogramowania obiektowego. Jednak
będziesz już w stanie rozpocząć od spójnego i przemyślanego widoku.
Przykład
Książkowy przykład zarabiania odsetek
Wyobraźmy sobie inny scenariusz dla aplikacji do śledzenia inwestycji omó-
wionej w poprzednim przykładzie. Podobnie jak poprzednio historia
rozpocznie się od programistki zdającej sobie sprawę, że projekt, a w szcze-
gólności kalkulator odsetkowy (Interest Calculator) staje się dziwny.
Jednak w tym scenariuszu podstawowa odpowiedzialność eksperta będzie
leżeć gdzieś indziej i nie będzie on mieć za dużo interesu w tym, aby
pomagać projektowi tworzącemu oprogramowanie. Programistka nie będzie
mogła więc zwrócić się do eksperta i poprosić go o dyskusję w celu zasta-
nowienia się nad brakującymi pojęciami, które — jak podejrzewa — kryją
się gdzieś głębiej.
Rysunek 9.9.
Nieco głębszy
model, oparty
na lekturze książki
Wprawdzie wciąż nie miała pełnej wiedzy o tym, że aktywa (Assets) są
generatorami przychodów, co spowodowało pozostawienie kalkulatorów
(Calculators) na swoim miejscu. Również wiedza o księgach była wciąż
w aplikacji, a nie w warstwie dziedziny, do której prawdopodobnie powinna
przynależeć. Jednak programistce udało się już oddzielić kwestię płatności
od przyrostowego naliczania przychodu, co stanowiło największy problem.
Wprowadziła również do modelu oraz JĘZYKA WSZECHOBECNEGO
słowo „przyrost”. Dalsze ulepszanie modelu mogło nastąpić w późniejszych
iteracjach.
Bezpośrednie ograniczenia
Ograniczenia stanowią szczególnie istotną kategorię pojęć modelowych.
Bardzo często pojawiają się one w sposób niejawny, a ich bezpośrednie
wyrażenie może w znaczny sposób poprawić projekt.
Niekiedy ograniczenia te znajdują swoje naturalne miejsce w obiekcie
lub metodzie. Na przykład obiekt reprezentujący „wiaderko ” (Bucket)
musi gwarantować niezmiennik, który będzie zapewniać, że nie włożymy
do niego więcej, niż określa to jego pojemność.
Tego rodzaju prosty niezmiennik może być wymuszony przy pomocy
instrukcji warunkowej w każdej operacji, która ma możliwość zmiany
zawartości obiektu.
class Bucket {
private float capacity;
private float contents;
public void pourIn(float addedVolume) {
Przykład
Analiza powtórna — polityka nadkompletu
W rozdziale 1. zajmowaliśmy się pewną powszechną praktyką w trans-
porcie — rezerwacją 10% więcej ładunku, niż umożliwia to przestrzeń
ładunkowa transportu (firmy transportowe z doświadczenia nauczyły
się, że ta polityka nadkompletu równoważy odwołania zleceń składane
w ostatniej chwili, co sprawia, że ich statki pływają prawie pełne).
To ograniczenie w asocjacji pomiędzy obiektami Voyage oraz Cargo
zostało uwidocznione jawnie zarówno na diagramach, jak i w kodzie
poprzez dodanie do nich nowej klasy, reprezentującej to ograniczenie.
Rysunek 9.12.
Rysunek 9.13.
Bardziej
rozbudowana reguła
wymagalności,
utworzona w postaci
SPECYFIKACJI
Walidacja
Najprostszym przypadkiem użycia SPECYFIKACJI jest walidacja. Jed-
nocześnie demonstruje ona całą ideę w najprostszy sposób.
class DelinquentInvoiceSpecification extends
InvoiceSpecification {
private Date currentDate;
// Instancja jest tworzona i usuwana w konkretnym dniu
Rysunek 9.14.
Model używający
SPECYFIKACJI
do walidacji
Przykład
Pakowacz w magazynie chemicznym
Istnieje pewien magazyn, w którym różne chemikalia są poukładane
w stosy olbrzymich kontenerów, podobnych do tych, w których przewozi
się samochód. Niektóre z tych chemikaliów są obojętne i mogą być skła-
dowane prawie wszędzie. Inne są bardzo delikatne i mogą być przecho-
wywane jedynie w specjalnych wentylowanych kontenerach. Na koniec
są jeszcze materiały wybuchowe, do których przechowywania służą spe-
cjalne wzmocnione opancerzone kontenery. Zdefiniowane są również
odpowiednie reguły dotyczące kombinacji dopuszczalnych wewnątrz
kontenerów.
Celem jest napisanie programu, który odnajdzie efektywny i bez-
pieczny sposób umieszczenia substancji w kontenerze.
Rysunek 9.16.
Model magazynu Moglibyśmy rozpocząć pracę od utworzenia procedury biorącej sub-
stancję chemiczną i umieszczającej ją w kontenerze. Jednak rozpocznijmy
Substancja
chemiczna Specyfikacja kontenera
Trotyl Kontener opancerzony
Piach
Próbki biologiczne Nie może współdzielić kontenera z materiałami
wybuchowymi
Amoniak Kontener wentylowany
Projekt elastyczny
279
często wymusza kod monolityczny, uniemożliwiający łączenie jego różnych
elementów. Oczywiście, w celu ich lepszego wykorzystania, klasy oraz
metody mogą zostać podzielone, jednak śledzenie przeznaczenia tych
wszystkich małych obiektów zaczyna być trudne. Jeżeli programowi bra-
kuje spójnego projektu, programiści ze strachem spoglądają na istniejący
bałagan i mają niewielką ochotę, aby wprowadzać jakiekolwiek zmiany,
mogące zaburzyć istniejący układ lub też zepsuć coś z powodu nieprze-
widzianej zależności. We wszystkich systemach (może z wyjątkiem tych
małych) ta słabość wyznacza górną granicę możliwych do wykonania zmian
wzbogacających logikę aplikacji. Powstrzymuje ona jakąkolwiek możli-
wość refaktoryzacji lub iteracyjnego ulepszania kodu.
Zespół, który ma być przygotowany na przyspieszenie prac wraz
z rozbudowywaniem kodu (a nie powstrzymywany przez jego rozmiar),
wymaga projektu zapraszającego do zmian — takiego, z którym praca
będzie przyjemnością.
Takie wymaganie spełnia projekt elastyczny. Jest on uzupełnieniem
modelowania dogłębnego. Gdy tylko wydobędziesz na światło dzienne
niejawne pojęcia, będziesz też dysponować odpowiednim materiałem.
Następnie w iteracyjnych cyklach możesz nadać mu przydatny kształt,
rozwijając model w sposób prosty i przejrzysty wychwytujący najważniej-
sze pojęcia, wpływający równocześnie na projekt, który pozwoli progra-
miście wdrożyć ten model. Rozwój projektu oraz kodu programu pro-
wadzi do spostrzeżeń, które będą ulepszać pojęcia modelu. Krok po kroku
wracamy zatem do cyklu powtórzeń oraz refaktoryzacji prowadzących
ku głębszemu zrozumieniu. Do jakiego rodzaju projektu zamierzasz jed-
nak dotrzeć? Jakiego rodzaju eksperymenty powinieneś podjąć w drodze
do celu? O tym właśnie mówi ten rozdział.
Elastycznością projektu uzasadniano już wiele dodatkowych wysił-
ków projektowych. Jednak dodatkowe warstwy abstrakcji oraz warstwy
pośredniczące bardzo często sprawdzają się w praktyce. Spójrz na pro-
jekty aplikacji, które rzeczywiście zwiększają możliwości pracujących z nimi
osób. Zazwyczaj dostrzeżesz w nich coś prostego, ale prostota nie zna-
czy, że są one łatwe. Aby móc utworzyć elementy, które będą mogły
stanowić składniki złożonych systemów i wciąż będą możliwe do zro-
zumienia, konieczne jest całkowite oddanie PROJEKTOWI STERO-
WANEMU MODELEM, z utrzymywaniem rygorystycznego stylu pro-
jektowania. Może to wymagać zaawansowanych umiejętności w zakresie
projektowania, zarówno w trakcie tworzenia tych elementów, jak i póź-
niejszego używania.
Rozdział 10. Projekt elastyczny
Przykład
Refaktoryzacja — aplikacja do mieszania farb
Program przeznaczony dla sklepów z farbami może pokazać klientom
rezultat mieszania podstawowych farb. Oto początkowy projekt, z poje-
dynczą klasą dziedziny.
Rysunek 10.2.
Rysunek 10.3.
***
Dzięki wykorzystaniu INTERFEJSÓW UJAWNIAJĄCYCH ZAMYSŁ
możliwe jest umieszczenie w modułach oraz zahermetyzowanie całych
poddziedzin. Taki sposób upraszczania kodu, ułatwiający skoncentrowa-
nie się na projekcie i zarządzanie złożonością dużych systemów wraz
z MECHANIZMAMI SPÓJNOŚCI oraz GENERYCZNYMI POD-
DOMENAMI, zostanie omówiony szerzej w rozdziale 15., „Destylacja”.
Jednak w kolejnych dwóch wzorcach zastanowimy się nad zwięk-
szeniem przewidywalności efektów użycia metody. Złożona logika może
być bezpiecznie umieszczona w FUNKCJACH BEZ EFEKTÓW
UBOCZNYCH. Natomiast metody zmieniające stan systemu mogą zostać
scharakteryzowane przy użyciu ASERCJI.
Przykład
Ponowna refaktoryzacja aplikacji do mieszania farb
Program dla sklepów z farbami może pokazać klientowi rezultat mie-
szania podstawowych farb. Rozpoczynając od miejsca, w którym zakoń-
czyliśmy ostatni przykład, zajmijmy się podstawową klasą dziedziny.
Rysunek 10.4.
Rysunek 10.5.
Efekty uboczne
metody mixIn()
Rysunek 10.6.
1
Różnica pochodzi od metody tworzenia kolorów. Mieszanie świateł jest
metodą addytywną i polega na łączeniu różnych odcieni składowych ostatecznej
barwy. Farby natomiast tworzone są metodą subtraktywną i uzyskanie ostatecz-
nego koloru polega na znalezieniu takich składowych farby, które w efekcie wytną
z odbitego od ściany widma światła niepożądane kolory, a pozostawią te, na których
nam zależy. Dlatego to bardziej w pierwszym przypadku tworzymy kolor. W dru-
gim raczej usuwamy te kolory, które są niepotrzebne. Teoretycznie te dwie metody
dopełniają się i są jakby spojrzeniem na dziedzinę mieszania kolorów z dwóch
przeciwnych stron — przyp. tłum.
Rysunek 10.7.
Rysunek 10.8.
***
ASERCJE 293
obiektu pod koniec każdej operacji. Niezmienniki mogą być również
zadeklarowane dla całego AGREGATU, w sposób rygorystyczny definiując
reguły dotyczące jego integralności.
Wszystkie te asercje opisują stan, a nie procedury, są więc łatwiejsze
do analizy. Niezmienniki klas pomagają scharakteryzować logikę klasy,
upraszczając zadanie programisty poprzez uczynienie obiektów bardziej
przewidywalnymi. Jeżeli wierzysz gwarancji warunków końcowych, to
nie musisz przejmować się tym, jak działa metoda. Efekty delegacji powinny
być już dołączone do asercji.
Dlatego:
Określ warunki końcowe operacji oraz niezmienników klas
i AGREGATÓW. W przypadku, gdy w używanym przez Ciebie
języku programowania ASERCJE nie mogą zostać wyrażone bez-
pośrednio, napisz dla nich automatyczne testy jednostkowe. Zapisz
je w dokumentacji lub na diagramach w miejscu, w którym odpo-
wiadają stylowi wybranego procesu tworzenia oprogramowania.
Poszukaj modeli ze spójnym zestawem pojęć, które pomogą
programiście odkryć zamierzone ASERCJE, przyspieszą naukę oraz
zredukują ryzyko utworzenia sprzecznego kodu.
Nawet jeżeli wiele obecnych języków obiektowych nie wspiera bez-
pośrednio ASERCJI, to są one wciąż bardzo powszechnym sposobem
myślenia o projekcie. Brak wsparcia ASERCJI w języku mogą częściowo
zrekompensować automatyczne testy jednostkowe. Ponieważ wszystkie
ASERCJE wyrażone są raczej w kategoriach stanów, a nie procedur, ułatwia
to znacznie stworzenie testów. Konfiguracja testu ma jedynie ustawić
warunki początkowe, a następnie po wykonaniu testu sprawdzić speł-
nienie wszystkich warunków końcowych.
Jasno wyrażone niezmienniki oraz warunki początkowe i końcowe
pozwalają programiście zrozumieć konsekwencje używania danej operacji
lub obiektu. Teoretycznie dobrze działać powinien dowolny zestaw nie-
wykluczających się asercji. Jednak ludzie zwykle nie kompilują w umyśle
jedynie predykatów. Będą dodatkowo ekstrapolować oraz interpolować
pojęcia użytego modelu, dlatego tak istotne jest odnalezienie modeli,
które będą logiczne dla ludzi, a jednocześnie będą zaspokajać potrzeby
aplikacji.
Rysunek 10.9.
Po p1.mixIn(p2):
p1.volume jest zwiększany o wartość p2.volume.
p2.volume pozostaje bez zmian.
ASERCJE 295
Dlatego też zmiana objętości w modelu w sposób spójny logicznie
spowodowałaby, że aplikacja nie spełniałaby wymagań. Wydaje się to
dużym dylematem. Czy skazani jesteśmy na dokumentację dziwnych
warunków końcowych wspieraną dobrą komunikacją? Niekiedy najlepszą
odpowiedzią jest fakt, iż nie wszystkie rzeczy na tym świecie są intuicyjne.
Jednak w tym przypadku ten dziwny kod wydaje się wskazywać na pewne
brakujące pojęcia. Przyjrzyjmy się nowemu modelowi.
Rysunek 10.10.
mix.mixIn(paint1);
mix.mixIn(paint2);
assertEquals(2.5, mix.getVolume(), 0.01);
}
***
Zdolność do komunikowania informacji przez INTERFEJSY UJAW-
NIAJĄCE ZAMIAR, w połączeniu z przewidywalnością FUNKCJI BEZ
EFEKTÓW UBOCZNYCH oraz ASERCJI, powinna uczynić hermety-
zację oraz abstrakcję zdecydowanie bezpieczniejszą.
Kolejnym elementem, którego można używać wielokrotnie, jest
efektywna dekompozycja.
ASERCJE 297
ZARYSY KONCEPCYJNE
Programiści niekiedy rozdrabniają funkcjonalności, aby umożliwić ich
elastyczne łączenie ze sobą. Innym razem definiują duże obiekty, aby sku-
tecznie zahermetyzować złożoną logikę. A w jeszcze innych przypadkach
szukają stałego poziomu rozdrobnienia, tworząc wszystkie klasy i operacje
o podobnej skali. To wszystko są zdecydowane uproszczenia, które nie
funkcjonują zbyt dobrze w charakterze ogólnych zasad. Jednak wszystkie
są motywowane podstawowym zestawem problemów.
W sytuacji, gdy elementy modelu lub projektu osadzone są
w konstrukcji monolitycznej, ich funkcjonalność staje się zdu-
plikowana. Interfejsy zewnętrzne nie mówią wszystkiego, czego
klient mógłby oczekiwać. Ich znaczenie jest coraz trudniejsze
do zrozumienia, ponieważ różne pojęcia są ze sobą wymieszane.
Z drugiej strony, rozbijanie klas oraz metod na mniejsze
może bezcelowo skomplikować kod klienta, zmuszając twórców
tego kodu do zrozumienia, w jaki sposób te małe elementy będą
do siebie pasować. Co gorsza, może zostać utracone samo pojęcie.
Pół atomu uranu nie jest uranem. I oczywiście nie liczy się sam
rozmiar podziałów, lecz to, dokąd one prowadzą.
Nie działają w tym przypadku również żadne ogólne przepisy. Jednak
w większości dziedzin musi istnieć pewna logiczna spójność, gdyż w prze-
ciwnym razie nie byłyby w stanie nawet funkcjonować w swoim otoczeniu.
Nie oznacza to wcale, że dziedziny są doskonale spójne, a z pewnością
niespójne są sposoby dyskutowania o nich przez ludzi. Niemniej jednak
musi istnieć logika, inaczej wysiłek modelowania byłby bezcelowy. Z tej
wewnętrznej spójności wynika pewna prawidłowość — jeżeli znajdziemy
model, który sprawdza się w części dziedziny, wtedy jest bardzo praw-
dopodobne, że będzie on również spójny z innymi częściami dziedziny,
które odkryjemy później. Niekiedy nowe odkrycie nie pasuje dobrze do
modelu, lecz wtedy możemy stosować refaktoryzację ku głębszemu zro-
zumieniu, mając nadzieję, że następne odkrycie będzie już bardziej z nim
zgodne.
To jeden z powodów, dlaczego powtarzalna refaktoryzacja ostatecz-
nie wspiera elastyczność projektu. W chwili, gdy projekt jest adaptowany
do zrozumianych właśnie pojęć oraz wymagań, zaczynają się pojawiać
ZARYSY KONCEPCYJNE.
Na wszystkich poziomach w projekcie, począwszy od poszczególnych
metod, przez klasy i MODUŁY, aż do struktur dużej skali, szczególną rolę
odgrywają wysoka spójność oraz słabe związanie. Te dwa pryncypia mają
takie samo zastosowanie do pojęć jak tworzony kod. Aby jednak uniknąć
2
Wzorzec WARTOŚCI CAŁKOWITEJ stworzony przez Warda Cun-
ninghama.
Przykład
ZARYSY sum przyrostowych
Przedstawiony w rozdziale 9. system do śledzenia pożyczek został pod-
dany refaktoryzacji na podstawie głębszego zrozumienia pojęć z zakresu
księgowości.
Chociaż w porównaniu do starego nowy model zawierał tylko jeden
obiekt więcej, to jednak podział odpowiedzialności został zdecydowanie
zmodyfikowany.
Harmonogramy, które umieszczone były w logice klas kalkulatora,
zostały przeniesione do osobnych klas dla różnych rodzajów opłat oraz
odsetek. Z drugiej strony, płatności opłat oraz odsetek, które były wcze-
śniej utrzymywane osobno, zostały teraz połączone.
Z powodu zbieżności nowych ujawnionych pojęć oraz spójności
hierarchii dla harmonogramów sum przyrostowych programistka uwie-
rzyła, że ten model w lepszy sposób mieści się w ZARYSACH KON-
CEPCYJNYCH.
Jedną z nowych zmian, która została z ufnością przewidziana przez
programistkę, było dodanie nowej klasy Accrual Schedule. Wymagania
dotyczące tych harmonogramów już czekały w pogotowiu. Dlatego oprócz
Nieprzewidziana zmiana
Wraz z postępem projektu pojawiały się wymagania dotyczące szczegóło-
wych zasad obsługi wczesnych oraz późnych płatności. W miarę pozna-
wania dziedziny programistka z przyjemnością zobaczyła, że prawie te same
Rysunek 10.13.
***
Oczywiście, istnieć będą również zależności, co nie jest złą rzeczą, jeżeli
dana zależność jest podstawowa dla danego pojęcia. Dalsze ograniczenie
interfejsów w taki sposób, aby działały jedynie na typach prostych, mogłoby
pozbawić je ich funkcjonalności. Jednak w interfejsach wprowadzanych
jest również wiele niepotrzebnych zależności lub nawet całych pojęć.
Najbardziej interesujące obiekty wykonują swoje zadania
w sposób, który nie może być scharakteryzowany jedynie przez
typy proste.
Kolejną powszechną praktyką stosowaną w ulepszaniu projektów jest
coś, co nazywam „ZAMKNIĘCIEM OPERACJI”. Nazwa pochodzi od
najdoskonalszego z systemów pojęciowych — matematyki. 1 + 1 = 2.
Operacja dodawania jest zamknięta w zbiorze liczb rzeczywistych. Mate-
matycy są fanatycznie przeciwni wprowadzaniu nowych pojęć, a właści-
wość zamknięcia udostępnia im sposób na zdefiniowanie operacji bez
dołączania żadnych innych pojęć. Jesteśmy tak przyzwyczajeni do dosko-
nałości matematyki, że trudno nam być może zdać sobie sprawę z tego,
jak wiele możliwości dają jej małe sztuczki. To pojęcie jest również szeroko
stosowane w projektach aplikacji. Na przykład proste użycie transformacji
XSLT powoduje zmianę jednego dokumentu XML na inny. Tego rodzaju
operacja XSLT jest zamknięta w kontekście zbioru dokumentów XML.
Właściwość zamknięcia w olbrzymim stopniu upraszcza interpretację ope-
racji. Łatwiej jest też myśleć o wzajemnym łączeniu operacji zamkniętych.
Dlatego:
Wszędzie, gdzie jest to możliwe, definiuj operację zwraca-
jącą typ identyczny z typem jej argumentów. Jeżeli obiekt imple-
mentujący ma taki sam stan jak użyty w obliczeniach, wtedy jest
on efektywnie argumentem operacji, dlatego jej argumenty oraz
zwracana wartość powinny być takiego samego typu. Tego rodzaju
operacja zamknięta jest w zbiorze instancji tego typu. Zamknięte
operacje udostępniają interfejsy wysokiego poziomu bez wprowa-
dzania żadnej nowej zależności lub innych pojęć.
Przykład
Wybór z kolekcji
Jeżeli w języku Java chciałbyś wybrać podzbiór elementów z kolekcji,
żądasz obiektu klasy Iterator. Następnie przeszukujesz kolekcję, pobie-
rając jej elementy, sprawdzając każdy z nich i prawdopodobnie przenosząc
te, które pasują do nowej kolekcji.
Set employees = (pewien obiekt klasy Set zawierający obiekty klasy
Employee);
Set lowPaidEmployees = new HashSet();
Iterator it = employees.iterator();
while (it.hasNext()) {
***
Wzorce zaprezentowane w tym rozdziale ilustrują ogólny styl projektowania
oraz sposób myślenia o tym projekcie. Stworzenie oczywistego, przewi-
dywalnego programu we właściwy sposób przekazującego informacje
o dziedzinie sprawia, że abstrakcja oraz hermetyzacja stają się efektywne.
Modele mogą być przekształcane w taki sposób, aby obiekty były proste
w użyciu i zrozumiałe, wciąż jednak dysponowały bogatymi, wysokopo-
ziomowymi interfejsami.
Zastosowanie tych technik wymaga całkiem zaawansowanych umie-
jętności projektowania, a nawet niekiedy utworzenia klienta. Przydatność
PROJEKTOWANIA STEROWANEGO PROJEKTEM jest bardzo wraż-
liwa na jakość szczegółowego projektu oraz decyzje implementacyjne. Aby
sprowadzić projekt z właściwej drogi, wystarczy tylko kilku zagubionych
programistów.
Dlatego zespół chcący pielęgnować swoje umiejętności modelowania
oraz projektowania może użyć przedstawionych w tym rozdziale wzorców,
Projektowanie deklaratywne
ASERCJE mogą prowadzić do powstania znacznie lepszych projektów,
nawet jeżeli dysponujemy jedynie relatywnie nieformalnym sposobem
ich testowania. Jednak w programie utworzonym ręcznie nie ma żadnych
gwarancji. Na przykład mógłby on uniknąć ASERCJI, dysponując efek-
tami ubocznymi, które nie zostały jawnie wykluczone. Niezależnie od
tego, jak bardzo nasz projekt jest STEROWANY MODELEM, ostatecz-
nie będziemy tworzyć rzeczywiste procedury wykonujące zadania zdefi-
niowane wcześniej przez współdziałanie pojęć. A dotychczas spędzili-
śmy dużo czasu, tworząc zarysy kodu, który właściwie nie ma żadnego
znaczenia lub logiki. Jest to zajęcie żmudne i obarczone błędami, a jego
rozmiar zaciemnia znaczenie naszego modelu (niektóre języki są lepsze
od innych, jednak wszystkie wymagają od nas wykonania podstawowej
pracy). Przedstawione w tym rozdziale INTERFEJSY UJAWNIAJĄCE
ZAMIAR oraz inne wzorce mogą pomóc, jednak nigdy nie narzucą trady-
cyjnym programom obiektowym formalnego rygoru.
To właśnie są motywacje leżące u podstaw projektowania deklaratywnego.
To pojęcie oznacza dla wielu ludzi różne rzeczy, jednak zazwyczaj wska-
zuje na sposób tworzenia programu lub niektórych jego części, jako rodzaj
specyfikacji wykonawczej. W rzeczywistości aplikacja jest kontrolowana
przez bardzo precyzyjny opis jej właściwości. Służą do tego różne metody,
jak na przykład mechanizm refleksji lub sposób tworzenia kodu na etapie
kompilacji (dzięki automatycznemu tworzeniu kodu na podstawie dekla-
racji). Takie podejście pozwala innemu programiście polegać na deklaracji.
Stanowi ona bowiem absolutną gwarancję.
Tworzenie działającego programu z deklaracji właściwości modelu
jest rodzajem świętego Graala w PROJEKTOWANIU STEROWANYM
MODELEM, jednak w praktyce czai się za nim wiele pułapek. Na przykład
poniżej przedstawione są dwa szczególne problemy, na które natknąłem
się więcej niż raz:
Język deklaratywny, który nie jest wystarczająco ekspresywny do wyko-
nania wszystkich potrzebnych zadań, stanowi jednak szkielet utrud-
niający rozszerzenie programu poza jego automatyczną część.
3
Jest to odmiana języka programowania Lisp — przyp. tłum.
Rysunek 10.14.
public abstract class AbstractSpecification implements
Specification { SPECYFIKACJA
public Specification and(Specification other) { w postaci
return new AndSpecification(this, other); KOMPOZYTU
}
public Specification or(Specification other) {
return new OrSpecification(this, other);
}
public NotSpecification(Specification x) {
wrapped = x;
}
public boolean isSatisfiedBy(Object candidate) {
return !wrapped.isSatisfiedBy(candidate);
}
}
Przykład
Przykładowa implementacja
SPECYFIKACJI KOMPOZYTU
W niektórych środowiskach implementacyjnych bardzo drobne obiekty
nie są obsługiwane w zbyt dobry sposób. Kiedyś pracowałem przy pro-
jekcie, w którym baza obiektowa wymuszała nadawanie każdemu obiek-
towi identyfikatora, a następnie go śledziła. Każdy obiekt zużywał dużo
pamięci i miał niską wydajność, zaś głównym czynnikiem ograniczającym
była całkowita przestrzeń adresowa. Zdecydowałem się na utworzenie
SPECYFIKACJI w ważnych miejscach projektu dziedziny, co wtedy
uważałem za dobry pomysł. Jednak okazało się to ostatecznie pomyłką,
ponieważ użyłem implementacji nieco bardziej rozbudowanej od opisanej
w tym rozdziale. Spowodowało to powstanie milionów bardzo drobnych
obiektów, co zupełnie unieruchomiło system.
Oto kolejny przykład alternatywnej implementacji SPECYFIKACJI
kompozytu, kodującej go w postaci łańcucha znakowego lub tablicy
z wyrażeniami logicznymi interpretowanymi w czasie działania programu.
(Nie przejmuj się, jeżeli wciąż nie widzisz sposobu, w jaki mógłbyś
to zaimplementować. Najważniejszą rzeczą jest zdanie sobie sprawy, że
istnieje wiele różnych sposobów implementacji SPECYFIKACJI z opera-
torami logicznymi, więc jeżeli prosta implementacja okaże się nieprak-
tyczna, zawsze będzie jakieś wyjście).
Jeżeli chciałbyś przetestować kandydata na właściwy kontener,
musiałbyś zinterpretować jego strukturę poprzez wyciągnięcie jednego
elementu, sprawdzenie go, a następnie przejście do kolejnego w sposób
wymagany przez operator. Ostatecznie uzyskałbyś coś takiego:
and(not(armored), not(ventilated))
Subsumpcja
Ta ostatnia funkcjonalność nie jest zazwyczaj wymagana, a jej zaimple-
mentowanie może być trudne, jednak rozwiązuje ona naprawdę trudny
problem. Wyjaśnia również znaczenie SPECYFIKACJI.
Rozważmy ponownie program do pakowania dla magazynu chemicz-
nego z wcześniejszego przykładu. Każda substancja chemiczna (Chemical)
posiadała specyfikację kontenera (Container Specification), a USŁUGA
Packer gwarantowała, że wszystkie wymagania zostaną spełnione, gdy
beczki (Drum) będą przypisywane do kontenerów (Container). Wszystko
dobrze... dopóki ktoś nie zmieni przepisów.
Co kilka miesięcy wydawany jest zestaw nowych reguł, a nasi użyt-
kownicy chcieliby móc utworzyć listę posiadanych chemikaliów, które
będą mieć bardziej zaostrzone wymagania. Oczywiście, moglibyśmy podać
częściową odpowiedź (prawdopodobnie satysfakcjonującą użytkowników),
uruchamiając walidację każdej z beczek w magazynie i używając jedno-
cześnie nowej SPECYFIKACJI, co spowodowałoby odnalezienie tych,
które już dłużej jej nie spełniają. To dałoby użytkownikom informację,
które z beczek w magazynie należy przenieść.
A co w przypadku, gdyby użytkownik poprosił nas o listę chemika-
liów, których obsługa stała się bardziej rygorystyczna? Być może jeszcze
nie mielibyśmy żadnego z nich w magazynie lub już zostały zapakowane
Rysunek 10.15.
SPECYFIKACJA
dla benzyny została
zaostrzona
W języku SPECYFIKACJI powiedzielibyśmy, że nowa SPECYFI-
KACJA jest subsumpcją starej, ponieważ dowolny kandydat na odpowiedni
kontener spełniający nową SPECYFIKACJĘ spełniałby również starą.
W każdym przypadku SPECYFIKACJA jest traktowana jako predykat.
Subsumpcja jest natomiast równoważna logicznej implikacji. Tradycyjny
zapis A→B oznaczać będzie, że wyrażenie A implikuje wyrażenie B,
więc gdy A jest prawdziwe, dotyczyć to będzie również B.
Zastosujmy tę logikę do naszych potrzeb. W chwili, gdy SPECY-
FIKACJA zostanie zmieniona, chcielibyśmy wiedzieć, czy nowa spełnia
wszystkie warunki starej.
Nowa SPECYFIKACJA → Stara SPECYFIKACJA
Zgadza się. Gdy nowa specyfikacja jest prawdziwa, stara też musi speł-
niać ten warunek. Ogólna implementacja logicznej implikacji jest bardzo
trudna, jednak w bardziej konkretnych przypadkach może być prosta. Na
przykład konkretna parametryzowana SPECYFIKACJA może definiować
swoją własną regułę subsumpcji.
4
Subsumpcja w języku logiki formalnej oznacza podporządkowanie jed-
nego pojęcia bardziej ogólnemu — przyp. tłum.
SPECYFIKACJA Arystotelesa
Wszyscy ludzie są śmiertelni. Specification manSpec = new
ManSpecification();
Specification mortalSpec = new
MortalSpecification();
assert manSpec.subsumes(mortalSpec);
Kierunki ataku
W tym rozdziale zaprezentowaliśmy dużo technik pozwalających na wyja-
śnienie zamiaru leżącego u podstaw kodu, zneutralizowanie konsekwencji
jego użycia oraz oddzielenie elementów modelu. Nawet przy ich użyciu
stworzenie tego szczególnego rodzaju projektu jest dość trudne. Nie
możesz po prostu spojrzeć na olbrzymi system i powiedzieć „uelastycz-
nijmy go”. Musisz wcześniej określić swoje cele. Poniżej zaprezentujemy
kilka sposobów podejścia wraz z rozszerzonym przykładem, ukazującym,
w jaki sposób przedstawione wzorce mogą współpracować podczas two-
rzenia dużego projektu.
Definiowanie poddziedzin
Niemożliwe jest stworzenie od razu całego projektu. Musisz od tego
odejść i wybrać z niego pewne elementy. Niektóre aspekty systemu zasu-
gerują Ci możliwe podejścia, które będziesz mógł wykorzystać. Możesz
na przykład dostrzec część modelu, która może być traktowana jako
W miarę możliwości
polegaj na ustalonym formalizmie
Nie codziennie tworzysz od podstaw ściśle pojęciowy szkielet. Niekiedy
możesz najzwyczajniej odkryć i ulepszyć jeden z tych, które spotkałeś na
drodze życia projektu. Bardzo często jednak możesz również zastosować
i zaadaptować systemy pojęciowe, które zostały już dawno ustanowione
w tej lub innych dziedzinach. Niektóre z nich mogły być już ulepszane
przez setki lat. Wiele aplikacji biznesowych wykorzystuje na przykład
księgowość. Ta dziedzina ma już dobrze zdefiniowany i utworzony zestaw
ENCJI oraz reguł, umożliwiających prostą adaptację do postaci dogłębnego
modelu oraz projektu elastycznego.
Istnieje wiele tego rodzaju sformalizowanych szkieletów pojęciowych,
jednak z nich wszystkich moim ulubionym jest matematyka. Zadziwiające
jest, jak przydatne może być wykorzystanie niektórych sztuczek podsta-
wowej arytmetyki. Wiele dziedzin zawiera gdzieś w sobie matematykę.
Postaraj się jej poszukać i ją wydobyć. Specjalizowana matematyka jest
czysta, możliwa do wykorzystania przy użyciu jasnych reguł. Jest także
prosta do zrozumienia przez ludzi. Jednym z przykładów jej użycia
w mojej przeszłości jest „matematyka udziałów”, której opisem zakoń-
czymy ten rozdział.
Przykład
Integracja wzorców — matematyka udziałów
W rozdziale 8. opowiedzieliśmy historię momentu przełomowego w defi-
niowaniu modelu w projekcie zajmującym się systemem pożyczek syn-
dykalnych. Teraz spróbujemy uszczegółowić ten przykład, koncentrując
Rysunek 10.16.
double newLoanShareAmount =
initialLoanShareAmount - paymentShareAmount;
Share newLoanShare =
new Share(owner, newLoanShareAmount);
loanShares.put(owner, newLoanShare);
}
return paymentShares;
}
Rysunek 10.17.
public void applyPrincipalPaymentShares(Map paymentShares) {
Map loanShares = getShares();
Iterator it = paymentShares.keySet().iterator();
while(it.hasNext()) {
Object lender = it.next();
Share paymentShare = (Share) paymentShares.get(lender);
Share loanShare = (Share) loanShares.get(lender);
double newLoanShareAmount = loanShare.getAmount() -
paymentShare.getAmount();
Share newLoanShare = new Share(lender, newLoanShareAmount);
loanShares.put(lender, newLoanShare);
}
}
Rysunek 10.18.
Rysunek 10.20.
Możemy również dla naszych nowych WARTOŚCI SharePie
utworzyć dobrze zdefiniowane ASERCJE. Każda metoda coś oznacza.
public class SharePie {
private Map shares = new HashMap();
Stosowanie wzorców
analitycznych
333
W swojej książce Analysis Patterns: Reusable Object Models Martin
Fowler zdefiniował wzorce analityczne w następujący sposób:
Wzorce analityczne stanowią grupę pojęć reprezentujących powszechne
konstrukcje w modelowaniu biznesowym. Mogą być istotne tylko dla
jednej dziedziny lub też rozciągać się na wiele dziedzin. [Fowler
1997 r., str. 8]
Przykład
Naliczanie odsetek na kontach
W rozdziale 10. przedstawiliśmy kilka możliwych sposobów, które progra-
mista mógłby wykorzystać, poszukując dogłębnego modelu dla danej apli-
kacji wspierającej księgowość. Poniżej natomiast przedstawiony zostanie
jeszcze inny scenariusz. W tym przypadku w celu odnalezienia przydat-
nych pomysłów programistka będzie wertować książkę Fowlera o wzor-
cach analitycznych.
Przypomnijmy sobie przez moment — aplikacja do śledzenia poży-
czek oraz innych aktywów przynoszących odsetki oblicza generowane przez
nie odsetki i opłaty, a także śledzi wpłaty od pożyczkobiorcy. Wartości te
są przetwarzane przez nocny proces wsadowy, a następnie przekazywane
do starego systemu księgowego, ze wskazaniem, do której księgi rachun-
kowej mają być wpisane. Nasz projekt działa, lecz jest dziwny w użyciu,
trudny do zmiany i nie przekazuje za dużo informacji o sobie.
Rysunek 11.1.
Początkowy
diagram klas
Rysunek 11.2.
Prosty model
księgowy
Rysunek 11.3.
Model transakcyjny
Rysunek 11.4.
Programiści zastanowili się wspólnie nad wprowadzeniem zmiany Nowa propozycja
do ich pierwotnego modelu, uwzględniając w nim niektóre elementy, modelu
o których przeczytali w książce. Następnie udali się do eksperta dziedzi-
nowego (Ekspert) w celu przedyskutowania nowych pomysłów dotyczą-
cych modelu.
Programista 1: W tym nowym modelu do konta odsetkowego (Interest
Account) wstawiamy nowy wpis (Entry) dla każdego zarobionego
procentu, a nie modyfikujemy tak jak poprzednio wartości interest
DueAmount. Następnie wstawiamy kolejny wpis (Entry) dla bilansu
płatności.
Ekspert: Czyli teraz będziemy w stanie zaobserwować zarówno przyrosty
odsetkowe, jak i historię płatności? Właśnie na to czekaliśmy.
Reguły księgowania
Systemy księgowe bardzo często udostępniają wiele widoków tych
samych informacji finansowych. Jedno konto mogłoby śledzić dochód,
a inne szacunkowy podatek od tego dochodu. Jeżeli system ma
w założeniu automatycznie uaktualniać konto podatkowe szacunkową
kwotą, wtedy oba te konta stają się ze sobą powiązane. Istnieją sys-
temy, w których większość wpisów na kontach jest wynikiem tego
rodzaju reguł. W takich systemach zależna logika staje się przyczyną
bałaganu. Nawet w niewielkich systemach te wzajemne księgowania
mogą być skomplikowane. Pierwszym krokiem na drodze do oswo-
jenia tego gąszczu zależności jest ujawnienie reguł poprzez wpro-
wadzenie nowych obiektów.
Rysunek 11.9.
Programista 1: Co oznacza ta „usługa księgowania ” (Posting Service)? Podejście do użycia
Programista 2: To FASADA wystawiająca API aplikacji księgowej i udo- reguł księgowania
w skrypcie
stępniająca je USŁUDZE. W rzeczywistości stworzyłem ją już
chwilę temu, aby uprościć kod tego skryptu wsadowego. W ten
Rysunek 11.10.
Diagram klas
z regułami
księgowymi
Wzorce analityczne
stanowią wiedzę do wykorzystania
Gdy będziesz mieć na tyle szczęścia, aby znaleźć wzorzec analityczny, okaże
się, że rzadko kiedy będzie stanowić on odpowiedź na Twoje określone
potrzeby. Często wciąż jednak będzie oferować cenne wskazówki, pomocne
w dochodzeniu do niej, oraz będzie zawierać jasno wyrażone słownictwo.
Powiązanie wzorców
projektowych
z modelem
349
elementów projektowych, które rozwiązały problemy powszechnie spo-
tykane w różnorodnych kontekstach. Same wzorce oraz motywacja leżąca
u ich podstaw są typowo techniczne. Jednak podzbiór niektórych elemen-
tów może zostać zastosowany w szerszym kontekście modelowania oraz
projektowania dziedziny, ponieważ odpowiadają one ogólnym pojęciom,
które pojawiają się w wielu dziedzinach.
Oprócz podstawowych wzorców projektowych opisanych w książce
Gammy, w ciągu ostatnich lat zostało zaprezentowanych wiele innych.
Niektóre z nich korespondują z głębokimi pojęciami pojawiającymi się
w dziedzinach. Byłoby bardzo miło, gdybyśmy mogli skorzystać z efek-
tów tej pracy. Aby użyć wzorców projektowych w projektowaniu stero-
wanym dziedziną, musimy rozpatrywać je jednocześnie na dwóch różnych
poziomach. Na jednym są one technicznymi wzorcami projektowymi
w kodzie, a na innym stanowią wzorce projektowe w modelu.
Przeanalizowanie przykładowego wzorca z książki pt. Wzorce pro-
jektowe pokaże nam, w jaki sposób wzorzec powstały z myślą o zastoso-
waniu w projekcie może być użyty w modelu dziedziny. Wyjaśni nam
także różnicę pomiędzy technicznym wzorcem projektowym a wzorcem
dziedziny. Wzorce o nazwach KOMPOZYT oraz STRATEGIA zaprezen-
tują nam sposób, w jaki klasyczne wzorce projektowe mogą być zastoso-
wane do problemów dziedziny, dzięki spojrzeniu na nie w inny sposób.
Przykład
Polityki odnajdowania tras
Specyfikacja trasy (Route Specification) przekazywana jest do usługi
trasowania (Routing Service), której zadaniem jest utworzenie szcze-
gółowego planu podróży (Itinerary), spełniającego SPECYFIKACJĘ.
USŁUGA jest zoptymalizowanym silnikiem, który może być skonfi-
gurowany w celu odnalezienia najszybszej bądź najtańszej trasy.
Rysunek 12.1.
Parametryzowany
interfejs USŁUGI
będzie wymagać
logiki warunkowej Konfiguracja wygląda poprawnie, jednak po bardziej szczegółowym
przejrzeniu kodu używanego do trasowania dostrzeżemy w nim w prawie
każdym obliczeniu instrukcje warunkowe, służące do wyboru pomiędzy
***
Jeżeli w warstwie dziedziny będziemy używać wzorca projektu technicz-
nego, będziemy również musieli dodać dodatkową warstwę określającą
jego znaczenie. Dodatkowo w przypadku, gdy STRATEGIA korespon-
duje z rzeczywistą strategią biznesową lub polityką, wzorzec ten staje się
czymś więcej niż tylko przydatną techniką implementacyjną (choć ta cecha
tego wzorca jest również bardzo cenna).
Konsekwencje stosowania danego wzorca projektowego mają również
zastosowanie w przypadku użycia go do modelowania dziedziny. Na
przykład książka pt. Wzorce projektowe (Gamma i inni) wskazuje, że kod
klienta musi uwzględniać różne strategie, co także musi być wzięte pod
uwagę w przypadku użycia go do modelowania. Istnieją również kwestie
całkowicie związane z implementacją — STRATEGIA na przykład może
zwiększyć liczbę obiektów w aplikacji. Jeżeli jest to problemem, wtedy
narzut wydajnościowy może być ograniczony poprzez zaimplementowanie
STRATEGII jako bezstanowych obiektów, które mogą być współ-
dzielone w danym kontekście. Również cała długa dyskusja na temat
Przykład
Trasy statków złożone z tras
Całkowita trasa dostarczania ładunku jest skomplikowana. Na początku
kontener musi być dostarczony ciężarówką na stację kolejową, następnie
przewieziony koleją do portu, później przetransportowany statkiem (lub
prawdopodobnie wieloma statkami) do innego portu, a na koniec prze-
wieziony tradycyjnym transportem do punktu docelowego.
Zespół programujący aplikację utworzył model obiektowy wyraża-
jący te dowolnie długie łańcuchy etapów składających się na trasę.
Dzięki temu modelowi programiści byli w stanie utworzyć obiekty
trasy (Route) na podstawie rezerwacji. Mogli przetworzyć etapy w plan
operacyjny w celu obsługi ładunku krok po kroku. Wtedy odkryli coś spe-
cjalnego. Programiści zawsze traktowali trasę jako dowolny niezróżnico-
wany łańcuch etapów.
Jednak okazało się, że eksperci dziedzinowi widzą trasę jako sekwencję
pięciu logicznych segmentów.
KOMPOZYT 357
Rysunek 12.3.
Schemat „trasy”
złożonej z „etapów”
Rysunek 12.4.
Diagram klas trasy
(Route) złożonej
z etapów (Leg)
Rysunek 12.5.
Koncepcja trasy
według
Te etapy mogą być planowane w różnych momentach przez różnych
programistów
ludzi, dlatego powinny być rozpatrywane niezależnie, a etapy „od drzwi”
są całkiem inne od pozostałych, ponieważ polegają na lokalnie wynaję-
tych ciężarówkach, a niekiedy nawet na transporcie klienta, w przeci-
wieństwie do szczegółowo zaplanowanych etapów transportu kolejowego
lub morskiego.
Rysunek 12.7.
Pod względem strukturalnym powyższy model nie jest wcale zły,
Rozszerzony
jednak utracona została jednolitość przetwarzania planu operacyjnego, co diagram klas
sprawia, że kod lub nawet opis logiki staje się znacznie bardziej skompli- reprezentujący trasę
kowany. Zaczynają się również wyłaniać inne problemy. Każde przejrzenie (Route)
trasy angażuje wiele kolekcji obiektów różnego rodzaju.
Zastosuj KOMPOZYT. Dla niektórych klientów byłoby przyjemnie,
gdyby mogli oni traktować różne poziomy przedstawionego modelu jed-
nolicie, to znaczy jako trasy złożone z tras. Pod względem pojęciowym
KOMPOZYT 359
powyższy widok jest przyzwoity. Każdy poziom trasy jest przemieszcze-
niem kontenera z jednego miejsca do innego, począwszy od najbardziej
ogólnego poziomu, aż do poszczególnych etapów (patrz rysunek 12.8).
Rysunek 12.8.
Diagram klas
używający
KOMPOZYTU
***
Refaktoryzacja
ku głębszemu
zrozumieniu
363
Początek
Refaktoryzacja ku głębszemu zrozumieniu może rozpocząć się na wiele
różnych sposobów. Może stanowić ona odpowiedź na problem występu-
jący w kodzie — jakąś szczególną złożoność lub dziwny fragment kodu.
Zamiast po prostu modyfikować kod w standardowy sposób, programiści
wyczuwają, że główna przyczyna ich problemu leży w modelu dziedziny.
Być może brakuje w nim pewnego pojęcia albo nieprawidłowe są pewne
relacje.
Takie przemyślenia mogą wystąpić, jeżeli kod wydaje się niepo-
rządny lub jeżeli język modelu sprawia wrażenie odłączonego od tego,
jakim posługują się eksperci dziedziny, a także jeżeli nowe wymagania
nie pasują w sposób naturalny do modelu. Refaktoryzacja może być
również konsekwencją zwiększonej wiedzy na temat dziedziny w sytu-
acji, gdy programista, który zyskał jej lepsze zrozumienie, dostrzeże szansę
stworzenia bardziej przejrzystego lub przydatnego modelu.
Najtrudniejszą rzeczą jest najczęściej dostrzeżenie miejsca wystą-
pienia problemu. Kiedy już to się stanie, programiści mogą w sposób
konsekwentny poszukiwać elementów nowego modelu. W tym celu mogą
konsultować się ze swoimi kolegami oraz ekspertami dziedzinowymi. Mogą
również skorzystać z usystematyzowanej wiedzy spisanej w postaci wzor-
ców analitycznych lub projektowych.
Zespoły poszukiwawcze
Niezależnie od tego, jakie jest źródło niezadowolenia, kolejnym etapem
będzie poszukiwanie udoskonalenia, które przyczyni się do komunikowania
dziedziny przez model w sposób bardziej przejrzysty i naturalny. Mogłoby
to wymagać jedynie niewielkiej, od razu dostrzegalnej zmiany, której można
dokonać w kilka godzin. W takiej sytuacji zmiana przypomina tradycyjną
refaktoryzację. Jednak poszukiwanie nowego modelu może równie dobrze
wymagać większej ilości czasu i zaangażowania w tym celu większej
liczby ludzi.
Pomysłodawca zmiany dobiera sobie kilku innych programistów,
mających doświadczenie w rozwiązywaniu tego rodzaju problemów, zna-
jących ten obszar dziedziny lub też mających odpowiednie umiejętności
związane z modelowaniem. Jeżeli będą występować jakieś subtelności,
z pewnością poprosi on również o współpracę eksperta dziedzinowego.
Taka grupa czterech lub pięciu osób udaje się następnie do pokoju kon-
ferencyjnego lub kawiarni i zamyka się tam na dyskusji trwającej pół lub
Wcześniejsze odkrycia
Nie zawsze konieczne jest odkrywanie koła na nowo. Proces dyskusji
o brakujących pojęciach i lepszych modelach tworzy duży potencjał do
przyswajania wiedzy z dowolnych źródeł, łączenia jej z lokalną wiedzą
Wyczucie czasu
Jeżeli będziesz czekał na całkowite usprawiedliwienie dla zmiany, to już czekasz
zbyt długo. Projekt cały czas generuje duże koszty, a odłożenie zmiany
w czasie może skutkować utrudnieniem wykonania zmiany, ponieważ
w tym czasie kod docelowy może zwiększyć swoją złożoność lub zostać
osadzony w innym kodzie.
Ciągła refaktoryzacja stała się już powszechną praktyką, jednak więk-
szość zespołów projektowych wciąż jej nie ufa. Dostrzegają one jedynie
ryzyko zmiany kodu lub koszt czasu programistów wymaganego do zmiany
kodu. O wiele trudniej jednak jest dostrzec ryzyko utrzymywania dziw-
nego projektu lub też koszt szukania jego obejść. Programiści pragnący
wykonać refaktoryzację są bardzo często proszeni o uzasadnienie swojej
decyzji. Chociaż wydaje się to bardzo rozsądne, to jednak jest bardzo trudne
i w rezultacie refaktoryzacja jest pomijana (lub ukrywana). Rozwój oprogra-
mowania nie jest przewidywalnym procesem, w którym można łatwo obli-
czyć korzyści z przeprowadzenia zmiany lub też koszty jej niewykonania.
Refaktoryzacja ku głębszemu zrozumieniu musi stać się częścią cią-
głego odkrywania wiedzy eksperckiej w dziedzinie, edukowania programi-
stów, a także wspólnego zrozumienia dziedziny przez programistów oraz
ekspertów. Dlatego wykonuj tę refaktoryzację, gdy:
Projekt nie wyraża bieżącego zrozumienia dziedziny przez zespół.
Ważne pojęcia są ukryte w dziedzinie (a Ty widzisz sposób na ich
ujawnienie).
Dostrzegasz możliwości uelastycznienia ważnych części projektu.
Agresywne nastawienie nigdy nie uzasadnia żadnej zmiany. Nigdy
nie wykonuj refaktoryzacji dzień przed przekazaniem kodu do produkcji.
Nie wprowadzaj „projektów elastycznych” będących jedynie demonstracją
technicznej wirtuozerii, jednak nieoddających istoty dziedziny. Tak samo,
niezależnie od jego eleganckiej budowy, nigdy nie wprowadzaj „modelu
370 CZĘŚĆ IV
aplikacjami, że bardzo mała część dziedziny zdaje się wyglądać podobnie.
W takim przypadku ujednolicenie modeli wyrażonych pośrednio w tych
częściach może być zbyt trudnym zadaniem. Decydując się na jawne zde-
finiowanie KONTEKSTU ZWIĄZANEGO, do którego będzie mieć
zastosowanie model, wraz z jego relacjami z innymi kontekstami (w przy-
padku takiej konieczności), osoba odpowiedzialna za model może uniknąć
zniekształcenia tego kontekstu.
Destylacja redukuje bałagan i pomaga w odpowiednim skoncen-
trowaniu uwagi. Bardzo często najwięcej wysiłku pochłaniają problemy
powstałe na brzegach dziedziny. Aby tego uniknąć, całkowity model dzie-
dziny musi zwrócić uwagę na najistotniejsze wartości oraz specjalne
aspekty systemu, tak aby nadać im odpowiednie znaczenie. Chociaż nie-
które ze wspierających komponentów wciąż będą krytyczne, muszą one
zostać umieszczone w odpowiedniej perspektywie. Takie podejście nie tylko
pomaga skierować wysiłki na żywotne części systemu, lecz także zapobiega
utracie całkowitej jego wizji. Strategiczna destylacja może nadać dużym
modelom przejrzystość. A dzięki bardziej uporządkowanemu widokowi
projekt GŁÓWNEJ DZIEDZINY może stać się bardziej przydatny.
Całego obrazu dopełniają struktury dużej skali. W bardzo złożonym
modelu możesz nie dostrzec jego istoty. W tym zadaniu będzie pomagać
destylacja, umożliwiając skoncentrowanie uwagi na głównej istocie modelu,
a także pozycjonując inne elementy w roli wspierającej. Jednak ich relacje
mogą być wciąż mylące, jeżeli nie zastosujemy pewnych elementów pro-
jektowych oraz wzorców dla całego systemu. Dlatego zaprezentuję kilka
podejść do modelowania struktur dużej skali, a następnie rozwinę szcze-
gółowo jeden z wzorców — WARSTWY ODPOWIEDZIALNOŚCI, aby
przybliżyć implikacje stosowania struktur tego rodzaju. Przedstawione
struktury będą jedynie przykładami. Nie zamierzam zamieszczać w książce
wyczerpującego ich katalogu. Jeżeli zajdzie taka potrzeba, trzeba zdefinio-
wać nowe struktury; możliwa jest również modyfikacja struktur istniejących.
Służyć będzie do tego proces zwany PORZĄDKOWANIEM EWOLU-
CYJNYM. Niektóre ze struktur tego rodzaju mogą wnieść do projektu
spójność, zdecydowanie przyspieszającą prace projektowe oraz wzmac-
niającą integrację.
Powyższe trzy pryncypia są przydatne nawet samodzielnie, lecz
szczególną moc ujawniają dopiero w przypadku, gdy zostaną zastosowane
wspólnie. Pomagają one utworzyć dobre projekty — nawet dla rozległych
systemów, których nikt już nie rozumie. Struktury dużej skali nadają roz-
bieżnym częściom spójność, umożliwiając ich połączenie. Struktura wraz
z destylacją pozwalają zrozumieć złożone zależności między częściami
i jednocześnie utrzymują spójny obraz całego systemu. KONTEKSTY
372 CZĘŚĆ IV
ROZDZIAŁ 14.
Utrzymywanie
integralności modelu
373
danych potwierdziło, że była to wartość wymagana. Co więcej, system
nadawał jej nawet wartość domyślną.
Problem polegał na tym, że te dwie grupy miały różne modele, z czego
nie zdawały sobie sprawy. Nie było także procesu, który pomógłby im to
wykryć. Każda grupa zrobiła założenie o naturze obciążenia, które było
przydatne w danym kontekście (fakturowania klienta lub płatności rachun-
ków). Połączenie kodów bez rozwiązania tych sprzeczności poskutkowało
stworzeniem niestabilnej aplikacji.
Gdyby tylko członkowie tych dwóch grup byli świadomi tej rzeczy-
wistości, mogliby zdecydować, jakie wybrać rozwiązanie. Mogliby na przy-
kład pracować razem nad utworzeniem wspólnego modelu, a następnie
w celu uniknięcia przyszłych niespodzianek stworzyć zestaw testów auto-
matycznych. Mogliby też najzwyczajniej uzgodnić, że będą tworzyć
oddzielne modele i nie będą dotykać nawzajem swojego kodu. W każ-
dym z tych przypadków pierwszym zadaniem byłoby jasne ustalenie
granic stosowania danego modelu.
Co w takim razie oba zespoły uczyniły, gdy zdały sobie sprawę z pro-
blemu? Stworzyły oddzielne klasy Customer Charge oraz Supplier
Charge i zdefiniowały każdą z nich zgodnie ze swoimi potrzebami. Bez-
pośredni problem został rozwiązany i projektanci mogli powrócić do
swoich poprzednich zadań. Jak dobrze...
Chociaż bardzo rzadko myślimy o tym bezpośrednio, najważniej-
szym wymaganiem w stosunku do modelu jest to, aby był on wewnętrz-
nie spójny, by jego pojęcia miały zawsze to samo znaczenie oraz aby nie
zawierał wykluczających się reguł. Wewnętrzna spójność modelu —
polegająca na tym, aby każde z jego określeń było jednoznaczne, a reguły
nie stały ze sobą w sprzeczności — nazywa się unifikacją. Model, który
nie jest logicznie spójny, będzie nieistotny. W idealnym świecie mieli-
byśmy pojedynczy model rozciągający się na całą dziedzinę przedsię-
biorstwa. Byłby on zunifikowany, bez żadnych wykluczających się lub
nakładających definicji czy określeń. Każde logiczne zdanie o dziedzinie
byłoby spójne.
Jednak świat rozwoju dużych systemów nie jest tak idealny. Utrzy-
mywanie takiego poziomu unifikacji dla całego przedsiębiorstwa przy-
nosi więcej kłopotu, niż jest to warte. Niezbędne jest umożliwienie nie-
zależnego rozwoju wielu modeli w różnych częściach systemu, jednak
powinniśmy być bardzo ostrożni w tym, którym częściom pozwolimy się
od siebie oddalać oraz jakie powinny być ich wzajemne związki. Istotne
części modelu powinny być ze sobą ściśle zunifikowane. Nic takiego nie
wydarzy się samo lub jedynie dzięki pobożnym życzeniom. Można to
zapewnić jedynie poprzez świadome decyzje projektowe oraz ustanowie-
Rysunek 14.1.
Mapa nawigacji
dla wzorców
integralności
modelu
Komórki mogą istnieć, ponieważ ich ściany definiują, co jest wewnątrz, a co na zewnątrz,
a także co może przejść przez taką ścianę.
Przykład
Dwa KONTEKSTY w aplikacji transportowej
Wróćmy ponownie do systemu obsługi ładunków. Jedną z głównych funk-
cjonalności aplikacji było automatyczne trasowanie ładunków w momencie
ich rezerwacji. Model przypominał ten na rysunku 14.2.
Usługa trasowania (Routing Service) jest USŁUGĄ hermetyzu-
jącą w sobie mechanizm leżący u podstaw INTERFEJSU UJAWNIA-
JĄCEGO ZAMIAR (ang. INTENTION-REVEALING INTERFACE),
złożonego z FUNKCJI BEZ EFEKTÓW UBOCZNYCH (ang. SIDE-
EFFECT FREE FUNCTIONS). Rezultaty tych funkcji scharakteryzowane
są za pomocą ASERCJI.
1. Interfejs deklaruje, że w przypadku przekazania obiektu Route
Specification zwracany jest obiekt Itinerary.
2. ASERCJA określa, że zwrócony obiekt Itinerary będzie spełniać
obiekt klasy Route Specification przekazany do usługi.
Niestety, nic nie jest powiedziane o tym, w jaki sposób to bardzo trudne
zadanie zostało wykonane. A teraz zajrzyjmy za kulisy, aby poznać cały
mechanizm.
Początkowo w projekcie, którego ten przykład dotyczy, byłem zbyt
dogmatyczny, jeżeli chodzi o zawartość obiektu Routing Service. Chcia-
łem, aby rzeczywiste operacje trasowania wykonywane były przez roz-
szerzony model dziedziny, który reprezentowałby podróże i w sposób
bezpośredni wiązał je z odpowiednimi etapami (Leg) w planie podróży
(Itinerary). Jednakże zespół pracujący nad problemem trasowania
wskazał, iż poprawne wykonanie tego zadania i oparcie go na powszech-
nie stosowanych algorytmach wymaga, aby rozwiązanie polegało na
zoptymalizowanej sieci, w której każdy etap podróży reprezentowałby
element macierzy. W tym celu członkowie zespołu nalegali na stworze-
nie oddzielnego modelu operacji logistycznych.
Ponieważ swoje oczekiwania dotyczące obliczeń trasy wyrazili w bar-
dzo przejrzysty sposób, zgodziłem się na ich pomysł, nie mając lepszego.
W rezultacie utworzyliśmy dwa oddzielne KONTEKSTY ZWIĄZANE,
z których każdy miał osobną organizację operacji transportowych (patrz
rysunek 14.3)
Nasze wymaganie zakładało wzięcie żądania z usługi Routing
Service, przekształcenie go do postaci zrozumiałej dla usługi przeszuki-
wania sieci (Network Traversal Service), a następnie pobranie wyniku
jej działania i ponowne przekształcenie do postaci, jaką miała zwracać
usługa trasowania (Routing Service).
Rysunek 14.5.
Tłumaczenie trasy
w usłudze
przeszukiwania
sieci połączeń
Rysunek 14.6.
Tłumacz
dwustronny
Rysunek 14.7.
Implementacja usługi trasowania (Routing Service) polegać teraz
będzie głównie na delegowaniu żądań do tłumacza (Translator) oraz
usługi przeszukiwania sieci (Network Traversal Service). Jej pojedyncza
operacja mogłaby wyglądać podobnie do poniższej:
public Itinerary route(RouteSpecification spec) {
Booking_TransportNetwork_Translator translator =
new Booking_TransportNetwork_Translator();
1
Prezydent Reagan przetłumaczył stare powiedzenie rosyjskie, które dosko-
nale oddawało istotę rzeczy dla obu stron — jest to kolejna metafora dla łączenia
kontekstów.
***
Sukces ZESPOŁÓW KLIENTA – DOSTAWCY jest bardziej prawdopo-
dobny, gdy dwa zespoły pracują pod tym samym kierownictwem, a więc
ostatecznie współdzielą te same cele, lub w przypadku różnych firm,
które rzeczywiście pełnią takie role. Jeżeli jednak nie istnieje nic, co
mogłoby zmotywować zespół nadrzędny, wtedy sytuacja wygląda inaczej...
KONFORMISTA 403
Naśladowanie zostanie opóźniony do czasu, aż zespół ostatecznie nauczy się żyć
nie zawsze jest złe z tym, co otrzymał. Interfejs dopasowany do potrzeb zespołu pod-
Używając gotowego
komponentu z dużym
rzędnego nie wchodzi w grę.
interfejsem, powinieneś W takiej sytuacji istnieją trzy możliwe ścieżki. Jedną jest całkowite
zazwyczaj dostosować porzucenie zespołu nadrzędnego. To wyjście powinno być rozpatrywane
się do modelu w tym realistycznie, bez tworzenia założeń, że zespół nadrzędny uwzględni
komponencie.
Ponieważ zarówno
potrzeby zespołu podrzędnego. Niekiedy przeszacowujemy wartość lub nie
komponent, doszacowujemy kosztu zależności tego rodzaju. Jeżeli zespół podrzędny
jak i aplikacja zdecyduje się przeciąć pępowinę, oba zespoły pójdą ODDZIELNYMI
są zdecydowanie DROGAMI (patrz opis wzorca w dalszej części tego rozdziału).
KONTEKSTAMI
ZWIĄZANYMI,
Niekiedy wartość używania oprogramowania nadrzędnego jest tak
opartymi na organizacji duża, że ta zależność musi być utrzymywana (lub też podjęta została poli-
oraz sposobie kontroli tyczna decyzja, której zespół nie może zmienić). W takim przypadku pozo-
zespołu, dla małych stają otwarte dwie ścieżki. Wybór pomiędzy nimi zależy od jakości oraz
zmian formatu mogą
być wymagane adaptery,
stylu projektu nadrzędnego. Jeżeli z tym projektem trudno jest pracować —
jednak sam model być może ze względu na brak hermetyzacji, z powodu dziwnych abstrakcji
powinien pozostać lub modelowania w paradygmacie, którego zespół nie może używać —
równoważny. zespół podrzędny musi utworzyć swój własny model. Będzie również
W przeciwnym
razie powinieneś
musiał wziąć całą odpowiedzialność za warstwę tłumaczenia, która praw-
kwestionować wartość dopodobnie będzie złożona (patrz WARSTWA ZAPOBIEGAJĄCA
posiadania komponentu. USZKODZENIU w dalszej części tego rozdziału).
Jeżeli jest on na tyle Z drugiej strony, jeżeli jakość kodu nie jest aż tak zła, a styl jest całkiem
dobry, aby przynosić
Ci korzyści,
zgodny, najlepszym wyjściem może być całkowite porzucenie niezależnego
prawdopodobnie modelu. Taka właśnie sytuacja wymaga użycia KONFORMISTY (ang.
osadzona jest w nim CONFORMIST).
jakaś wiedza. W swojej Dlatego:
wąskiej dziedzinie
komponent ten może
Wyeliminuj złożoność tłumaczenia pomiędzy KONTEK-
być znacznie bardziej STAMI ZWIĄZANYMI, niewolniczo przyporządkowując się do
zaawansowany modelu zespołu nadrzędnego. Chociaż będzie to przeszkadzać
niż Twoje własne
projektantom aplikacji podrzędnej i prawdopodobnie nie skoń-
zrozumienie.
Twój model czy się stworzeniem idealnego modelu dla aplikacji, wybór KON-
prawdopodobnie FORMISTY ogromnie upraszcza integrację. Będziesz również
rozszerza się poza musiał współdzielić z zespołem dostawcy JĘZYK WSZECH-
zakres tego komponentu,
a Twoje własne pojęcia
OBECNY. Dostawca będzie kierować, dlatego warto ułatwić mu
będą rozwijać się komunikację. Altruizm może być wystarczającym argumentem
dla tych innych części. do przekonania ich do podzielenia się informacją.
Jednak w miejscu ich Podjęcie takiej decyzji zwiększa zależność od aplikacji nadrzędnej
połączenia Twój
model będzie
i ogranicza możliwości aplikacji do tych w aplikacji nadrzędnej — umoż-
KONFORMISTĄ, liwiając wyłącznie dodawanie rozszerzeń. Jest to wyjście niezbyt pocią-
naśladując model gające, dlatego wybieramy je prawdopodobnie rzadziej, niż powinniśmy.
komponentu.
KONFORMISTA 405
WARSTWA ZAPOBIEGAJĄCA
USZKODZENIU
Nowe systemy prawie zawsze muszą być zintegrowane ze starymi lub też
innymi dysponującymi własnym modelem. Warstwy tłumaczące mogą
być proste, a nawet eleganckie, gdy łączą dobrze zaprojektowane KON-
TEKSTY ZWIĄZANE z zespołami chętnymi do współpracy. Kiedy jed-
nak druga strona granicy zacznie się przesączać, warstwa odpowiedzialna
za tłumaczenie może przyjąć ton bardziej defensywny.
***
Kiedy tworzony jest nowy system, który musi mieć duży
interfejs z innym systemem, trudność powiązania dwóch modeli
może ostatecznie przeważyć nad celem nowego modelu, zmu-
szając go do zmodyfikowania w sposób przypominający model
innego systemu. Modele starych systemów są zazwyczaj słabe,
a nawet taki, który stanowi wyjątek od tej zasady i jest dobrze
utworzony, może nie pasować do potrzeb obecnego projektu.
Wciąż jednak integracja może przynosić dużą wartość, a niekiedy
jest ona niezbędnym wymaganiem.
Rozwiązaniem nie jest unikanie jakiejkolwiek integracji z innymi
systemami. Uczestniczyłem w projektach, w których w sposób entuzja-
styczny nastawiano się na zastąpienie wszystkich starych aplikacji, jednak
systemem może być to za duży wysiłek na jeden raz. Poza tym integracja
z istniejącym jest bardzo cennym sposobem na jego ponowne wykorzy-
stanie. W dużych projektach jeden podsystem będzie często mieć interfejs
z wieloma innymi, niezależnie utworzonymi podsystemami. Będą one
w inny sposób odzwierciedlać problem dziedziny. Przy łączeniu systemów
Implementacja WARSTWY
ZAPOBIEGAJĄCEJ USZKODZENIU
Jednym ze sposobów organizacji projektu WARSTWY ZAPOBIEGAJĄ-
CEJ USZKODZENIU jest połączenie FASAD, ADAPTERÓW (wszystkie
wzorce z książki Gammy z roku 1995) oraz tłumaczy z mechanizmami
komunikacji oraz transportu, wymaganymi zazwyczaj w celu prowadze-
nia dialogu pomiędzy systemami.
Często musimy zintegrować się z systemami dysponującymi cięż-
kimi i skomplikowanymi interfejsami. Jest to problem dotyczący imple-
mentacji, a nie różnic pojęciowych, skłaniający do użycia WARSTWY
Rysunek 14.8.
Struktura
Do połączenia obu podsystemów będziesz zazwyczaj potrzebować
WARSTWY mechanizmu komunikacji, a same podsystemy mogą równie dobrze
ZAPOBIEGAJĄCEJ znajdować się na oddzielnych serwerach. W takim przypadku musisz
USZKODZENIU zdecydować, w którym miejscu umieścić łącza komunikacyjne. Jeżeli
nie masz dostępu do drugiego podsystemu, możesz umieścić te łącza
pomiędzy FASADĄ a drugim podsystemem. Jeżeli jednak FASADA
może być zintegrowana bezpośrednio z drugim podsystemem,
dobrym rozwiązaniem byłoby umieszczenie łącza komunikacyjnego
pomiędzy ADAPTEREM a FASADĄ, ponieważ protokół FASADY jest
prawdopodobnie prostszy od tego, co ona przykrywa. Będą również
Przykład
Stara aplikacja do rezerwacji
Aby jak najszybciej otrzymać pierwszą małą działającą wersję, napiszemy
minimalną aplikację, która będzie mogła skonfigurować transport, a następ-
nie przekazać go do starego systemu w celu wykonania rezerwacji oraz
dalszych operacji, używając do tego warstwy transportowej. Ponieważ two-
rzymy warstwę transportową specjalnie w celu ochrony naszego modelu
przed wpływem starego projektu, to tłumaczenie będzie WARSTWĄ
ZAPOBIEGAJĄCĄ USZKODZENIU.
Początkowo WARSTWA ZAPOBIEGAJĄCA USZKODZENIU
będzie akceptować obiekty reprezentujące ładunek, przekształcać je, prze-
kazywać do starego systemu i żądać rezerwacji. Następnie będzie ona
przechwytywać z powrotem potwierdzenie rezerwacji i tłumaczyć je na
obiekt potwierdzenia w nowym projekcie. Taka izolacja pozwoli nam
utworzyć naszą nową aplikację w sposób w dużej mierze niezależny od
starej, chociaż będziemy musieli zainwestować trochę czasu w tłuma-
czenie.
Z każdą nową wersją opracowywany system może przejmować
coraz więcej funkcjonalności od starego lub po prostu dodawać nowe
możliwości bez zastępowania starych. Wszystko zależeć będzie od póź-
niejszych decyzji. Taka elastyczność oraz możliwość ciągłego użytkowania
połączonych systemów podczas stopniowego migrowania funkcjonal-
ności prawdopodobnie uzasadnia inwestycję w tworzenie WARSTWY
ZAPOBIEGAJĄCEJ USZKODZENIU.
Opowieść ku przestrodze
Aby uchronić swoje granice przed najazdem dzikich hord nomadów,
starożytni Chińczycy zbudowali Wielki Mur. Nie był on barierą nie do
pokonania, jednak umożliwiał handel z sąsiadami, będąc jednocześnie
przeszkodą dla inwazji oraz innego niepożądanego wpływu. Przez dwa
Przykład
Odchudzony projekt ubezpieczeń
Zespół projektowy rozpoczął pracę nad tworzeniem nowego oprogra-
mowania do obsługi roszczeń ubezpieczeniowych, który w jednym sys-
temie zintegrowałby wszystko, począwszy od obsługi agentów ubezpiecze-
niowych, a skończywszy na procesie obsługi wniosków. Po roku pracy
zespół utknął w martwym punkcie. Z powodu paraliżu analitycznego
oraz konieczności dokonania dużych wcześniejszych inwestycji w infra-
strukturę członkowie zespołu nie byli w stanie przedstawić niczego kon-
kretnego coraz bardziej zniecierpliwionemu kierownictwu. A mówiąc
bardziej poważnie, zakres prac, które planowali wykonać, przerósł ich
zdecydowanie.
Nowy kierownik projektu zebrał na tydzień cały zespół w jednym
pokoju, aby utworzyć nowy plan projektu. Pracę rozpoczęli od stworze-
nia listy wymagań oraz oszacowania ich trudności i przyporządkowania
im stopnia ważności. Najtrudniejsze i najmniej ważne wymagania zostały
bezlitośnie odrzucone. Następnie zespół zaczął porządkować pozostałą
listę. W czasie tego tygodnia podjętych zostało wiele mądrych decyzji,
jednak na końcu tylko jedna z nich okazała się istotna. W pewnym momen-
cie zdano sobie sprawę, że istniały funkcjonalności, dla których integracja wnosiła
tylko niewiele wartości dodanej. Na przykład osoby dokonujące zmian w sys-
temie potrzebowały dostępu do pewnych istniejących baz danych, a moż-
liwy obecnie dostęp okazał się niewygodny. Jednak pomimo że użytkownicy
potrzebowali tych danych, okazało się, że żadna z funkcjonalności w proponowa-
nym systemie ich nie używała.
Członkowie zespołu zaproponowali szereg sposobów na ułatwienie
dostępu. W jednym z przypadków kluczowy raport mógł być wyekspor-
towany w postaci strony HTML i umieszczony w intranecie. W innym
osoby zainteresowane mogły otrzymać specjalizowane zapytanie napisane
przy użyciu standardowego pakietu aplikacji. Wszystkie te funkcje mogłyby
być zintegrowane poprzez odpowiednie zorganizowanie łączy na stronie
intranetowej lub poprzez umieszczenie przycisków na pulpicie użyt-
kownika.
Zespół rozpoczął pracę nad zestawem małych projektów, które nie
podejmowały żadnych prób integracji poza używaniem tego samego
***
Wybór ODDZIELNYCH DRÓG zamyka pewne możliwości. Chociaż
każda decyzja może być ostatecznie zneutralizowana przez ciągłą refak-
toryzację, to jednak trudno jest połączyć modele, które zostały utworzone
w całkowitej izolacji. Ostatecznie i tak potrzebna wydaje się integracja,
konieczne będzie utworzenie warstw tłumaczeń, które mogą okazać się
skomplikowane. Oczywiście, będzie to coś, z czym i tak będziesz musiał
się zmierzyć.
A teraz, wracając na moment do bardziej kooperatywnych relacji,
przyjrzyjmy się, w jaki sposób zwiększyć integrację.
Przykład
Chemiczny JĘZYK OPUBLIKOWANY
Do katalogowania i analizy formuł chemicznych w przemyśle i nauce
oraz manipulowania tymi formułami zostało już stworzonych mnóstwo
programów. Wymiana danych zawsze była trudna, ponieważ do repre-
zentowania struktur chemicznych prawie każdy program używa innego
modelu dziedziny. Dodatkowo większość z nich jest utworzona w języ-
kach takich jak FORTRAN, które nie wyrażają w pełni modelu dziedziny.
Gdy tylko ktoś chciał wymieniać dane, musiał rozwikłać szczegóły bazy
danych innego systemu i opracować pewnego rodzaju schemat tłumaczenia.
Poznaj zatem chemiczny język znaczników (ang. Chemical Markup
Language — CML), będący dialektem języka XML, w zamierzeniu mają-
cym być wspólnym językiem wymiany dla tej dziedziny. Został on opra-
cowany i jest zarządzany przez grupę reprezentującą uczelnie oraz prze-
mysł (Murray-Rust w 1995 r.).
Informacje chemiczne są bardzo złożone oraz różnorodne, a co więcej,
ciągle się zmieniają wraz z nowymi odkryciami. Dlatego też twórcy tego
języka opracowali język, który mógłby opisywać podstawy, w rodzaju
formuł chemicznych dla molekuł organicznych oraz nieorganicznych,
sekwencji proteinowych, spektrum oraz właściwości fizycznych.
W chwili opublikowania języka mogły zostać również opracowane
narzędzia, nad którymi wcześniej praca nie byłaby warta zachodu, ponie-
waż byłyby one przydatne tylko dla jednej bazy danych. Na przykład
aplikacja w języku Java o nazwie „Przeglądarka JUMBO” opracowana
***
Unifikacja słonia
Żyło sześciu w Hindustanie ludzi ciekawych niesłychanie.
I chociaż byli ślepi, wybrali kiedyś się na błonie,
aby zapoznać się ze słoniem i umysł swój pokrzepić.
2
J. Godfrey Saxe, Księga nonsensu, tłum. Antoni Marianowicz, Wydawnictwa
Artystyczne i Filmowe, Warszawa 1986 — przyp. red.
Rysunek 14.10.
Cztery konteksty
— minimalna
integracja
Rysunek 14.11.
Jeden kontekst
— przybliżona
integracja
Rysunek 14.12.
Jeden kontekst
— głębszy model
Przekształcanie granic
Istnieje nieograniczona liczba różnorodnych sytuacji oraz możliwości
wyznaczenia granic KONTEKSTÓW ZWIĄZANYCH. Zazwyczaj jed-
nak wyzwanie stanowi zachowanie równowagi pomiędzy następującymi
siłami:
Preferowanie większych KONTEKSTÓW ZWIĄZANYCH
Przepływ pomiędzy zadaniami użytkowników jest płynniejszy, kiedy
więcej z nich jest obsługiwanych przez model zunifikowany.
Znacznie łatwiej jest zrozumieć jeden spójny model niż dwa oddzielne
wraz z mapowaniami.
Tłumaczenie pomiędzy dwoma modelami może być trudne (niekiedy
niemożliwe).
Język współdzielony zaszczepia jasną komunikację w zespole.
Wdrożenie
Koordynacja pakietów oraz wdrożenia w złożonych systemach jest jednym
z tych nudnych zadań, które prawie zawsze są trudniejsze w rzeczywi-
stości, niż wyglądają na pierwszy rzut oka. Na wdrożenie ma wpływ wybór
strategii dotyczącej KONTEKSTÓW ZWIĄZANYCH. Na przykład,
gdy ZESPOŁY KLIENTA – DOSTAWCY wdrażają nowe wersje, muszą
skoordynować ze sobą wdrożenie tych wersji, które wspólnie przetestowały.
Migracja kodu oraz danych musi funkcjonować w tych kombinacjach.
W systemie rozproszonym pomóc może utrzymywanie warstw tłumaczenia
razem w jednym procesie pomiędzy KONTEKSTAMI, tak aby nie było
konieczności utrzymywania wielu istniejących wspólnie wersji.
Nawet wdrożenie komponentów z jednego KONTEKSTU ZWIĄ-
ZANEGO może być wyzwaniem w przypadku, gdy migracja danych
wymaga pewnego czasu lub gdy systemy rozproszone nie mogą być
uaktualnione jednocześnie, co sprawia, że będą wspólnie istnieć dwie wer-
sje kodu oraz danych.
W zależności od środowiska wdrożeniowego oraz technologii może
istnieć wiele kwestii technicznych mających wpływ na wdrożenie. Jednak
to relacje KONTEKSTU ZWIĄZANEGO mogą wskazać Ci punkty klu-
czowe. Interfejsy tłumaczące zostały już oznaczone.
Wykonalność planu wdrożeniowego powinna skutkować komen-
tarzami zwrotnymi do procesu wyznaczania granic KONTEKSTÓW.
W sytuacji, gdy dwa KONTEKSTY są połączone warstwą tłumaczącą,
jeden z nich może być uaktualniony, jeżeli nowa warstwa tłumaczenia
udostępnia ten sam interfejs dla innego KONTEKSTU. JĄDRO WSPÓŁ-
DZIELONE narzuca znacznie więcej wysiłku związanego z koordynacją
nie tylko w fazie projektowania, lecz również wdrożenia. Użycie
ODDZIELNYCH DRÓG może znacznie uprościć życie.
Rysunek 14.13.
Kiedy projekt już trwa Względne
Bardzo prawdopodobne jest, że nie rozpoczynasz nowego projektu, lecz wymagania
wzorców relacji
poszukujesz sposobów na udoskonalenie tego, który już trwa. W takim
dla KONTEKSTU
przypadku pierwszą czynnością będzie zdefiniowanie KONTEKSTÓW
ZWIĄZANYCH zgodnie z zastanym stanem rzeczy. Jest to czynność zasad-
nicza. Aby zachować dobrą efektywność, MAPA KONTEKSTÓW musi
odzwierciedlać prawdziwą strukturę zespołów, a nie idealną organizację,
na którą mógłbyś się zdecydować, podążając za opisanymi właśnie wska-
zówkami.
Transformacje
Podobnie jak każdy inny aspekt modelowania lub projektowania, decyzje
w stosunku do KONTEKSTÓW ZWIĄZANYCH nie są nieodwołalne.
Na pewno będzie istnieć wiele sytuacji, gdy będziesz musiał zmienić swoje
początkowe decyzje o granicach oraz relacjach pomiędzy KONTEK-
STAMI ZWIĄZANYMI. Ogólnie mówiąc, dzielenie KONTEKSTÓW
jest całkiem proste, jednak ich łączenie lub zmiana łączących je relacji
stanowią wyzwanie. Opiszę teraz kilka reprezentatywnych zmian, które
są dość trudne, chociaż wciąż ważne. Transformacje są zazwyczaj zbyt
duże, aby można je było wykonać w pojedynczym podejściu refaktory-
zacyjnym, lub nawet w pojedynczej iteracji projektowej. Z tego powodu
wskazałem kilka planów gry, pozwalających na wykonanie tych trans-
formacji w postaci szeregu możliwych do zarządzania etapów. Będą to
oczywiście wskazówki, które należy dostosować do swoich konkretnych
okoliczności oraz zdarzeń.
TRANSFORMACJE 435
które nie zostały jeszcze przetłumaczone. Połączona grupa musi też
wskazać podstawowy zestaw testów dla modelu.
5. Programiści z jednego zespołu podejmują się zadania implementacji
modelu (lub adaptacji istniejącego kodu w celu jego współdzielenia),
opracowując szczegóły, a także sprawiając, aby zadziałał. Jeżeli napo-
tkają oni problemy z modelem, powołują zespół z kroku nr 3 i uczest-
niczą w każdej niezbędnej przeróbce pojęć.
6. Programiści z każdego zespołu podejmują się zadania integracji
z nowym JĄDREM WSPÓŁDZIELONYM.
7. Usuń tłumaczenia, które nie są dłużej wymagane.
W tym momencie będziesz dysponować bardzo małym JĄDREM
WSPÓŁDZIELONYM, ze zdefiniowanym procesem do jego utrzymy-
wania. W kolejnych iteracjach projektowych powtórz kroki od 3. do 7.
w celu zwiększenia zakresu współdzielenia. Kiedy proces trochę okrzep-
nie, a zespół zdobędzie już pewne zaufanie, możesz zabrać się za bardziej
skomplikowane poddziedziny, przerabiając jednocześnie kilka z nich, lub
też zająć się tymi, które stanowią DZIEDZINĘ GŁÓWNĄ.
Uwaga: Kiedy zajmiesz się częściami modelu bardziej właściwymi
dla dziedziny, możesz napotkać przypadki, w których dwa modele dosto-
sowały się do specjalistycznego żargonu różnych środowisk użytkowni-
ków. Mądrym sposobem jest powstrzymanie się przed dołączaniem ich
do JĄDRA WSPÓŁDZIELONEGO do czasu wystąpienia przełomu,
polegającego na odnalezieniu modelu dogłębnego, dzięki któremu zde-
finiowany zostanie wspólny język, mogący zastąpić oba specjalizowane.
Przewaga JĄDRA WSPÓŁDZIELONEGO polega na tym, że możesz
mieć zalety CIĄGŁEJ INTEGRACJI przy zachowaniu pewnych korzyści
ODDZIELNYCH DRÓG.
To tylko kilka ze wskazówek dotyczących łączenia kontekstów do
postaci JĄDRA WSPÓŁDZIELONEGO. Zanim przejdziesz dalej, roz-
waż jedną możliwość spełniającą kilka potrzeb tej transformacji. Jeżeli
jeden z tych dwóch modeli jest szczególnie preferowany, rozważ skie-
rowanie się ku niemu bez integracji. Zamiast współdzielić poddziedziny,
zwyczajnie stopniowo przenoś pełną odpowiedzialność z jednego KON-
TEKSTU ZWIĄZANEGO do drugiego poprzez refaktoryzację aplikacji
przy użyciu modelu z najbardziej preferowanego KONTEKSTU lub
poprzez wykonywanie usprawnień wymaganych przez ten model. W ten
sposób bez żadnego dodatkowego narzutu wyeliminowaliśmy redundan-
cję. Potencjalnie (lecz niekoniecznie) najbardziej preferowany KON-
TEKST ZWIĄZANY mógłby ostatecznie zdominować całkowicie model,
Łączenie KONTEKSTÓW
— JĄDRO WSPÓŁDZIELONE →
CIĄGŁA INTEGRACJA
Jeżeli używane przez Ciebie JĄDRO WSPÓŁDZIELONE rozszerza się,
możesz dać się skusić zaletom pełnej unifikacji dwóch KONTEKSTÓW
ZWIĄZANYCH. Nie jest to tylko kwestia usunięcia różnic w modelach.
Będziesz też musiał zmienić struktury zespołów oraz ostatecznie również
język używany przez ich członków.
Rozpocznij od przygotowania ludzi oraz zespołów:
1. Upewnij się, że wszystkie procesy wymagane dla CIĄGŁEJ INTE-
GRACJI (współdzielona własność kodu, częsta integracja itp.)
dostępne są dla każdego z zespołów oddzielnie. Zharmonizuj pro-
cedury integracji obu zespołów tak, aby każdy wykonywał swoje
zadania w ten sam sposób.
2. Rozpocznij od wymiany członków zespołów pomiędzy nimi. Utworzy
to pulę ludzi, którzy rozumieją oba modele, i zacznie łączyć człon-
ków obu zespołów ze sobą.
3. Wyjaśnij destylację każdego zespołu z osobna (patrz rozdział 15.).
4. W tym momencie Twoja pewność powinna być już na tyle duża, abyś
mógł rozpocząć łączenie dziedziny głównej do JĄDRA WSPÓŁ-
DZIELONEGO. Może to wymagać kilku iteracji i niekiedy pomię-
dzy częściami nowo współdzielonymi a tymi jeszcze nieujednolico-
nymi muszą występować tymczasowe warstwy tłumaczące. W trakcie
łączenia DZIEDZINY GŁÓWNEJ najlepiej jest postępować dość
szybko. Jest to faza o sporym narzucie, pełna błędów i należy ją jak
najbardziej skrócić, dając jej wyższy priorytet niż innym pracom
programistycznym. Nie bierz jednak na siebie więcej, niż jesteś
w stanie obsłużyć.
W przypadku łączenia GŁÓWNYCH modeli masz kilka możliwości.
Możesz pozostać przy jednym modelu i zmodyfikować inne w taki sposób,
aby były z nim zgodne. Możesz też utworzyć nowy model poddziedziny
TRANSFORMACJE 437
i zaadaptować oba konteksty, aby go używały. Uważaj, jeżeli dwa modele
zostały dostosowane do spełniania różnych potrzeb użytkowników. Możesz
potrzebować specjalizacji obu oryginalnych modeli. Wymagać to będzie
utworzenia głębszego modelu, który mógłby zastąpić oba oryginalne.
Tworzenie głębszego modelu unifikującego jest bardzo trudne. Jeżeli
jednak jesteś zdecydowany na pełne połączenie dwóch KONTEKSTÓW,
nie będziesz mieć już możliwości wykorzystywania różnych dialektów.
Nagrodą będzie przejrzystość integracji wynikowego modelu i kodu. Uwa-
żaj, aby nie odbyło się to kosztem możliwości zaspokojenia przez Ciebie
specjalizowanych potrzeb użytkowników.
5. W miarę jak JĄDRO WSPÓŁDZIELONE będzie się rozrastać,
zwiększ częstotliwość integracji do jednego dnia, a ostatecznie
zaadaptuj CIĄGŁĄ INTEGRACJĘ.
6. Gdy JĄDRO WSPÓŁDZIELONE dojdzie do momentu, w którym
będzie zawierać całe dwa poprzednie KONTEKSTY ZWIĄZANE,
będziesz dysponować jednym dużym zespołem lub dwoma mniej-
szymi, używającymi współdzielonego kodu źródłowego CIĄGLE
INTEGROWANEGO, a także wymieniającymi często swoich człon-
ków między sobą.
TRANSFORMACJE 439
USZKODZENIU. Niemniej jednak bardzo prawdopodobne będzie, że
dominować będą inne czynniki, a Ty będziesz musiał żyć z rozbudowa-
nymi tłumaczeniami w trakcie wielu podejść.
TRANSFORMACJE 441
442 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU
ROZDZIAŁ 15.
Destylacja
443
Podobnie jak w przypadku wielu chemicznych destylacji, oddzielone
produkty stają się z powodu samego procesu bardziej wartościowe, jak na
przykład PODDZIEDZINY OGÓLNE (ang. GENERIC SUBDOMA-
INS) lub SPÓJNE MECHANIZMY (ang. COHERENT MECHANI-
SMS). Jednak cały wysiłek motywuje chęć wydobycia jednej szczególnie
istotnej części — tej, która odróżnia nasz program i powoduje, że warto
go tworzyć, czyli „DZIEDZINY GŁÓWNEJ”1.
Strategiczna destylacja modelu dziedziny przyczynia się do nastę-
pujących rzeczy:
1. Pomaga wszystkim członkom zespołu ogarnąć ogólny projekt sys-
temu, a także zrozumieć, jak poszczególne części do siebie pasują.
2. Ułatwia komunikację, identyfikując główny model w rozmiarze
możliwym do zarządzania i umieszczenia w JĘZYKU WSZECH-
OBECNYM.
3. Daje wskazówki do refaktoryzacji.
4. Koncentruje prace na tych obszarach modelu, które mają największą
wartość.
5. Daje wskazówki na temat delegowania zadań, użycia gotowych kom-
ponentów, a także decyzji dotyczących przypisań.
W tym rozdziale zdefiniujemy systematyczne podejście do strate-
gicznej destylacji DZIEDZINY GŁÓWNEJ (ang. CORE DOMAIN),
a także wyjaśnimy, w jaki sposób można wydajnie dzielić jej widok
w zespole i udostępniać język do rozmów o wykonywanej pracy.
Podobnie do ogrodnika przycinającego drzewo w celu umożliwienia
rozwoju głównych gałęzi, będziemy stosować zestaw technik, aby odrzucić
miejsca odwracające uwagę w modelu i skoncentrować się na części, która
liczy się najbardziej.
1
Dosłownie „DZIEDZINY RDZENIA” — przyp. tłum.
Zwiększanie destylacji
Różne techniki destylacji umieszczone w dalszej części rozdziału można
stosować w prawie dowolnej kolejności, jednak różnią się one radykal-
nością modyfikacji projektu.
Prosty OPIS WIZJI DZIEDZINY (ang. DOMAIN VISION STA-
TEMENT) komunikuje podstawowe pojęcia oraz ich wartość przy mini-
malnej inwestycji. RDZEŃ WYRÓŻNIONY (ang. HIGHLIGHTED
CORE) może zwiększyć komunikację oraz pomóc w podjęciu decyzji —
będzie on wciąż wymagać niewielu modyfikacji projektu lub też nie będzie
ich wymagać wcale.
Bardziej agresywne podejście do refaktoryzacji jawnie oddziela
PODDZIEDZINY OGÓLNE (ang. GENERIC SUBDOMAINS), z któ-
rymi można następnie pracować indywidualnie. MECHANIZMY SPÓJNE
(ang. COHESIVE MECHANISMS) mogą zostać zahermetyzowane przy
użyciu uniwersalnego, komunikatywnego i elastycznego projektu. Usunię-
cie tego rodzaju odwracających uwagę kwestii odciąża RDZEŃ.
Zmiana opakowania RDZENIA ODDZIELONEGO odsłania bez-
pośrednio RDZEŃ w kodzie i ułatwia przyszłą pracę nad jego modelem.
Najbardziej ambitnym zadaniem jest RDZEŃ ABSTRAKCYJNY,
wyrażający w czystej postaci najbardziej fundamentalne pojęcia oraz relacje
(wymagający rozległej reorganizacji oraz refaktoryzacji modelu).
Każda z tych technik wymaga coraz większego zaangażowania, jednak
praktyka czyni mistrza. Kolejne destylacje modelu dziedziny tworzą atut,
nadający projektowi większą szybkość, uniwersalność i precyzję działania.
Aby od czegoś rozpocząć, możemy odparować najmniej charaktery-
styczne aspekty modelu. PODDZIEDZINY OGÓLNE stanowią przeci-
wieństwo DZIEDZINY GŁÓWNEJ, co wyjaśnia ich znaczenie.
Przykład
Opowieść o dwóch strefach czasowych
Dwukrotnie zdarzyło się mi obserwować, jak najlepsi programiści w pro-
jekcie spędzają tygodnie na rozwiązywaniu problemu przechowywania
i konwersji czasu w różnych strefach czasowych. Takie działania zawsze
wydają się mi podejrzane. Niekiedy jest to konieczne, a te dwa projekty
stanowią prawie perfekcyjny przykład dwóch przeciwieństw.
Pierwszy projekt miał za zadanie zbudowanie aplikacji do tworzenia
harmonogramów dla transportów morskich. W tym celu krytyczne staje
się posiadanie dokładnych obliczeń dotyczących czasu. Ponieważ zaś wszyst-
kie harmonogramy podawane są w czasie lokalnym, niemożliwe jest koor-
dynowanie transportów bez odpowiednich konwersji.
Zdefiniowawszy jasno swoje oczekiwania dotyczące tej funkcjonal-
ności, zespół zaczął tworzenie DZIEDZINY GŁÓWNEJ oraz wczesnych
iteracji kodu aplikacji, używając w tym celu dostępnych klas obsługujących
czas, a także danych testowych. W miarę dojrzewania aplikacji stawało
się jasne, że istniejące klasy obsługujące czas nie były odpowiednie, a pro-
***
Dokument destylacji
Bardzo często tworzę oddzielny dokument, który opisuje i wyjaśnia
DZIEDZINĘ GŁÓWNĄ. Może on być tak prosty jak lista najbardziej
zasadniczych obiektów pojęciowych. Może być również zestawem dia-
gramów skoncentrowanych na tych obiektach, ukazujących ich najbardziej
krytyczne relacje. Dokument może obejmować podstawowe interakcje
na poziomie abstrakcyjnym lub uwzględniać też szczegółowe przykłady.
Może on wykorzystywać klasy UML lub diagramy sekwencji, niestandar-
dowe diagramy właściwe dziedzinie, ostrożnie sformułowane opisy tek-
stowe lub wszystkie te kombinacje równocześnie. Dokument destylacji nie jest
kompletnym dokumentem projektowym. Jest raczej minimalistycznym punk-
tem startowym, wyróżniającym oraz opisującym RDZEŃ, a także suge-
rującym powody, dla których przyglądamy się szczegółowo poszczególnym
elementom. Czytelnik otrzymuje szeroki obraz tego, w jaki sposób ele-
menty do siebie pasują, a także jest kierowany do właściwej części kodu
w celu uzyskania większej liczby szczegółów.
Dlatego (jako jedna z postaci RDZENIA WYRÓŻNIONEGO):
Utwórz bardzo prosty dokument (od trzech do siedmiu nie-
zbyt gęsto zapisanych stron), który będzie opisywać DZIEDZINĘ
GŁÓWNĄ oraz podstawowe interakcje pomiędzy elementami
RDZENIA.
Niestety, będą się z tym wiązać wszystkie ryzyka właściwe dla oddziel-
nych dokumentów:
1. Dokument może nie być należycie utrzymywany.
2. Dokument może nie zostać przeczytany.
3. Z powodu powielania różnych źródeł informacji dokument może nie
obronić celu, jaki leżał u jego podstaw, tzn. uproszczenia złożoności.
RDZEŃ oznaczony
W trakcie mojego pierwszego dnia pracy przy projekcie w dużej firmie
ubezpieczeniowej dostałem do rąk kopię „modelu dziedziny” — dwu-
stustronicowego dokumentu zakupionego za dużą sumę od konsorcjum
przemysłowego. Poświęciłem kilka dni na podróżowanie pomiędzy plą-
taniną diagramów klas obejmujących wszystko, począwszy od szczegó-
łowej kompozycji polityk ubezpieczeniowych do niesamowicie abstrakcyj-
nych modeli relacji pomiędzy osobami. Jakość dokumentacji tych modeli
wahała się od projektów studenckich do takich, które były raczej dobre
(kilka nawet opisywało reguły biznesowe, przynajmniej w towarzyszącym
im tekście). Jednak od czego należało zacząć? Dwieście stron...
Kultura projektowa zdecydowanie faworyzowała tworzenie abstrak-
cyjnych szkieletów aplikacji, a moi poprzednicy skoncentrowali się na
bardzo abstrakcyjnym modelu relacji poszczególnych osób ze sobą, z rze-
czami oraz z ich aktywnościami lub uzgodnieniami. W rzeczywistości była
to bardzo ładna analiza tych relacji. Sposób, w jaki eksperymentowali
z modelem, przypominał jakością model będący rezultatem akademic-
kiego projektu naukowego. Jednak nie przybliżał nas w żaden sposób do
aplikacji ubezpieczeniowej.
Moim pierwszym instynktownym pomysłem było, aby rozpocząć od
podziałów w celu odnalezienia małych DZIEDZIN GŁÓWNYCH, aby
na czymś móc się oprzeć, następnie ich refaktoryzacji i ponownego wpro-
wadzenia innych złożonych pojęć w trakcie przyszłej pracy. Jednak takie
podejście zaalarmowało kierownictwo. Inwestycja w dokument wymagała
przecież dużej władzy. W jego utworzenie zaangażowani byli eksperci
z całego przemysłu. Co więcej, konsorcjum otrzymało za pracę zdecydo-
wanie więcej niż ja, dlatego firma nie była zbyt chętna, aby dobrze oce-
nić moje rekomendacje do przeprowadzenia drastycznej zmiany. Byłem
Przykład
Mechanizm w schemacie organizacyjnym
Przez ten proces przeszedłem podczas pracy przy projekcie, który wymagał
w pełni rozwiniętego modelu schematu organizacyjnego. Model repre-
zentował fakt, iż jedna osoba pracowała dla innej, a także precyzował,
w których gałęziach organizacji ta osoba się znajdowała. Dodatkowo
model udostępniał interfejs, przez który mogły być zadawane odpowiednie
pytania, a także otrzymywane odpowiedzi. Ponieważ większość tych pytań
przypominała następujące: „Kto w tym łańcuchu zarządzania ma preroga-
tywy do zatwierdzenia tego?” albo „Kto w tym departamencie ma moż-
liwość obsługi problemu w tym rodzaju?”, zespół zdał sobie sprawę, że
większość złożoności związana jest z przeszukiwaniem odpowiednich gałęzi
drzewa organizacyjnego w poszukiwaniu określonych osób oraz relacji.
Jest to dokładnie ten sam problem, który rozwiązuje dobrze rozwinięty
formalizm grafów, stanowiących zestaw węzłów połączonych łukami (zwa-
nymi krawędziami), a także reguł i algorytmów wymaganych do przeszu-
kiwania grafu.
Podwykonawca zaimplementował szkielet mechanizmu przeszuku-
jącego graf i umieścił go w postaci SPÓJNEGO MECHANIZMU. Ten
szkielet używał standardowej terminologii oraz algorytmów nauki o grafach,
znanych większości naukowców z dziedziny informatyki, a także dobrze
opisanych w podręcznikach. W żadnym razie jednak nie zaimplementował
on w pełni ogólnego grafu. Był to tylko podzbiór pojęciowego szkieletu,
który obejmował funkcjonalności wymagane przez model organizacyjny.
A dzięki INTERFEJSOWI UJAWNIAJĄCEMU ZAMIAR środki, za
pomocą których uzyskiwane były odpowiedzi, nie stanowiły już podsta-
wowego zmartwienia.
W tym momencie model organizacji mógł w prosty sposób stwierdzić,
używając w tym celu standardowej terminologii grafów, że każda osoba
jest węzłem, a każda relacja pomiędzy osobami jest krawędzią (łukiem)
łączącym te węzły. Po takiej definicji prawdopodobnie mechanizmy ze
szkieletu grafów mogły odnaleźć relacje pomiędzy dwiema osobami.
OGÓLNE PODDZIEDZINY
a SPÓJNE MECHANIZMY
Zarówno OGÓLNE PODDZIEDZINY, jak i SPÓJNE MECHANIZMY
są motywowane przez to samo pragnienie, aby odciążyć DZIEDZINĘ
GŁÓWNĄ. Różnica leży w naturze przyjmowanej odpowiedzialności.
OGÓLNA PODDZIEDZINA jest oparta na wymownym modelu repre-
zentującym pewne aspekty tego, jak zespół widzi dziedzinę. Pod tym
względem nie różni się od DZIEDZINY GŁÓWNEJ; jest tylko mniej
centralna i mniej ważna, a także mniej specjalizowana. SPÓJNY MECHA-
NIZM nie reprezentuje dziedziny. Rozwiązuje on raczej trudny problem
obliczeniowy postawiony przez modele.
Model proponuje, zaś SPÓJNY MECHANIZM wykonuje.
W praktyce, dopóki nie rozpoznasz sformalizowanego i opublikowa-
nego obliczenia, to rozróżnienie nie jest zazwyczaj jasne. W trakcie
kolejnych refaktoryzacji taki mechanizm mógłby zostać oddestylowany
do mechanizmu bardziej przejrzystego lub też przetransformowany do
OGÓLNEJ PODDZIEDZINY z pewnymi poprzednio nierozpoznanymi
pojęciami modelu, które uprościłyby mechanizm.
Przykład
Pełny cykl — schemat organizacyjny
wchłania swój MECHANIZM
W rzeczywistości po roku od ukończenia modelu organizacyjnego
w poprzednim przykładzie inni programiści przeprojektowali go, aby wyeli-
minować oddzielenie szkieletu grafów. Czuli oni, że zwiększona liczba
obiektów oraz skomplikowany proces oddzielania MECHANIZMU do
osobnego pakietu nie były gwarantowane. W zamian do klasy nadrzędnej
ENCJI organizacyjnych dodali oni zachowanie właściwe węzłowi. Wciąż
jednak zachowali deklaratywny interfejs publiczny modelu organizacyj-
nego. Co więcej, zahermetyzowali nawet MECHANIZM w ENCJACH
organizacyjnych.
Przykład
Oddzielenie RDZENIA
w modelu transportu morskiego
Rozpoczniemy od modelu przedstawionego na rysunku 15.2, traktowanego
jako podstawa oprogramowania do koordynacji transportu ładunków.
Zauważ, że jest on znacznie uproszczony w stosunku do tego,
który byłby wymagany w rzeczywistej aplikacji. Realistyczny model był-
by zbyt kłopotliwy. Dlatego chociaż taki przykład nie musiałby być wy-
starczająco skomplikowany, aby skłonić nas do stworzenia RDZENIA
ODDZIELONEGO, puśćmy wodze fantazji i potraktujmy ten model
jako zbyt skomplikowany, aby łatwo zinterpretować go w całości.
1
Autor nawiązuje do słynnej inicjatywy mieszkańców San Francisco z 1987 r.,
by utworzyć widoczny znak solidarności z chorymi na AIDS oraz ofiarami tej
choroby — więcej informacji na stronie http://www.aidsquilt.org — przyp. tłum.
485
Jednak główni programiści tego projektu czuli niepokój. Problem
był wewnętrzne złożony. Programiści musieli wyjaśnić złożone relacje
w modelu, dlatego rozłożyli projekt na spójne MODUŁY o rozmiarze
łatwym do zarządzania. W rezultacie stworzonych zostało wiele MODU-
ŁÓW. Do którego pakietu powinien zatem zajrzeć programista, aby odna-
leźć określony aspekt funkcjonalności? Gdzie powinna być umieszczona
nowa klasa? Co w rzeczywistości oznaczają niektóre z tych małych pakie-
tów? W jaki sposób wszystkie one do siebie pasują? A do utworzenia została
jeszcze duża ilość kodu.
Programiści bardzo dobrze komunikowali się ze sobą i dlatego wciąż
mogli domyślić się, co robić z dnia na dzień. Jednak kierownicy projektu
nie byli zadowoleni z takiej złożoności. Oczekiwali jakiegoś sposobu
uporządkowania projektu, aby mógł on zostać zrozumiany oraz zmody-
fikowany, zanim osiągnie kolejny poziom złożoności.
W tym celu zorganizowali debatę. Istniało wiele rozwiązań. Zapro-
ponowany został alternatywny schemat pakowania. Być może niektóre
dokumenty mogłyby przekazywać ogólny obraz systemu lub też nie-
które nowe widoki mogłyby zostać dodane do diagramu klas w narzę-
dziu do modelowania, co dałoby programiście informację o właściwym
MODULE. Niemniej jednak te wszystkie sztuczki nie satysfakcjonowały
kierowników projektu.
Patrząc na model, mogli oni opowiedzieć proste historie o symula-
cji, o tym, jak dane mogłyby być uporządkowane przez infrastrukturę,
o integralności danych, a także ich drodze przez odpowiednie warstwy
technologii telekomunikacyjnych. Każdy szczegół tej historii był w modelu,
jednak nie można było z niego wyczytać szerokiego aspektu całej historii.
Brakowało niektórych zasadniczych pojęć z dziedziny. Tym razem
nie dotyczyło to tylko jednej lub dwóch klas nieobecnych w modelu obiek-
towym. Brakowało całej struktury modelu, jako całości.
Kiedy programiści przemyśleli cały problem przez tydzień lub dwa,
zaczął w ich głowach pączkować pomysł. Mogliby zwyczajnie nałożyć na
model pewną strukturę. Całą symulację można by rozpatrywać w kate-
goriach serii warstw powiązanych z aspektami systemu komunikacyjnego.
Warstwa niższa reprezentowałaby fizyczną infrastrukturę, podstawową
zdolność do transmitowania bitów z jednego węzła do innego. Następnie
istniałaby warstwa trasowania pakietów, która zajmowałaby się w całości
zadaniem kierowania określonego strumienia danych. Inne warstwy identy-
fikowałyby pozostałe poziomy pojęciowe problemu. Wszystkie warstwy razem
nakreślałyby historię systemu.
Rozdział 16. Struktury dużej skali
Rysunek 16.1.
Niektóre wzorce
struktury dużej skali
2
Pojęcie METAFORY SYSTEMU zrozumiałem ostatecznie, gdy słuchałem
wykładu Warda Cunninghama o przykładzie z zaporą ogniową.
Przykład
Analiza szczegółowa — podział na warstwy
systemu logistyki morskiej
Przyjrzyjmy się konsekwencjom zastosowania WARSTW ODPOWIE-
DZIALNOŚCI w aplikacji do obsługi logistyki morskiej, omówionej
w przykładach w poprzednich rozdziałach.
Gdy ponownie przeanalizujemy historię, ujrzymy, że zespół osią-
gnął zauważalny postęp, tworząc PROJEKT STEROWANY MODELEM
oraz destylując DZIEDZINĘ GŁÓWNĄ. Kiedy jednak spojrzymy na
projekt, ujrzymy, że członkowie zespołu mają problem z koordynacją
tego, w jaki sposób wszystkie elementy do siebie razem pasują. Dlatego
szukają struktury dużej skali, która mogłaby wydobyć główne tematy
z ich systemu i ujednolicić spojrzenie wszystkich osób.
Spójrzmy na widok reprezentatywnej części modelu.
Członkowie zespołu byli zajęci analizą dziedziny transportu mor-
skiego od miesięcy i dlatego dostrzegli pewne naturalne rozwarstwienie
jej pojęć. Całkiem rozsądnie jest dyskutować o harmonogramie trans-
portu (zaplanowanych podróżach statków oraz pociągów) bez odwoły-
wania się do ładunków umieszczonych w tych transportach. Trudniej jed-
nak jest rozmawiać o śledzeniu ładunku bez odwoływania się do transportu,
który go przewozi. Pojęciowe zależności są całkiem jasne. Zespół może
łatwo rozróżnić dwie warstwy: „operacje” oraz substrat tych operacji, który
nazwali „potencjałem”.
Odpowiedzialności „operacyjne”
Czynności firmy wykonywane w przeszłości, obecnie i planowane są
gromadzone w warstwie operacji (Operations). Najbardziej oczywistym
obiektem operacji jest ładunek (Cargo), będący przedmiotem codzien-
nego działania firmy. Specyfikacja trasy (Route Specification) jest
integralną częścią ładunku, wskazując wymagania transportu. Plan podróży
(Itinerary) jest operacyjnym planem dostarczenia ładunku. Wszystkie te
obiekty stanowią część AGREGATU ładunku, a ich cykle życia są związane
z zakresem czasowym aktywnej dostawy.
Rysunek 16.4.
Odpowiedzialności „potencjału” Użycie modelu
do wyznaczania
Ta warstwa odzwierciedla zasoby firmy, z których korzysta ona w celu
trasy ładunku
przeprowadzania operacji. Klasycznym przykładem będzie etap podróży
w trakcie rezerwacji
(Transit Leg). Statki mają swój harmonogram, a także ładowność dla
transportowanego ładunku, która może, lecz nie musi być w pełni wyko-
rzystana.
Zgoda. Gdybyśmy koncentrowali się na zarządzaniu flotą statków,
wtedy etap podróży znajdowałby się w warstwie operacyjnej. Jednak
Rysunek 16.6.
Odwołania w tym modelu zachowują spójność z tymi trzema war-
Pierwsze podejście
stwami, z wyjątkiem jednego niezgodnego elementu — atrybutu „is do modelu
preferred” (jest preferowany) w obiekcie Transport Leg (etap podróży). z warstwami
Ten atrybut istnieje, ponieważ firma preferuje w miarę możliwości uży-
wanie własnych statków lub statków określonych innych firm, z którymi
ma korzystne kontrakty. Atrybut „is preferred” skłania obiekt Router do
wyboru tych preferowanych firm transportowych. Nie ma on nic wspólnego
z „potencjałem”. Jest to raczej polityka, ukierunkowująca proces podej-
mowania decyzji. W celu użycia nowych WARSTW ODPOWIEDZIAL-
NOŚCI model musi zostać zrefaktoryzowany.
Refaktoryzacja w taki sposób powoduje uwidocznienie polityki
wyboru trasy (Route Bias Policy), podczas gdy etap trasy (Transport
Leg) jest bardziej skoncentrowany na podstawowym pojęciu związanym
Rysunek 16.9.
Możliwy projekt
trasowania ładunków
niebezpiecznych
Jednak problem tkwi w tym, że ten projekt nie pasuje do struktury
dużej skali. Usługa polityki trasowania materiałów niebezpiecznych
(HazMat Route Policy Service) nie jest problemem. Pasuje ona bowiem
Przykład
System do wypłat pracowniczych, część 1.
Dział zarządzania zasobami ludzkimi w pewnej średniej wielkości firmie
ma prosty program do obliczania wypłat i emerytur.
Rysunek 16.15.
Stary model,
zbyt ograniczony
dla nowych
wymagań
Rysunek 16.17.
Zaproponowany
model. Teraz zawiera
on zbyt mało
ograniczeń
Rysunek 16.19.
To wymaganie może być wyrażone w następujący sposób w JĘZYKU
Obiekt Type
pozwala WSZECHOBECNYM:
na spełnienie Rodzaj pracownika (Employee Type) jest przypisywany do planu
wymagań emerytalnego (Retirement Plan) lub dowolnej listy płac.
Pracownicy są ograniczani rodzajem pracownika (Employee Type).
3
POSA jest skrótem od architektury programów sterowanej wzorcami
(ang. Pattern Oriented Software Architecture) z książki Buschmanna z 1996 r.
Rysunek 16.21.
Wyróżnienie
POZIOMU
WIEDZY ukrytego
w istniejącym
modelu Ograniczone opcje edycji znajdowały się w WARSTWIE WIEDZY,
natomiast te używane na co dzień były na poziomie operacyjnym. Cał-
kiem ładne dopasowanie. Wszystkie obiekty ponad linią opisywały typy lub
długotrwałe polityki. Rodzaj pracownika (Employee Type) w sposób
efektywny wymuszał zachowanie na pracowniku.
Programista dzielił się właśnie swoimi spostrzeżeniami z kolegami,
gdy wtem inna programistka dokonała całkiem odmiennego odkrycia.
Spójność modelu zorganizowanego przez POZIOM WIEDZY umożli-
wił jej dostrzeżenie tego, z czym zmagała się poprzedniego dnia. W jednym
obiekcie zostały połączone dwa różne pojęcia. Wprawdzie dosłyszała je
w języku używanym wczoraj, jednak jeszcze nie zdążyła się za to zabrać.
Rodzaj pracownika (Employee Type) jest przypisywany do planu
emerytalnego (Retirement Plan) lub listy płac.
Rysunek 16.22.
POZIOM WIEDZY był ukryty pomiędzy charakterystycznymi ogra- Lista płac (Payroll)
niczeniami dostępu oraz relacjami typu „rzecz do rzeczy”. Po umiesz- jest teraz widoczna
czeniu go na właściwym miejscu przejrzystość w modelu, do której się i odróżniona
przyczynił, pozwoliła dokonać kolejnego odkrycia, które rozjaśniło dwa od rodzaju
ważne pojęcia dziedziny poprzez stworzenie listy płac (Payroll). pracownika
POZIOM WIEDZY, podobnie do innych struktur dużej skali, nie (Employee Type)
jest niezbędny. Obiekty będą funkcjonować również bez niego, zaś odkry-
cie, które spowodowało oddzielenie rodzaju pracownika (Employee
Type) od listy płac (Payroll), mogłoby i tak zostać dokonane i później
wykorzystane. Wciąż może wystąpić sytuacja, w której sama struktura
nie będzie uzasadniać swojej złożoności i będzie mogła zostać odrzucona.
Jednak w tej chwili wydaje się ona przekazywać przydatną historię o modelu
i pomagać programistom go zrozumieć.
Rysunek 16.24.
Bardzo uproszczony
W chwili, gdy dostawca wyprodukuje nową maszynę, będzie też
podzbiór
musiał utworzyć specjalizowaną implementację interfejsu procesowego interfejsów CIM
tego urządzenia. Jeżeli tylko będą stosować się do interfejsu, komponent wraz z prostymi
implementacjami
Rysunek 16.25.
Użytkownik Sam szkielet ma bardzo określone wymagania dotyczące infrastruk-
umieszcza serię tury. W celu zapewnienia trwałości danych, transakcji, zdarzeń oraz in-
w kolejnym nych technicznych usług jest on ściśle związany z technologią CORBA.
urządzeniu Jednak najbardziej interesującą w nim rzeczą jest definicja SZKIELETU
i zapisuje KOMPONENTÓW DOŁĄCZANYCH, umożliwiającego ludziom nie-
tę czynność zależne tworzenie oprogramowania oraz płynne integrowanie go z rozbu-
w komputerze dowanymi systemami. Nikt nie zna wszystkich szczegółów takiego sys-
temu, lecz każdy rozumie jego ogólny zarys.
***
Tworzenie elementu
Umieść nazwisko osoby, która wspominasz. Jeżeli chcesz, możesz dodać
inne informacje, w rodzaju daty urodzenia i śmierci, a także rodzinnego
miasta. Na jednym elemencie umieść dane jednej osoby.
Wybierz materiały
Pamiętaj, że narzuta będzie składana i rozkładana wiele razy, więc trwa-
łość materiałów jest bardzo istotna. Ponieważ klej z upływem czasu traci
trwałość, najlepiej jest przyszywać wszystko do danego elementu. Najlepiej
sprawdzają się materiały nierozciągliwe oraz o średniej wadze, w rodzaju
bawełny lub popeliny.
Projekt może być utworzony w pionie lub poziomie, jednak ukoń-
czony element musi mieć rozmiar 3 na 6 stóp (90×180 cm) — nie więcej
ani nie mniej!. Kiedy będziesz wycinać materiał, pozostaw dodatkowe 2 – 3
cale (5 – 7,6 cm) z każdej strony na obszycie. Jeżeli nie możesz obszyć
danego elementu samodzielnie, wykonamy to za ciebie. Wypychanie
elementów nie jest wymagane, jednak zalecamy ich podszycie. Umożli-
wi to zachowanie elementów w czystości, kiedy będą przechowywane na
ziemi. Pozwoli również zachować kształt materiału.
Tworzenie elementu
W trakcie tworzenia swojego elementu możesz użyć niektórych z poniż-
szych technik:
Aplikacja: Wszyj materiał, litery oraz krótkie wspomnienia na tle
materiału. Nie polegaj na kleju — nie przetrwa.
Malowidło: Namaluj coś na materiale przy użyciu farb szybkoschną-
cych lub użyj ołówka kopiowego. Proszę, nie używaj farb „z fakturą”,
ponieważ są one zbyt kleiste.
Refaktoryzacja
ku lepiej dopasowanej strukturze
W czasach, w których przemysł informatyczny drży w posadach, starając
się tworzyć rozbudowane projekty warstwy interfejsu użytkownika, nie-
którzy traktują struktury dużej skali jako powrót do starych dobrych
czasów, w których dominowała architektura wodospadu. Jednak w rze-
czywistości przydatna struktura może zostać odkryta jedynie w wyniku
Minimalizm
Jednym z kluczy do utrzymania niskich kosztów jest zachowanie struk-
tury w prostym i lekkim kształcie. Nie staraj się być wszechstronny.
Najzwyczajniej zajmij się tylko najpoważniejszymi problemami, a resztę
pozostaw do indywidualnej obsługi w trakcie projektu.
Na samym początku pomocny może okazać się wybór luźnej struk-
tury, w rodzaju METAFORY SYSTEMU lub kilku WARSTW ODPO-
WIEDZIALNOŚCI. Minimalna, luźna struktura może pomimo to dostar-
czyć wskazówki, które zapobiegną chaosowi.
Łączenie strategii
W poprzednich trzech rozdziałach zaprezentowany został szereg pryn-
cypiów oraz technik dla strategicznego projektu sterowanego dziedziną.
W dużych, złożonych systemach możesz być zmuszony połączyć w jed-
nym projekcie kilka z nich. W jaki sposób struktury dużej skali współeg-
zystują z MAPĄ KONTEKSTU? Do czego pasują poszczególne elementy
składowe? Jaką czynność wykonasz jako pierwszą? Drugą? Trzecią? W jaki
sposób opracujesz swoją strategię?
Rysunek 17.1.
531
Trzech podstawowych pryncypiów projektu strategicznego (kontekst,
destylacja oraz struktura dużej skali) nie można używać zamiennie.
Uzupełniają się one nawzajem i współdziałają ze sobą na szereg różnych
sposobów. Na przykład struktura dużej skali może istnieć w jednym
KONTEKŚCIE ZWIĄZANYM lub też przecinać wiele z nich i organi-
zować MAPĘ KONTEKSTU.
Poprzednie przykłady WARSTW ODPOWIEDZIALNOŚCI były
ograniczone do jednego KONTEKSTU ZWIĄZANEGO. Taki sposób naj-
prościej wyjaśnia całe pojęcie i jest powszechnie stosowanym wzorcem.
W takim prostym scenariuszu znaczenia innych nazw warstw są ograni-
czone do KONTEKSTU, podobnie jak nazwy elementów modelu lub
interfejsy podsystemu istniejące wewnątrz tego KONTEKSTU.
Rysunek 17.2.
Umieszczanie Takie struktury lokalne mogą być przydatne w wielu skomplikowa-
modelu wewnątrz nych, jednak zunifikowanych modelach, podnosząc dopuszczalne ogra-
pojedynczego niczenie złożoności lub zwiększając liczbę elementów, które mogą być
KONTEKSTU
utrzymywane w pojedynczym KONTEKŚCIE ZWIĄZANYM.
ZWIĄZANEGO
Jednak w wielu projektach większe wyzwanie stanowi zrozumienie,
jak poszczególne różne elementy pasują do siebie. Mogą być one podzielone
na oddzielne KONTEKSTY, jednak jaką rolę pełni każda z tych części
w całym zintegrowanym systemie i jak te części mają się do siebie nawza-
jem? Następnie struktura dużej skali może być użyta do zorganizowania
MAPY KONTEKSTU. W tym przypadku terminologia struktury ma
zastosowanie do całego projektu (lub przynajmniej do jego jasno ograni-
czonych części).
Rysunek 17.5.
Ta sama struktura
zastosowana
w KONTEKŚCIE
oraz w całej MAPIE
KONTEKSTU
Powstawanie struktury
w trakcie tworzenia aplikacji
Zdyscyplinowany zespół, złożony z osób o bardzo dobrych zdolnościach
komunikacyjnych, może działać bez centralnego nadzoru i używać
PORZĄDKU EWOLUCYJNEGO w celu osiągnięcia wspólnego zestawu
pryncypiów. Dlatego ten porządek powstaje organicznie, a nie jest narzu-
cony odgórnie.
Jest to typowy model dla zespołu projektowania ekstremalnego.
W teorii struktura może powstać całkowicie spontanicznie ze spostrzeżenia
dowolnej pary programistów. Częściej jednak przydzielenie pewnej osobie
lub podzbiorowi zespołu nadzoru nad odpowiedzialnością za strukturę
dużej skali pomaga w utrzymaniu jednolitości struktury. To podejście
działa poprawnie, szczególnie gdy taki nieformalny lider jest również
praktykującym programistą arbitrem oraz ma dobre zdolności komuni-
kacyjne i nie jest jedynie źródłem pomysłów. W zespołach programo-
wania ekstremalnego, które widziałem do tej pory, takie przewodnictwo
w kontekście projektu strategicznego wydawało się powstawać sponta-
nicznie, bardzo często skupiając się na osobie trenera. Niezależnie od tego,
kto będzie naturalnym przywódcą, będzie on wciąż członkiem zespołu
programistów. W konsekwencji w zespole programistów musi być przy-
najmniej kilka osób dużego kalibru, zdolnych do podejmowania decyzji
projektowych, które będą mieć wpływ na cały projekt.
Jeżeli struktura dużej skali rozciąga się na wiele zespołów, wtedy te
ściśle ze sobą zaznajomione mogą zacząć współpracować nieformalnie.
W takiej sytuacji każdy zespół aplikacyjny wciąż będzie dokonywać odkryć
prowadzących do pomysłów dla struktury dużej skali, jednak następnie
poszczególne alternatywy będą omawiane na forum nieformalnego komi-
tetu złożonego z przedstawicieli różnych zespołów. Po ocenie wpływu
danego projektu uczestnicy mogą zdecydować się na jego wdrożenie,
modyfikację lub pozostawienie do dalszej dyskusji. Zespoły będą próbo-
wać poruszać się wspólnie w takim luźnym związku. Takie porozumienie
może funkcjonować, jeżeli istnieje relatywnie mało zespołów i są one
zaangażowane we wzajemną koordynację, kiedy ich możliwości projek-
towe są porównywalne oraz kiedy ich potrzeby strukturalne są na tyle
podobne, aby mogły być spełnione przez jedną strukturę dużej skali.
546 ZAKOŃCZENIE
Oprogramowanie do zarządzania pożyczkami, o którym opowiadałem
w rozdziale 9., rozwijało się prężnie, podążając tą samą ścieżką przez trzy
lata — do momentu przełomu, o którym również napisałem. W tym
momencie projekt został przekształcony w niezależną firmę. W ferworze
tej reorganizacji kierownik projektu, który prowadził go od samego
początku, został zwolniony, a wraz z nim odeszło kilku kluczowych pro-
gramistów. Nowy zespół miał nieco inną filozofię projektowania, nie do
końca związaną z projektowaniem obiektowym. Jego członkowie zostawili
jednak wyraźnie widoczną warstwę dziedziny wraz ze skomplikowanym
działaniem oraz nadal cenili wiedzę dziedzinową w zespole programistów.
Siedem lat po przekształceniu oprogramowanie nadal jest rozszerzane
o nowe funkcje. Jest to wiodąca w swojej dziedzinie i służąca wielu insty-
tucjom aplikacja, która stała się również największym źródłem dochodu
firmy.
Kiedy podejście sterowane dziedziną stanie się bardziej powszechne,
ciekawe oprogramowanie w wielu projektach będzie powstawało w krót-
kich, wysoko produktywnych odstępach. Ostatecznie projekt przekształci
się w coś bardziej konwencjonalnego i nie będzie mógł całkowicie wyko-
rzystać siły drzemiącej w wydestylowanych wcześniej modelach dogłębnych.
Mógłbym życzyć sobie więcej, ale to są tak naprawdę sukcesy, które przez
lata dostarczają długotrwałe wartości dla użytkowników.
W jednym z projektów pracowałem w parze z innym programistą,
żeby napisać dla klienta narzędzie potrzebne do wyprodukowania jego
głównego produktu. Właściwości były dość skomplikowane i połączone
ze sobą w zawiły sposób. Lubiłem pracę nad tym projektem i razem
wykonaliśmy projekt elastyczny z RDZENIEM ABSTRAKCYJNYM.
ZAKOŃCZENIE 547
Moment przekazania oprogramowania był końcem zaangażowania
wszystkich osób, które je tworzyły. Ponieważ było to tak nagłe przejście,
spodziewałem się, że cechy projektu, które wspierały połączenia ele-
mentów, mogą być zagmatwane i zamienione na bardziej typową logikę
warunkową. Początkowo jednak to się nie wydarzyło. Kiedy przekazy-
waliśmy pakiet, zawierał on dokładny zestaw testów oraz dokument desty-
lacji. Nowy zespół wykorzystał je do ukierunkowania swoich poszuki-
wań. W miarę jego poznawania programiści stawali się coraz bardziej
zafascynowani możliwościami oferowanymi przez projekt. Kiedy rok
później usłyszałem ich komentarze, okazało się, że JĘZYK WSZECH-
OBECNY wzbudził zainteresowanie innego zespołu i pozostał żywy
oraz jest nadal rozwijany.
Później, po kolejnym roku, usłyszałem inną historię. Zespół zetknął
się z nowymi wymaganiami, których programiści nie mogli zaimplemen-
tować w odziedziczonym projekcie. Kiedy poprosiłem o więcej szcze-
gółów, zrozumiałem, że punkt widzenia naszego modelu sprawiłby, że
rozwiązanie problemu byłoby kłopotliwe. To jest dokładnie ten moment,
kiedy przełom w modelu dogłębnym jest często możliwy, zwłaszcza gdy,
jak w tym przypadku, programiści zgromadzili głęboką wiedzę i doświad-
czenie w dziedzinie. Właściwie mieli oni napływ nowych spostrzeżeń
i w rezultacie przekształcili model i projekt na ich podstawie.
Opowiedzieli mi tę historię ostrożnie i dyplomatycznie, oczekując,
jak sądzę, że będę rozczarowany tym, że porzucili tak dużo z mojej pracy.
Nie mam aż takich sentymentów do moich projektów. Sukces projektu
niekoniecznie jest nacechowany jego niezmiennością. Weź system, od
którego ludzie są zależni, uczyń go przezroczystym, a będzie on żyć
wiecznie, jako nietykalne dziedzictwo. Model dogłębny pozwala na skry-
stalizowanie wizji, co może prowadzić do nowych spostrzeżeń, podczas gdy
projekt elastyczny ułatwia wprowadzanie ciągłych zmian. Model, który
oni stworzyli, był głębszy, lepiej dopasowany i rzeczywiście interesował
użytkowników. Ich projekt rozwiązywał prawdziwy problem. Naturą
oprogramowania jest zmiana i ten program był rozwijany przez zespół,
który był jego właścicielem.
Rozrzucone w książce przykłady dotyczące przewozów są luźno
powiązane z projektem dla dużej międzynarodowej firmy przewożącej
kontenery. Już na samym początku kierownictwo projektu zobowiązało
się do podejścia dziedzinowego, ale nigdy nie stworzyło sprzyjającej mu
w pełni kultury programistycznej. Kilka zespołów z bardzo różnym pozio-
mem umiejętności projektowania i doświadczenia obiektowego zaczęło
tworzyć moduły, luźno koordynowane dzięki nieformalnej współpracy
548 ZAKOŃCZENIE
pomiędzy liderami zespołów a zespołem architektów zorientowanych na
klienta. Stworzyliśmy dość głęboki model DZIEDZINY GŁÓWNEJ
i realny JĘZYK WSZECHOBECNY.
Kultura firmy zaciekle przeciwstawiała się rozwojowi iteracyjnemu,
a my zdecydowanie zbyt długo czekaliśmy z wypuszczeniem działającej
własnej wersji. Z tego powodu problemy zostały zidentyfikowane na tyle
późno, że powodowały większe ryzyko i trudniej było im zaradzić. W pew-
nym momencie odkryliśmy, że pewne aspekty modelu powodują pro-
blemy wydajnościowe bazy danych. Naturalnym elementem PROJEKTU
STEROWANEGO MODELEM jest zmiana modelu na podstawie opinii
i problemów napotkanych podczas implementacji. Jednak wtedy panowało
przekonanie, że prace są już zbyt zaawansowane, aby wprowadzać zmiany
w modelu podstawowym. Zamiast nich wprowadzono zmiany w kodzie
źródłowym w taki sposób, aby działał bardziej efektywnie, co osłabiło
jego powiązanie z samym modelem. Pierwsza wydana wersja wykazała
również ograniczenia skalowalności po stronie infrastruktury, które z kolei
przeraziły kierownictwo. Zaangażowano biegłych do rozwiązania pro-
blemów z infrastrukturą i projekt powrócił na właściwy tor, jednak
luka pomiędzy implementacją a modelem dziedziny nigdy nie została
domknięta.
Niektóre zespoły dostarczyły oprogramowanie mające złożone moż-
liwości i pełne wyrazu modele. Inne dostarczyły ciężkie oprogramowa-
nie redukujące model do struktur danych, aczkolwiek zachowujące ślady
JĘZYKA WSZECHOBECNEGO. Być może pomogłaby nam MAPA
KONTEKSTÓW, gdyż wyniki pracy poszczególnych zespołów były powią-
zane bardzo chaotycznie. Mimo to model GŁÓWNY przekazany poprzez
JĘZYK WSZECHOBECNY pomógł zespołom ostatecznie skleić system
w całość.
Pomimo okrojonego zakresu projekt zastąpił kilka odziedziczonych
systemów. Całość była zebrana jako współdzielone pomysły, jednak więk-
szość projektu nie była zbyt elastyczna. Teraz projekt w dużym stopniu
skamieniał i sam stał się po latach dziedzictwem, ale nadal 24 godziny na
dobę służy globalnemu biznesowi. Mimo że wpływ zespołów odnoszą-
cych sukcesy coraz bardziej się rozszerza, to nawet bogatym firmom czas
ucieka. Kultura projektu nigdy nie zaadaptowała PROJEKTU STERO-
WANEGO MODELEM. Rozwój tego projektu zachodzi obecnie na innych
platformach i nasza praca tylko pośrednio wpływa na ten rozwój, ponieważ
nowi programiści dostosowują się do spuścizny.
W pewnych kręgach ambitne cele, jak ten, który postawiła sobie
początkowo firma logistyczna, są podważane. Wydaje się, że lepiej jest
tworzyć małe aplikacje, które umiemy dostarczać. Lepiej trzymać się
ZAKOŃCZENIE 549
najmniejszego wspólnego mianownika projektu, żeby robić proste rzeczy.
To konserwatywne podejście ma swoje zastosowanie i pozwala na staranne
zdefiniowanie zakresu projektu będącego szybką odpowiedzią na zadany
problem. Jednakże zintegrowane, sterowane modelem systemy obiecują
wartość, której ten zlepek nie może obiecać. Jest trzeci sposób. Projekt
sterowany dziedziną pozwala na wyrywkowy rozwój dużych systemów
z bogatą funkcjonalnością poprzez budowanie na modelu dogłębnym
oraz projekcie elastycznym.
Zamknę listę firmą Evant, która buduje systemy do zarządzania
magazynem. Pełniłem w niej drugorzędną rolę wspierającą i przyczyniłem
się do rozwoju już i tak silnej istniejącej kultury projektowej. Niektórzy
pisali o tym projekcie jako o flagowym przykładzie programowania ekstre-
malnego, ale zwykle nie podkreśla się wcale, że był on głęboko sterowany
dziedziną. Bardziej dogłębne modele zostały wyodrębnione i wyrażone
w jeszcze bardziej elastycznych projektach. Projekt miał się doskonale aż
do pęknięcia „bańki internetowej” w 2001 roku. Wtedy firma skurczyła
się, nie mając inwestorów i funduszy, rozwój oprogramowania został
niemal zapomniany i wydawało się, że koniec jest już blisko. Jednak latem
2002 roku firma Evant dostała zapytanie od jednego z dziesięciu najwięk-
szych detalistów. Produkt spodobał się temu potencjalnemu klientowi, ale
wymagał zmian w projekcie, żeby umożliwić wyskalowanie do planowania
operacji ogromnego magazynu.
Mimo że zespół został zredukowany do czterech programistów, mieli
oni pewne zalety. Posiadali niezbędne umiejętności, wiedzę na temat
dziedziny, a jeden z nich był ekspertem od skalowania. Mieli również
bardzo efektywną kulturę rozwojową. Dysponowali kodem bazowym
z elastycznym projektem, który ułatwiał zmiany. Tego lata tych czterech
programistów wykonało heroiczną pracę programistyczną, której efektem
był system mogący obsłużyć miliony elementów planowania i setki użyt-
kowników. Dzięki tym możliwościom firma Evant podpisała kontrakt
z ogromnym klientem i wkrótce potem została zakupiona przez inną firmę,
która chciała wykorzystać jej oprogramowanie i jego sprawdzone możli-
wości dostosowywania się do nowych wymagań.
Kultura projektowania sterowanego dziedziną (jak również progra-
mowanie ekstremalne) przeżyła transformację i była rewitalizowana. Dziś
zarówno model, jak i projekt nadal są rozwijane, znacznie bardziej bogate
i elastyczne niż dwa lata temu, kiedy jeszcze wspierałem projekt. Co wię-
cej, członkowie zespołu dawnej firmy Evant, zamiast zasymilować się
z nową firmą, stali się liderami i inspiracją dla istniejących zespołów pro-
jektowych w nowej firmie. Ta historia toczy się nadal.
550 ZAKOŃCZENIE
Żaden projekt nie wykorzysta wszystkich technik zaprezentowanych w tej
książce. Mimo to każdy projekt sterowany dziedziną będzie można roz-
poznać na kilka sposobów. Definiowanie charakterystyki jest priorytetem
w zrozumieniu docelowej dziedziny i przeniesieniu tego zrozumienia do
oprogramowania. Wszystko inne wynika z tego założenia. Członkowie
zespołu świadomie używają języka w projekcie i kultywują jego udosko-
nalanie. Nigdy nie są zadowoleni z jakości modelu dziedziny, ponieważ
stale uczą się dziedziny. Postrzegają świadome udoskonalanie jako szansę,
a źle dopasowany model jako ryzyko. Poważnie traktują umiejętności pro-
jektowania, ponieważ nie jest łatwo zbudować oprogramowanie o jakości
produkcyjnej, które jasno odzwierciedla model dziedziny. Potykają się
o przeszkody, ale pozostają wierni zasadom, gdyż stąpają twardo po ziemi
i idą naprzód.
Patrząc w przyszłość
Pogoda, ekosystem, biologia zwykle są postrzegane jako obszary chaotyczne,
„miękkie”, w przeciwieństwie do fizyki czy chemii. Niemniej jednak
ostatnio ludzie zaczęli dostrzegać, że ten „chaos” jest tak naprawdę głębokim
technologicznym wyzwaniem prowadzącym do odkrycia i zrozumienia
porządku w tym bardzo złożonym zjawisku. Obszar określany jako „zło-
żony” jest awangardą wielu nauk. Pomimo że dla uzdolnionych inżynierów
oprogramowania najciekawsze wydają się zazwyczaj zadania czysto tech-
nologiczne, projekt sterowany dziedziną stwarza nowe wyzwania, które
są równie lub nawet bardziej interesujące. Oprogramowanie biznesowe
nie musi być mieszaniną połączonych ze sobą chaotycznych elementów.
Zmaganie się ze złożoną dziedziną i opisanie jej w szeroko zakrojonym
projekcie oprogramowania jest ekscytującym wyzwaniem dla osób bardzo
ukierunkowanych technicznie.
Daleko nam do ery zwykłych ludzi tworzących złożone działające
oprogramowanie. Armie programistów z podstawowymi umiejętnościami
mogą wyprodukować określony rodzaj oprogramowania, ale nie takie, które
uratuje firmę w ostatnim możliwym momencie. W tej chwili budujący
narzędzia powinni myśleć o rozszerzeniu możliwości i produktywności
utalentowanych programistów. Potrzebny jest mądry sposób eksploracji
modeli dziedziny i wyrażania ich w działającym oprogramowaniu. Nie mogę
się doczekać eksperymentowania z nowymi narzędziami zaprojektowa-
nymi w tym celu.
Oczywiście, narzędzia będą wartościowe, natomiast nie możemy
zapominać o podstawowym fakcie, że tworzenie dobrego oprogramowania
ZAKOŃCZENIE 551
jest aktywnością wymagającą uczenia się i myślenia. Modelowanie wymaga
wyobraźni i samodyscypliny. Dobre są narzędzia pomagające nam myśleć
i unikać rozproszenia. Próby zautomatyzowania rzeczy, które muszą zostać
przemyślane, są naiwne i przynoszą efekt odwrotny do zamierzonego.
Mając dzisiejsze narzędzia i technologie, możemy budować systemy
znacznie bardziej wartościowe niż większość prowadzonych obecnie pro-
jektów. Możemy napisać oprogramowanie, którego używa się z przyjem-
nością i nad którym również przyjemnie się pracuje. Oprogramowanie,
które wraz ze swoim wzrostem nie zaczyna nas ograniczać, ale tworzy
nowe możliwości, jest stale wartością dodaną również dla jego właścicieli.
552 ZAKOŃCZENIE
DODATEK
Wykorzystanie
szablonów
z tej książki
1
Traktując agregat jak zbiór, logicznie byłoby tłumaczyć „root” jako rdzeń.
W nomenklaturze przyjęto jednak zapożyczone z teorii drzew tłumaczenie słowa
„root” jako korzeń, ponieważ w teorii projektowania sterowanego dziedziną występuje
już pojęcie zbliżone, tj. „core”, tłumaczone jako jądro. W ten sposób intencje
twórcy dziedziny stają się bardziej przejrzyste — przyp. tłum.
większego systemu w celu wyciągnięcia na plan pierwszy DZIE-
DZINY GŁÓWNEJ.
dziedzina (ang. domain) — sfera wiedzy, wpływu lub działalności.
DZIEDZINA GŁÓWNA (ang. core domain) — zasadnicza część modelu,
niezbędna do osiągnięcia celów użytkownika, która wyróżnia aplikację
i sprawia, że ma ona wartość dla użytkownika.
efekt uboczny (ang. side effect) — dowolna zauważalna zmiana stanu
będąca zamierzonym lub niezamierzonym rezultatem operacji,
w szczególności zamierzone uaktualnienie.
ekspert dziedzinowy (ang. domain expert) — członek projektu progra-
mistycznego, którego obszarem działania jest bardziej dziedzina apli-
kacji niż tworzenie oprogramowania. W przeciwieństwie do użyt-
kownika oprogramowania, ekspert dziedzinowy posiada dogłębną
wiedzę na dany temat.
ENCJA (ang. entity) — obiekt określony nie przez atrybuty, a przez połą-
czenie jego ciągłości oraz tożsamości.
FABRYKA (ang. factory) — mechanizm pozwalający na wyodrębnienie
złożonej logiki tworzenia oraz zdefiniowanie abstrakcji typu two-
rzonego obiektu przez wzgląd na klienta.
funkcja (ang. function) — działanie obliczające i zwracające wynik bez
zauważalnego efektu ubocznego.
FUNKCJA POZBAWIONA EFEKTÓW UBOCZNYCH (ang. side-
-effect-free function) — zobacz funkcja.
INTERFEJS UJAWNIAJĄCY ZAMIAR (ang. intention-revealing
interface) — projekt, w którym nazwy klas, metod oraz innych ele-
mentów przekazują zarówno pierwotny zamiar dewelopera prowa-
dzący do ich utworzenia, jak i wartość dla dewelopera klienta.
iteracja (ang. iteration) — proces, w którym program jest cyklicznie udo-
skonalany w małych krokach. Dotyczy to również jednego z tych
kroków.
JĘZYK WSZECHOBECNY (ang. ubiquitous language) — język zbudo-
wany wokół modelu dziedziny i wykorzystywany przez wszystkich
członków zespołu w celu ukierunkowania wszystkich ich działań na
tworzenie oprogramowania.
KLASA SAMODZIELNA (ang. standalone class) — klasa, która może
zostać zrozumiana oraz przetestowana bez odwoływania się do
innych, za wyjątkiem podstawowych elementów systemowych lub
bibliotek.
560 SŁOWNIK
klient (ang. client) — fragment programu wywołujący właśnie projek-
towany element poprzez użycie jego właściwości.
kohezja (ang. cohesion) — ustalenie logiczne lub zależność.
komenda (ang. command lub modifier) — operacja, której efektem są pewne
zmiany w systemie (np. ustawienie zmiennej). Inaczej jest to ope-
racja, która w sposób zamierzony tworzy efekty uboczne.
kontekst (ang. context) — ustawienie, w którym pojawia się słowo lub
instrukcja określające jego znaczenie. Zobacz też KONTEKST
ZWIĄZANY.
KONTEKST ZWIĄZANY (ang. bounded context) — ustalony obszar
zastosowania danego modelu. Kontekst pozwala członkom zespołu
na jednoznaczne zrozumienie zakresu, obszaru, który ma pozostać
spójny, i oddzielenie go od tego, co może zostać zaprojektowane
w sposób niezależny.
MAPA KONTEKSTU (ang. context map) — reprezentacja używanego
w projekcie KONTEKSTU ZWIĄZANEGO oraz rzeczywistych
relacji pomiędzy nim a modelami.
model (ang. model) — system abstrakcji opisujący wybrane aspekty dzie-
dziny, który może również zostać użyty do rozwiązania problemów
związanych z tą dziedziną.
model dogłębny (ang. deep model) — wnikliwe wyrażenie podstawo-
wych zainteresowań ekspertów dziedzinowych oraz ich najbardziej
adekwatnej wiedzy. Model ten odrzuca powierzchowne aspekty
dziedziny oraz jej pobieżne interpretacje.
niezmienialność (ang. immutable) — właściwość stanu, który po utwo-
rzeniu nigdy nie ulega zmianie.
niezmiennik (ang. invariant) — ASERCJA dotycząca pewnego elementu
projektu, która w każdej chwili musi mieć wartość prawdziwą, za
wyjątkiem określonych sytuacji przejściowych, takich jak moment
wykonywania metody lub niepotwierdzonej transakcji bazodanowej.
OBIEKT WARTOŚCI (ang. value object) — obiekt opisujący charaktery-
stykę lub atrybut, aczkolwiek nieposiadający własnej odrębności.
odpowiedzialność (ang. responsibility) — zobowiązanie do wykonania
zadania lub posiadania informacji (Wirfs-Brock i inni 2003, str. 3).
paradygmat modelowania (ang. modeling paradigm) — szczególny styl
doskonalenia pomysłów w dziedzinie, powiązany z narzędziami do
tworzenia między nimi analogii programowych (na przykład progra-
mowanie zorientowane obiektowo oraz programowanie logiczne).
SŁOWNIK 561
pojęcie niejawne (ang. implicit concept) — pojęcie niezbędne do zrozu-
mienia znaczenia modelu lub projektu, które jednak nie zostało
nigdy wspomniane.
projekt elastyczny (ang. supple design) — projekt, który umożliwia dewe-
loperowi klienta skorzystanie z założeń odziedziczonych z modelu
dogłębnego w celu stworzenia jasnych, uniwersalnych wyrażeń pro-
wadzących do solidnych rezultatów. Co równie istotne, ten sam model
dogłębny wykorzystywany jest w tym przypadku do uproszczenia
projektu dla osoby go wykorzystującej, w taki sposób, aby mogła ona
go uformować lub zmienić jego kształt w celu obsłużenia nowych
spostrzeżeń.
PROJEKT STEROWANY MODELEM (ang. model-driven design) —
projekt, w którym niektóre podzbiory elementów systemu odpo-
wiadają ściśle elementom modelu. Jest to również proces tworzenia
ściśle ze sobą związanych modelu oraz jego implementacji.
projekt strategiczny (ang. strategic design) — decyzje dotyczące mode-
lowania oraz programowania mające zastosowanie do dużych części
systemu. Tego rodzaju decyzje wpływają na cały projekt i muszą
być definiowane na poziomie całego zespołu.
projektowanie deklaratywne (ang. declarative design) — rodzaj progra-
mowania, w którym dokładny opis właściwości nadzoruje model
oprogramowania. Innymi słowy, jest to wykonywalna specyfikacja.
REPOZYTORIUM (ang. repository) — mechanizm symulujący zestaw
obiektów, obejmujący metody ich przechowywania, odczytywania
oraz przeszukiwania.
struktura dużej skali (ang. large-scale structure) — zestaw wysokopo-
ziomowych pomysłów, zasad lub też obu tych rzeczy jednocześnie,
tworzący wzorzec projektowy całego systemu. Jest to język pozwa-
lający na omówienie lub zrozumienie systemu w kilku szerokich
krokach.
unifikacja (ang. unification) — wewnętrzna spójność modelu, polegająca
na tym, że każda definicja jest jednoznaczna i nie ma żadnych
sprzeczności w regułach.
USŁUGA (ang. service) — działanie oferowane w postaci interfejsu, które
w modelu istnieje niezależnie, bez żadnego stanu.
warstwa dziedziny (ang. domain layer) — część projektu oraz jego imple-
mentacji odpowiedzialna za logikę dziedziny w obrębie ARCHI-
TEKTURY WARSTWOWEJ. To właśnie w tej warstwie wyrażony
jest model dziedziny.
562 SŁOWNIK
WARTOŚĆ CAŁKOWITA (ang. whole value) — obiekt modelujący poje-
dyncze, kompletne pojęcie.
wzorzec analityczny (ang. analysis pattern) — grupa pojęć przedstawiają-
cych w modelowaniu biznesowym wspólną strukturę. Może odnosić
się do pojedynczej dziedziny lub też rozciągać się na wiele dziedzin
(Fowler 1997, str. 8).
wzorzec projektowy (ang. design pattern) — opis współdziałających
obiektów oraz klas dostosowanych w celu rozwiązania generycznego
problemu projektowego w określonym kontekście (Gamma i inni
1995, str. 3).
ZARYS KONCEPCYJNY (ang. conceptual contour) — podstawowa spój-
ność samej dziedziny, odzwierciedlona w modelu. Może ona pomóc
projektowi uwzględniać w sposób bardziej naturalny wszelkie zmiany.
SŁOWNIK 563
564 SŁOWNIK
BIBLIOGRAFIA
566 BIBLIOGRAFIA
PRAWA DO ZDJ}m
© Royalty-Free/Corbis
Odcisk palca (rozdziaï 5., strona 115), stacja obsïugi (rozdziaï 5., strona 132),
fabryka samochodów (rozdziaï 6., strona 166), bibliotekarka (rozdziaï 6.,
strona 178)
Martine Jousset
Winogrona (rozdziaï 6., strona 155), drzewa oliwne (mïode i stare)
(Epilog, strony 546 – 547)
Ross J. Venables
odzie wiosïowe (grupa oraz pojedyncze) (rozdziaï 14., strony 383
oraz 414)
A definiowanie
obiektów, 107
abstract factory, 168 poddziedzin, 321
ADAPTER, 411 tożsamości, 121
agile, 51 deklaratywny styl projektowania, 312
AGREGATY, 153, 155–165, 203
destylacja, 443, 451, 473, 530, 534
akceptacja, 429
aktywa, 254 destylacja
analiza modelu, 41
dziwnej implementacji, 247 strategiczna, 445
zysków, 402 diagram UML, 36, 63, 65
antywzorzec inteligentnego interakcji, 64
interfejsu użytkownika, 102 klas, 39, 46, 70
ARCHITEKTURA WARSTWOWA, 47, sekwencji, 64, 346
95, 101, 142, 443 dialekt, 53
ASERCJE, 286, 293–297 dodanie
asocjacje, 109, 111, 201, 258
obiektu, 210
asocjacje pomiędzy klasami, 113
operacji obsługi, 208
atrybuty, 117, 201
dokument, 63, 66, 67
destylacji, 465, 467
B XML, 98
baza danych, 130 dokumentacja MAP KONTEKSTÓW, 394
BUDOWNICZY, 169 dokumenty projektowe, 65
builder, 169 dostęp do
bazy danych, 143, 144
USŁUG, 137
C WARTOŚCI, 181
cechy USŁUGI, 133 dostosowanie, 432
cel operacji logistycznej, 197 duża spójność, high-cohesion, 108
chemiczny JĘZYK OPUBLIKOWANY, 422 dystrybucja spłaty kapitału, 233
CIĄGŁA INTEGRACJA, 376, 383, 429, 438 dziedzina, 30, 93
ciągła nauka, 44 DZIEDZINY GŁÓWNE, 444, 446, 458, 472
cykl życia obiektu dziedziny, 153, 154
cykliczna referencja, 202 E
czyszczenie pamięci, 129, 153
efekty uboczne metody, 289
D eksperci dziedzinowi, 61
elastyczność projektu, 280, 328
decyzje
elementy
proaktywne, 375
składowe projektu, 37, 38, 89
projektowe, 538
wydajnego modelowania, 40
zespołowe, 427
ENCJE, 45, 115–122, 125–130, 158, 199
F interakcje z FABRYKĄ, 168
INTERFEJS, 173, 283
FABRYKA ABSTRAKCYJNA, 168 USŁUGI, 100
FABRYKI, 154, 166–177 użytkownika, 96, 102
ENCJI, 174 INTERFEJSY UJAWNIAJĄCE ZAMIAR,
WARTOŚCI, 174 286, 297
factory method, 168 interpretacja asocjacji, 109
faktoryzacja OGÓLNYCH inwestycja pożyczkowa, 230
PODDZIEDZIN, 473 istota programu, 32
fałszywe pokrewieństwa, 381 izolacja, 106
FASADA, 410 izolacja dziedziny, 98, 198
funkcja, 287
calculateAccrualsThroughDate(), 251
FUNKCJE BEZ EFEKTÓW
J
UBOCZNYCH, 286–288, 297 JĄDRO WSPÓŁDZIELONE, 376,
funkcjonalność 395–398, 406, 435, 438
powtórzenie operacji, 206 JĘZYK, 53, 61
sprawdzanie przydziału, 215 definiowany modelem, 41
zmiana miejsca przeznaczenia Java, 113
ładunku, 206 mówiony, 58
naturalny, 70
G oparty na modelu, 61
OPUBLIKOWANY, 420–423, 441
garbage collection, 153 pidżynowy, 59
garbage collector, 129 UML, 63
generowanie, 270 WSZECHOBECNY, 53, 60, 67, 146, 192
GENERYCZNE PODDOMENY, 286 języki właściwe dziedzinie, 311
głęboka destylacja modelu, 482
granice AGREGATU, 203
K
I kalkulator, 254
kaskada spostrzeżeń, 326
idea JĘZYKA WSZECHOBECNEGO, 55 kierunek asocjacji, 110
identyfikator, 207 klasa Enterprise, 370
identyfikator obiektu, 119 KLASY SAMODZIELNE, 304–306
implementacja, 20 kodowanie pakietów, 141
asocjacji, 109 KOMPOZYT, 315, 356–361
ENCJI, 117 kompromis, 434
kolekcji, 212 komunikacja, 51, 528
REPOZYTORIÓW, 188 koncepcja trasy, 358
REPOZYTORIUM, 185, 186 KONFORMISTA, 404, 405
WARSTWY ZAPOBIEGAJĄCEJ konstruktor, 207
USZKODZENIU, 409 konstruktory publiczne, 172
informacje o projekcie, 65 KONTEKST, 425, 430
inicjalizacja Itinerary, 58 rezerwacji, 379
inicjalizator, 288 ZWIĄZANY, 55, 106, 372, 376–382,
integracja wzorców, 322 390, 394, 428, 488, 521, 527, 532
integralność KOORDYNATOR APLIKACJI, 99
modelu, 373 korygowanie pożyczki, 235
procesu zakupowego, 160 korzeń AGREGATU, 188, 206
INTELIGENTNY INTERFEJS koszt utworzenia RDZENIA
UŻYTKOWNIKA, 102, 105 ODDZIELONEGO, 475
570 SKOROWIDZ
kryzys, 368 mieszanie farb, 284, 295
kwalifikacja asocjacji, 113 mikrorefaktoryzacja, 225
kwalifikator, 109 minimalizm, 528
minimalna abstrakcja dziedziny, 56
L model, 31, 101
dziedziny, 31, 51, 101
lista płac, 519 firmy spedycyjnej, 243
logika GŁÓWNY, 549
biznesowa, 94 konta inwestycyjnego, 111
niezmienników, 174 księgowy, 336
logistyka morska, 213 logistyki, 213
pogłębiony, 233
Ł pożyczki, 234
transportu morskiego, 476
łączenie UML, 63, 68
dwóch systemów, 216 wzbogacony wiedzą, 41, 57
FABRYKI z REPOZYTORIUM, 190 zrefaktoryzowany, 503
KONTEKSTÓW, 435, 438 zrestrukturyzowany, 503
paradygmatów, 150 modele
SPECYFIKACJI, 313 dogłębne, 48, 226, 227
strategii, 531 dziedzinowe, 351
struktur dużej skali, 531, 534 księgowe, 336
warstw, 99 obiektowe, 149
objaśniające, 68
M modelowanie
AGREGATÓW, 154, 212
magazyn obiektów, 191
dogłębne, 241
MAPA KONTEKSTÓW, 376, 386–395,
ENCJI, 120, 212
427, 534, 549
na głos, 58
mapa nawigacyjna, 91, 445
obiektowe, 117
mapowanie
pojęć, 256, 283
metadanych, 182
USŁUGI, 136
obiektowo-relacyjne, 267
WARTOŚCI, 212
MECHANIZM, 472
wydajne, 40
SPÓJNOŚCI, 286
MODEL-WIDOK-KONTROLER, 99
w schemacie organizacyjnym, 470
modularność, 145
metafora naiwna, 495
MODUŁ
METAFORY SYSTEMU, 493, 495
MODUŁ RDZENIA, 481
metoda
MODUŁY, 108, 138–141, 213, 487
anInvoice.isOverdue(), 261
DZIEDZINY GŁÓWNEJ, 535
asSql(), 269
zwinne, 140
dostępu, accessor method, 109
moment przełomowy, 229, 231
isOverdue(), 261
MVC, model-view-controler, 99
isSafelyPacked(), 274
mixIn(), 289
METODY N
FABRYCZNE, 209 nadkomplet, 258
refaktoringu, 42 naliczanie odsetek, 335
testujące, 297 naruszony niezmiennik, 162
WYTWÓRCZE, 168, 172, 209 narzędzie QueryService, 112
metodyka, 65 narzut, 375
metodyka zwinna, agile, 51
SKOROWIDZ 571
nasłuchiwanie obsługa XML, 423
brakującego pojęcia, 243 oczekiwania komponentu podrzędnego, 402
języka, 242 oddelegowanie implementacji, 455
nazwa oddzielanie
klasy, 284 poleceń, 324
WZORCA, 557 RDZENIA, 476
MODUŁU, 213 warstwy aplikacji od dziedziny, 103
nieobiekty, 149 ODDZIELNE DROGI, 376, 415–417, 435
niezmiennik, 128, 164, 174, 256 odkrywanie pojęć niejawnych, 241
niezmienniki AGREGATU, 159 odnajdowanie tras, 352
normalizacja, 192 odpowiedzialności
notacja UML, 51 dziedziny, 219
numer operacyjne, 498
PESEL, 123 potencjału, 499
Social Security, 123 wsparcia decyzji, 500
odpytywanie REPOZYTORIUM, 184
O odsetki, 247, 335
odszukiwanie, 266
obiekt, 108 odtwarzanie obiektu, 175–177
Brokerage Account, 111, 170 odwołania zwrotne, 99
Cargo, 197, 199 OGÓLNE PODDZIEDZINY, 454,
Catalog Part, 173 459, 471
Charge, 373 ograniczenia, 257, 259
Container, 274 asocjacji, 110, 114
Customer, 199 kierunku interpretacji, 111
Delivery History, 198 określanie strategii, 536
Delivery Specification, 197, 198 operacje, 507
Facility, 231, 238 obsługi, 208
Invoice, 263 spedycyjne, 200
Itinerary, 58 na tożsamości, 121
Loan, 238 operator
Location, 200 AND, 316, 318
Packer, 275 NOT, 318
Paint, 291 OPIS WIZJI, 468
pojęciowy, 144 OPIS WIZJI DZIEDZINY, 461, 462, 463
REPOSITORY, 268 opowiadanie historii, 506
Route Specification, 58 optymalizacja bazy danych, 130
Routing Service, 58 opublikowany projekt, 454
Share Pie, 238, 325 organizacja, 394
SharePie, 326, 329 osadzanie reguły, 268
SPECIFICATION, 268 oszacowanie, 536
Strategia, 221 OTWARTE USŁUGI GOSPODARZA, 395
Trade Order, 170
typu Cargo, 46
P
typu Voyage, 46
obiekty PAKIETY, 138
dziedziny, 259 pakowacz, 272, 275
niezmienne, 128 paradygmat
referencyjne, 115 obiektowy, 146
WARTOŚCI, 128 relacyjny, 152
złożone, 168 paradygmaty modelowania, 146
OBSERWATOR, 99 partycjonowanie, 144
572 SKOROWIDZ
PCB, printed-circuit board, 35 projektowanie
plan główny, 542 asocjacji, 131, 201
początkowy stan zamówienia, 161 deklaratywne, 310, 311
PODDZIEDZINY, 322, 535 interfejsu, 173, 409
PODDZIEDZINY OGÓLNE, 444, 452 kontraktowe, 90
podejmowanie decyzji, 539 modeli, 224
podział obiektów, 190
na pakiety, 144 OBIEKTÓW WARTOŚCI, 128
na partycje, 144 obwodów drukowanych, 35
na warstwy, 97, 498 operacji, 121
systemu informatycznego, 95 sterowane dziedziną, 17, 31, 93, 283
podział usług, 136 STEROWANE MODELEM, 68, 91, 105
pojęcia STEROWANE MODELEM
ukryte, 325 z ARCHITEKTURĄ
wysokopoziomowe, 283 WARSTWOWĄ, 102
polimorfizm, 171 sterowane odpowiedzialnością, 90
polityka, policy, 47, 351, 508 projekty elastyczne, 227, 529
nadkompletu, 48, 258 prototyp, 277
trasowania, 352, 503 prototyp pakowacza, 275
połączenie modelu z implementacją, 107 przechowywanie danych, 150
poprawa wydajności, 219 przeglądanie scenariuszy, 206
PORZĄDEK EWOLUCYJNY, 490, 505 przekształcanie granic, 428
poszukiwanie pojęć, 253 przełom, 229–231
potencjał, 507 przepływ
powiązanie informacji, 216
warstw, 99 zadań, 150
wzorców projektowych, 349 przetwarzanie
powtarzanie prób, 255 wiedzy, 35, 41
POZIOM WIEDZY, 511–520 wsadowe, 341
poziomy refaktoryzacji, 224
pozycja, 238
R
pożyczka, 234
predykat, 261 RDZEŃ, 458
proces, 259 ABSTRAKCYJNY, 481, 521, 548
odkrywania, 228 DZIEDZINY, 449
projektowania, 20 ODDZIELONY, 474–479
XP, 22 oznaczony, 466
programowanie ekstremalne, 21, 66 WYRÓŻNIONY, 464–468
programowanie realizacja produkcji, 523
ekstremalne, XP, 21 refaktoring, 167, 209
obiektowe, 94 implementacji, 45
wizualne, 103 modelu obiektowego, 193
projekt refaktoryzacja, 223, 236, 284, 363–368,
AGREGATU, 209 483, 527
dla programistów, 366 aplikacji, 289
dystrybucji płatności, 323 kodu, 261
elastyczny, 279 obiektu, 290
GŁÓWNEJ DZIEDZINY, 371 ograniczenia, 257
STEROWANY MODELEM, 89, 101, referencja, 178, 202
107, 146, 151, 264 referencje do obiektów, 158
strategiczny, 369, 540 REFLEKSJA, 516
ubezpieczeń, 416 reguła wymagalności, 264
SKOROWIDZ 573
reguły SPECYFIKACJA, 185, 197, 260–274, 315,
biznesowe, 262 317, 319
księgowania, 341, 346 Arystotelesa, 321
łączenia nieobiektowych elementów, 151 KOMPOZYTU, 317
organizacji, 515 SPÓJNE MECHANIZMY, 469, 471, 473
relacje spójność zmian, 156
kwalifikowane, 110 SPÓJNY MECHANIZM, 469–471
pomiędzy elementami sprzeczności, 252
AGREGATU, 158 SQL, 113, 268
pomiędzy KONTEKSTAMI standaryzowany produkt, 453
ZWIĄZANYMI, 395 stosowanie wzorców analitycznych, 333
z FABRYKAMI, 189 STRATEGIA, 47, 351–355, 536
relacyjne bazy danych, 112, 190, 267 strefy czasowe, 458
REPOZYTORIA, 154, 178–190, 202, 269 struktura, 502, 504, 526, 537
repozytorium faktur, 266 struktury dużej skali, 485, 524
restrukturyzacja, 529 styl deklaratywny, 473
rezerwacje, 402, 413 subsumpcja, 318, 320
RGB, 290 system
rodzaj wiedzy, 45 automatyzacji fabryki, 509
rodzaje bankowości inwestycyjnej, 510
działalności, 508 do wypłat pracowniczych, 518
izolacji, 106 do wypłat pracowniczych, 512
rola, 197, 201 logistyczny, 195
Routing Service, 56 logistyki morskiej, 498
rozdzielanie zagadnień, 95 obsługi zamówienia, 160
rozdzielenie warstw, 98 w projektowaniu, 431
rozpoznawanie odprysków pojęciowych, 381 zewnętrzny, 430
rozróżnianie ENCJI oraz WARTOŚCI, 199 systemy
rozszerzalny język znaczników, 421 klasy Enterprise, 370
rozszerzenie wsparcia decyzji, 508
AGREGATU, 170 szablony, 553
języka, 62 szkielet
SPECYFIKACJI, 313 architektury, 100, 188
rozwijanie decyzji zespołowych, 476 CIM, 524
ryzyko projektowe, 459 języka, 54
KOMPONENTÓW
S DOŁĄCZANYCH, 521, 524
SEMATECH CIM, 523
samodyscyplina, 528 techniczny, 541
samodzielna implementacja, 455
scenariusz refaktoryzacji, 363
Ś
scenariusze, 206
schemat organizacyjny, 470, 472 śledzenie
SEGMENT PRZEDSIĘBIORSTWA, 219 płatności, 116
segmentacja biznesu, 217 tożsamości ENCJI, 126
serializacja, 122
sieć ścieżek, 37 T
silniki reguł biznesowych, 145, 150
SINGLETON, 137 testowanie, 297, 394
SKRYPT TRANSAKCYJNY, 105 języka, 54
słabe związanie, loose-coupling, 108 trasy, 391
topologia, 37, 39
574 SKOROWIDZ
tożsamość, 121 interfejsu użytkownika, 96, 97
ENCJI, 117, 126 OCHRONY PRZED
globalna, 158 USZKODZENIEM, 106
lokalna, 158 potencjału, 509
transakcje, 238 prezentacji, 96
transformacje, 435 ZAPOBIEGAJĄCA
trasowanie, 353, 503 USZKODZENIU, 218,
tworzenie 407–414, 430
AGREGATU, 171 zobowiązania, 509
elementu narzuty, 525 warstwy
harmonogramów, 456 powiązania, 99
interfejsu użytkownika, 103 DZIEDZINY, 267
modelu, 36 MAPOWANIA METADANYCH, 180
modelu dziedziny, 91 ODPOWIEDZIALNOŚCI, 496, 505,
na zamówienie, 270 526, 533
obiektów, 167, 207 WARTOŚCI, 125–131, 181
obiektów złożonych, 168 WARTOŚĆ, value-object, 107
pakietów, 142 warunki brzegowe, 59
RDZENIA ODDZIELONEGO, 475 wdrożenie, 433
REPOZYTORIUM, 211 wiedza biznesowa, 164
wielorakość relacji, 109
U WIZJA DZIEDZINY, 461
wprowadzenie aplikacji, 198
ujawnianie ukrytych pojęć, 325 wsparcie decyzji, 508
ukryte pojęcie, 45 współdzielenie
UML, Unified Modeling Language, 51, 63 bazy danych, 161
unifikacja, 375 obiektów, 128
UPORZĄDKOWANIE EWOLUCYJNE, wybór, 266
510 celów refaktoryzacji, 483
USŁUGA, service, 99, 107, 132–135, 217 FABRYK, 169
aplikacyjna, 136 RDZENIA dziedziny, 449
dziedzinowa, 136 REPOZYTORIÓW, 204
infrastrukturalna, 136 strategii kontekstu modelu, 427
OTWARTEGO GOSPODARZA, warstw, 505
418, 420, 441 z kolekcji, 308
trasowania, 56, 353 wyciąganie pojęć, 242
usuwanie wyczucie czasu, 367
asocjacji, 109 wydajność, 219
zatorów projektowych, 277 wydobywanie ukrytego pojęcia, 45
utrzymywanie integralności, 373 wygaszanie systemu, 439
używanie wyizolowanie dziedziny, 93
języka, 51, 195 wykonywanie reguł księgowania, 342
JĘZYKA WSZECHOBECNEGO, wymagania, 434
54, 62 wymagania specjalizowane, 375
wymogi infrastruktury, 142
W wymuszanie
niezmienników, 161
walidacja, 265 niezmiennika AGREGATU, 165
warstwa, 95, 97 wyświetlanie informacji, 96
aplikacji, 96, 97 wyznaczanie trasy ładunku, 499
dziedziny, 96, 97, 101, 134 wzbogacony model dziedziny, 57
infrastruktury, 96, 99
SKOROWIDZ 575
wzorce ZAMKNIĘCIE OPERACJI, 307–310, 327
analityczne, 217, 333, 346 zapłata, 237
destylacyjne, 460 zapytanie, 179, 182, 184
elementów modelu, 107 oparte na SPECYFIKACJI, 185
projektowe, 349 SQL, 268
wzorzec ZARYSY
ARCHITEKTURY WARSTWOWEJ, KONCEPCYJNE, 298–303, 507, 529
102 sum przyrostowych, 300
INTELIGENTNEGO INTERFEJSU zarządzanie
UŻYTKOWNIKA, 103, 105 kontami bankowymi, 97
PYŁKU, 361 obiektami, 153
REPOZYTORIUM, 182 ryzykiem projektowym, 459
ROZDZIELENIA MODELU- zmianą, 161
WIDOKU, 99 zasada podwójnego księgowania, 337
SEGMENT PRZEDSIĘBIORSTWA, zastosowanie
219 modelu dziedziny, 29
SINGLETON, 137 SPECYFIKACJI, 264
PECYFIKACJI, 197 zduplikowane pojęcia, 381
STRATEGIA, 47S ZESPOŁY PROGRAMISTYCZNE
KLIENTA – DOSTAWCY, 399
X zespół, 60
zespół architektoniczny, 538
XP, extreme programming, 21 ziarna encyjne, 101
ziarnistość, 136
Z złożone funkcjonalności, 19
zmiana
zablokowanie zamówienia, 163
JĘZYKA WSZECHOBECNEGO, 55
zachowanie obiektu, 64
refaktoryzacyjna, 237
zakleszczenie, 164
zmienna logiczna, 56
zakres USŁUGI, 100
zwiększanie destylacji, 451
zalety REPOZYTORIÓW, 183
zwrot z refaktoryzacji, 229
zależności
pojęciowe, 506, 509, 510
projektowe, 99
576 SKOROWIDZ