Professional Documents
Culture Documents
Domain-Driven Design Eric Evans
Domain-Driven Design Eric Evans
ISBN: 978-83-283-0528-1
All rights reserved. No part of this book may by reproduced or transmitted in any
form or by any means, electronic or mechanical, including photocopying, recording or
by any information storage retrieval system, without permission from Pearson Education, Inc.
Polish language edition published by HELION S.A. Copyright © 2015.
Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce
informacje były kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich
wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich.
Autor oraz Wydawnictwo HELION nie ponoszą również żadnej odpowiedzialności za ewentualne
szkody wynikłe z wykorzystania informacji zawartych w książce.
Wydawnictwo HELION
ul. Kościuszki 1c, 44-100 GLIWICE
tel. 32 231 22 19, 32 230 98 63
e-mail: helion@helion.pl
WWW: http://helion.pl (księgarnia internetowa, katalog książek)
Drogi Czytelniku!
Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres
http://helion.pl/user/opinie/domdri_ebook
Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję.
699b15244ae879c728b313782703a4d7
6
Wyrazy uznania dla książki
„Ta książka powinna znajdować się na półce każdego wnikliwego
programisty”.
— Kent Beck
699b15244ae879c728b313782703a4d7
6
„Eric Evans zdecydowanie przekonuje o ważności modelowania dziedziny,
stanowiącego centralny punkt uwagi w trakcie programowania, i dostarcza
solidny szkielet oraz zestaw technik umożliwiających pomyślne wykonanie
tego zadania. To ponadczasowa mądrość, która pozostanie aktualna na
długo po tym, gdy obecne metodologie wyjdą z mody”.
— Dave Collins,
autor książki pt. Designing Object-Oriented User Interfaces
699b15244ae879c728b313782703a4d7
6
Dla mamy i taty
699b15244ae879c728b313782703a4d7
6
699b15244ae879c728b313782703a4d7
6
SPIS TREŚCI
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
PRZEDMOWA
15
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
WSTĘP
1
ang. domain-driven design — przyp. tłum.
17
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
I
Zastosowanie
modelu
dziedziny
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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.).
699b15244ae879c728b313782703a4d7
6
2. Model stanowi szkielet języka używanego przez wszystkich członków zespołu.
Dzięki połączeniu modelu oraz implementacji programiści mogą
pomiędzy sobą rozmawiać o programie we wspólnym języku, a także
porozumiewać się z ekspertami dziedzinowymi bez konieczności
angażowania tłumacza. Dodatkowo, ponieważ język też jest oparty
na modelu, to do jego udoskonalenia można wykorzystać również
nasze naturalne zdolności językowe (patrz rozdział 2.).
3. Model stanowi samą kwintesencję wiedzy. Model stanowi uzgodnioną
w zespole metodę kształtowania wiedzy o dziedzinie, a także rozpo-
znawania jej najbardziej istotnych elementów. Pomaga on uchwycić
sposób, w jaki myślimy o dziedzinie, wybierając odpowiednie defi-
nicje, analizujemy jej zagadnienia, a także wiążemy je wzajemnie.
Wspólny język pozwala deweloperom oraz ekspertom dziedzino-
wym efektywnie pracować w trakcie zbierania informacji. Połącze-
nie modelu z implementacją pozwala traktować odczucia związane
z używaniem pierwszych wersji programu jako wnioski do ulep-
szenia modelu w procesie modelowania (patrz rozdział 1.).
W kolejnych trzech rozdziałach zajmiemy się omówieniem znaczenia
oraz wartości każdego z trzech powyższych założeń, a także pokazaniem,
w jaki sposób są one ze sobą powiązane. Wykorzystanie modelu w ten
właśnie sposób może sprzyjać tworzeniu oprogramowania wyposażonego
od początku w bogatą funkcjonalność. W przeciwnym razie wymagałoby
to dużego wysiłku w ulepszaniu programu już w trakcie procesu jego
tworzenia.
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
699b15244ae879c728b313782703a4d7
6
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ść.
699b15244ae879c728b313782703a4d7
6
Książka ta udowodni, że w procesie opracowywania dziedziny kryje się
wiele możliwości doskonalenia umiejętności dziedzinowych. W rzeczy-
wistości zagmatwanie większości dziedzin oprogramowania stanowi inte-
resujące wyzwanie techniczne. Podobnie w wielu dziedzinach naukowych
ich „złożoność” stanowi jeden z najbardziej interesujących tematów, ponie-
waż naukowcy starają się właśnie zmierzyć się z chaosem prawdziwego
świata. Twórca oprogramowania napotyka na wyzwanie o podobnej skali,
stojąc w obliczu złożonej dziedziny, która nie została jeszcze zbytnio sfor-
malizowania. Tworzenie jasnego modelu radzącego sobie z tą złożonością
jest bardzo ekscytującą czynnością.
W celu zgłębienia dziedziny i utworzenia wydajnych modeli progra-
miści mogą skorzystać z pewnych uporządkowanych sposobów myślenia.
Istnieją metody projektowania mogące usystematyzować rozciągniętą dzie-
dzinę przyszłego zastosowania programu. Używanie ich może uczynić
programistę bardziej przydatnym, nawet jeżeli początkowo nie był on
mistrzem danej dziedziny.
34 CZĘŚĆ I
699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 1.
Przetwarzanie wiedzy
Rysunek 1.1.
1
ang. printed-circuit board, w skrócie PCB — przyp. tłum.
35
699b15244ae879c728b313782703a4d7
6
Rysunek 1.2.
2
ang. ref-des — skrót od „reference destination” — przyp. tłum.
699b15244ae879c728b313782703a4d7
6
Projektant: Czy twierdzisz, że nóżka należy do jednego elementu skła-
dowego i łączy się tylko z jedną siecią połączeń?
Ekspert 1: Tak, to prawda.
Ekspert 2: Również każda sieć połączeń ma swoją topologię (ang. topo-
logy), czyli organizację determinującą sposób połączenia elementów
z siecią.
Projektant: W porządku, co powiecie więc na to?
Rysunek 1.3.
699b15244ae879c728b313782703a4d7
6
Rysunek 1.4.
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.
699b15244ae879c728b313782703a4d7
6
Rysunek 1.6.
Rysunek 1.7.
699b15244ae879c728b313782703a4d7
6
kolejnych dniach byłem w stanie przedstawić prosty symulator sondy.
Wprawdzie używał on jedynie danych przykładowych i wypisywał na kon-
soli użytkownika prosty tekst, jednak obliczał rzeczywistą długość ścieżki
przy użyciu obiektów języka Java. Były one utworzone według modelu
zdefiniowanego przeze mnie wspólnie z ekspertami.
Ta namacalność prototypu pozwoliła ekspertom w lepszy sposób
zrozumieć znaczenie modelu oraz jego związek z działającym programem.
Od tego momentu nasze rozmowy stały się bardziej interaktywne, ponie-
waż byli w stanie zrozumieć, w jaki sposób wykorzystałem nowo zdobytą
wiedzę do budowy modelu oraz programu. A co więcej, mogli użyć pro-
totypu do sprawdzenia własnych przemyśleń.
W modelu, który stał się oczywiście znacznie bardziej złożony od tego
przedstawionego w tym rozdziale, znalazła zastosowanie wiedza dziedzi-
nowa o projektowaniu obwodów drukowanych, niezbędna do rozwiązy-
wanych przez nas problemów. Gromadził on w sobie wiele synonimów
oraz niewielkich różnic w opisach. Pomijał setki faktów, rozumianych
przez inżynierów, jednak niezwiązanych bezpośrednio z problemem,
w rodzaju rzeczywistych cech cyfrowych elementów. Każdy podobny do
mnie specjalista od oprogramowania mógłby spojrzeć na diagramy i w ciągu
kilku minut zrozumieć, do czego ma służyć to oprogramowanie. Co wię-
cej, dysponowałby on pewnym wzorcem, który pomógłby mu w szybszym
uporządkowaniu nowych informacji, a także nauce, co z kolei doprowa-
dziłoby do lepszych przypuszczeń co do ważności konkretnych informacji,
a także lepszej komunikacji z inżynierami obwodów drukowanych.
W trakcie gdy inżynierowie opisywali potrzebne im funkcjonalności,
prowadzili mnie przez scenariusze współdziałania obiektów. Kiedy jednak
obiekty w modelu nie pozwalały obsłużyć ważnego scenariusza, wymy-
ślaliśmy nowe lub zmienialiśmy stare, stopniowo przetwarzając znaną im
wiedzę. Kiedy dopracowywaliśmy model, zmieniał się również kod. Po
kilku miesiącach inżynierowie dysponowali bogatym narzędziem, które
znacznie wykraczało poza ich oczekiwania.
699b15244ae879c728b313782703a4d7
6
2. Doskonalenie języka zdefiniowanego modelem. Początkowo inżynierowie
musieli wyjaśniać mi nawet najprostsze zagadnienia projektowania
obwodów, a ja musiałem im objaśniać znaczenie poszczególnych
diagramów klas. Jednak w trakcie rozwoju projektu każdy z nas
potrafił już pobrać definicje bezpośrednio z modelu, uporządkować
je w zdania spójne z jego budową, a w rezultacie mogliśmy zrozu-
mieć się bez potrzeby tłumaczenia.
3. Wypracowanie modelu wzbogaconego wiedzą. Obiekty posiadały pewne
zachowanie i wymuszone reguły. Model nie był jedynie prostym
schematem danych, lecz stanowił niepodzielny wkład w rozwiązy-
wanie złożonych problemów. Udało się w nim uchwycić różnego
rodzaju wiedzę.
4. Destylacja modelu. W trakcie rozbudowywania modelu dodawane były
do niego ważne zagadnienia, lecz równocześnie jednakowo ważne
były odrzucane w chwili, gdy nie znajdowały uzasadnienia. Kiedy
niepotrzebne zagadnienie było dowiązywane do tego istotnego, opra-
cowywany był nowy model, który wyróżniał istotę problemu w ten
sposób, aby ten poprzedni model mógł zostać odrzucony.
5. Dyskusja oraz eksperymenty. Użyty język, w połączeniu ze szkicami
oraz atmosferą sprzyjającą dyskusji, zmienił nasze rozmowy w labo-
ratoria modelowania, w których setki eksperymentalnych odmian
modelu mogły być wypróbowywane, testowane i oceniane. W trakcie
zespołowej pracy nad scenariuszami same wypowiadane zdania mogły
służyć za wstępny test wykonalności zaproponowanego modelu,
ponieważ ucho mogło szybko wychwycić klarowność lub dziwność
wyrażenia.
To właśnie kreatywność zawarta w dyskusji oraz wielu eksperymen-
tach, wzmocniona użyciem języka opartego na modelu oraz zdyscyplino-
wana kolejnymi wnioskami z jego iteracyjnej implementacji, umożliwiła
stworzenie bogatego w wiedzę modelu i jego destylację. Tego rodzaju
przetwarzanie wiedzy przemienia wiedzę zespołu w cenne modele.
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
699b15244ae879c728b313782703a4d7
6
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.
699b15244ae879c728b313782703a4d7
6
gramowanie, jednak projekt nigdy nie osiągnie stadium, w którym nowe
wydajne funkcjonalności programu będą wynikać z innych.
Oczywiście, zdolni programiści w sposób naturalny rozpoczną two-
rzenie opisu abstrakcyjnego programu i modelu, który będzie można dalej
rozwijać. Jeżeli jednak proces ten będzie zachodzić jedynie w otoczeniu
technicznym, bez współpracy z ekspertami dziedzinowymi, zdefiniowane
pojęcia będą prymitywne. Płycizna wiedzy będzie skutkować powstaniem
programu, który będzie wprawdzie wykonywać podstawowe zadania, jed-
nak będzie mu brakować głębszego powiązania ze sposobem rozumo-
wania ekspertów dziedzinowych.
Komunikacja pomiędzy członkami zespołu będzie ulegać zmianie
w czasie, gdy będą oni razem udoskonalać model. Ciągła modyfikacja
modelu dziedziny zmusza programistów do poznania wielu istotnych pryn-
cypiów wspieranego biznesu, zamiast jedynie bezwolnego mechanicznego
produkowania kolejnych funkcjonalności. Również eksperci dziedzinowi
bardzo często udoskonalają swoje własne zrozumienie tej dziedziny, będąc
zmuszonymi do rozłożenia tego, co znają, na czynnik pierwsze. Co więcej,
nabywają oni zrozumienia ograniczeń pojęciowych narzuconych projek-
towi informatycznemu.
Wszystko to razem sprawia, że członkowie zespołu zaczynają prze-
twarzać wiedzę w sposób bardziej kompetentny. Odsiewają rzeczy nie-
istotne, przekształcając model w coraz bardziej przydatną jego postać.
Ponieważ do tego procesu przyczyniają się zarówno analitycy, jak i pro-
gramiści, model jest uporządkowany i przejrzyście zdefiniowany, co spra-
wia, że może być wykorzystany w implementacji. Ponieważ dodatkowo
do modelu wkład swój wnoszą eksperci dziedzinowi, odzwierciedla on
rzeczywistą dogłębną wiedzę biznesową. Opis abstrakcyjny zakłada praw-
dziwe pryncypia biznesowe.
W trakcie ulepszania modelu staje się on narzędziem do porządko-
wania informacji, które będzie używane w całym projekcie. Skupia on na
sobie proces analizy wymagań. Zdecydowanie ma też zastosowanie w fazie
projektowania oraz programowania. W ten sposób pogłębia wgląd członków
zespołu w zagadnienia dziedziny, pozwalając im zobaczyć je w sposób
bardziej przejrzysty, co prowadzi do dalszych usprawnień modelu. Modele
nie są nigdy doskonałe — w sposób ciągły ewoluują. Muszą być praktyczne
i przydatne w zrozumieniu dziedziny, a dodatkowo wystarczająco rygory-
styczne, aby ich zastosowanie było proste do implementacji i zrozumienia.
PRZETWARZANIE WIEDZY 43
699b15244ae879c728b313782703a4d7
6
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.
699b15244ae879c728b313782703a4d7
6
całkowicie porzucona. Podobnie stało się z tymi częściami modelu, które
przechowywały zrozumienie propagacji sygnału przez elementy składowe,
a także zliczały skoki. Istota aplikacji okazała się leżeć gdzieś indziej, a model
został zmodyfikowany, aby uwypuklić tamte aspekty dziedziny. Eksperci
dziedzinowi nauczyli się bardzo wiele i sprecyzowali podstawowe zada-
nie aplikacji (więcej informacji na ten temat zawiera rozdział 15.).
Pomimo tych wszystkich wątpliwości wykonana początkowo praca
była zasadnicza. Kluczowe elementy modelu zostały zachowane, a co więcej,
dzięki tej pracy został zapoczątkowany proces przetwarzania informacji,
który zwiększył efektywność wszystkich późniejszych działań. Do ele-
mentów zasadniczych należała wiedza zdobyta przez członków zespołu,
programistów oraz ekspertów dziedzinowych, początkowa wersja współ-
dzielonego języka, a także utworzenie zasad komunikacji używanej w czasie
implementacji. Podróż musiała się gdzieś rozpocząć.
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.
699b15244ae879c728b313782703a4d7
6
Rysunek 1.8.
Rysunek 1.9.
3
ang. overbooking — przyp. tłum.
699b15244ae879c728b313782703a4d7
6
W tym momencie ważna reguła biznesowa została ukryta w metodzie
aplikacji i jest przez nią chroniona. W dalszej części książki, w rozdziale 4.,
przyjrzymy się pryncypium architektury warstwowej, które doprowadzi nas do
umieszczenia reguły nadkompletu w samym obiekcie dziedziny. Teraz jed-
nak skoncentrujmy się na tym, w jaki sposób wiedzę o tej regule uczynić
bardziej oczywistą oraz dostępną dla każdej osoby w projekcie. Takie rozu-
mowanie doprowadzi nas do podobnego rozwiązania.
1. Jak napisaliśmy wcześniej, jest bardzo mało prawdopodobne, aby
utworzony kod został przeanalizowany przez jakiegokolwiek eksperta
biznesowego, który mógłby sprawdzić tę regułę, nawet jeżeli uzy-
skałby pomoc programisty.
2. Osobie technicznej, niemającej wiedzy biznesowej, trudno byłoby
połączyć tekst wymagania z kodem.
Gdyby reguła była bardziej złożona, jej poprawność byłaby zagrożona.
Oczywiście, możemy zmienić projekt w taki sposób, aby lepiej
uchwycić tę wiedzę. Dopuszczanie nadkompletu jest pewną polityką4. Ta
natomiast jest inną nazwą wzorca znanego pod nazwą STRATEGII. Jej
zastosowanie jest zazwyczaj spowodowane koniecznością zastąpienia róż-
nych reguł, co — o ile nam wiadomo — nie jest konieczne w tym mo-
mencie. Jednak pojęcie, które chcemy uchwycić w tym miejscu, wpisuje
się w znaczenie polityki, co samo w sobie stanowi równoważną motywację
w projektowaniu sterowanym dziedziną (patrz rozdział 12., „Powiązanie
wzorców projektowych z modelem”).
Rysunek 1.10.
4
ang. policy — przyp. tłum.
699b15244ae879c728b313782703a4d7
6
Nowa klasa definiująca politykę nadkompletu (ang. Overbooking
Policy) zawiera poniższą metodę:
public boolean isAllowed(Cargo cargo, Voyage voyage) {
return (cargo.size() + voyage.bookedCargoSize()) <=
(voyage.capacity() * 1.1);
}
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.
699b15244ae879c728b313782703a4d7
6
Ponieważ dostawa kontenerów rozpoczyna się od etapu rezerwacji
przestrzeni ładunkowej, w tym projekcie stworzyliśmy model, który umoż-
liwił nam opis ładunku, jego planu podróży itp. Wszystko to było niezbędne
i przydatne, jednak eksperci dziedzinowi wciąż byli niezadowoleni, co było
spowodowane ich sposobem spojrzenia na swe zagadnienia biznesowe,
których brakowało w projekcie.
Ostatecznie dopiero po miesiącach przetwarzania wiedzy zdaliśmy
sobie sprawę, że obsługa ładunku — fizyczne operacje ładunku oraz
rozładunku, jego przenoszenie z miejsca na miejsce — w dużej mierze
wykonywana była przez inne firmy lub przez ludzi zajmujących się w firmie
zadaniami operacyjnymi. W ocenie naszych ekspertów od logistyki cały
ten proces był tylko ciągiem zdarzeń polegających na przekazywaniu sobie
odpowiedzialności pomiędzy poszczególnymi stronami. Proces ten zarzą-
dzał wykonaniem zdarzeń zarówno od strony prawnej, jak i fizycznej, od
armatora statku do pewnego lokalnego dostawcy, od niego do innego, i tak
dalej, aż do dostarczenia ładunku do odbiorcy. Bardzo często ładunek
pozostawał w magazynie, podczas gdy w tle podejmowane były istotne
czynności. Innym razem ładunek przechodził przez wiele złożonych fizycz-
nych etapów, które z punktu widzenia decyzji biznesowych firmy były
zupełnie nieistotne. Zamiast logistyki podróży na pierwszy plan wysunęły
się prawne dokumenty, w rodzaju rachunków za rozładunek, lub też pro-
cesy prowadzące do odmrożenia płatności.
Ten pogłębiony obraz biznesu logistycznego nie prowadził do usu-
nięcia obiektu reprezentującego plan podróży, jednak sam model został
znacznie zmieniony. Nasz obraz logistyki morskiej zmienił się z prostego
przewożenia kontenerów z miejsca na miejsce w przekazywanie odpowie-
dzialności za ładunek od jednego podmiotu do innego. Funkcjonalności
wspierające ten proces nie były już dłużej dziwnie dołączone do operacji
załadunku. Wciąż jednak były wspierane przez model, który wynurzył
się z głębszego zrozumienia ważnych zależności pomiędzy operacjami
logistycznymi a przekazywaniem odpowiedzialności za ich wykonanie.
Proces przetwarzania wiedzy jest odkrywaniem. I nikt nie wie, czym
się on zakończy.
MODELE DOGŁĘBNE 49
699b15244ae879c728b313782703a4d7
6
50 ROZDZIAŁ 1. PRZETWARZANIE WIEDZY
699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 2.
Komunikacja
i użycie języka
1
ang. Unified Modeling Language, czyli zunifikowany język modelowania —
przyp. tłum.
51
699b15244ae879c728b313782703a4d7
6
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”
699b15244ae879c728b313782703a4d7
6
nie będą do siebie pasować (patrz rozdział 14.). Wysiłek związany z tłuma-
czeniem uniemożliwia połączenie ze sobą pomysłów oraz wiedzy, które
mogłoby prowadzić do głębszych przemyśleń i udoskonalenia modelu.
W chwili zaistnienia podziałów językowych projekt zaczyna
napotykać poważne problemy. Eksperci dziedzinowi używają wła-
snego żargonu, a członkowie zespołu technicznego wykształcają
własny język na potrzeby omawiania dziedziny pod kątem pro-
jektowym.
Terminologia używana w codziennych dyskusjach staje się
rozłączna z tą osadzoną w kodzie (będącym najważniejszym pro-
duktem projektu informatycznego). Co więcej, nawet ta sama
osoba może używać różnego języka w mowie i piśmie, co sprawia,
że najbardziej kluczowe wyrażenia dziedzinowe bardzo często
pozostają jedynie w ulotnej, niematerialnej postaci, która nigdy
nie znajduje odzwierciedlenia w kodzie lub nawet w jego doku-
mentacji.
Konieczność tłumaczenia osłabia komunikację, a także cały
proces przetwarzania wiedzy.
Żaden z tych nowo powstałych dialektów nie może stanowić
wspólnego języka, ponieważ żaden z nich nie służy wszystkim
członkom zespołu.
Dodatkowy koszt związany z tłumaczeniem oraz ryzyko nieporo-
zumienia są zbyt duże. Dlatego projekt wymaga wspólnego języka, który
będzie bardziej wiarygodny od najmniejszego wspólnego mianownika.
Dzięki świadomym wysiłkom zespołu model dziedziny może stać się pod-
stawą wspólnego języka, łącząc komunikację zespołu z implementacją pro-
gramu, czyli języka wszechobecnego w pracy zespołu.
Do słownictwa tworzącego JĘZYK WSZECHOBECNY (ang. UBI-
QUITOUS LANGUAGE) należeć będą nazwy klas i znaczących operacji.
JĘZYK zawierać będzie pojęcia służące do omawiania reguł wyróżnionych
w modelu. Będzie on również uzupełniony pojęciami ogólnych pryncy-
piów narzuconych temu modelowi (na przykład MAP KONTEKSTU lub
struktur dużej skali, które zostaną omówione w rozdziałach 14. oraz 16.).
Ostatecznie język zostanie jeszcze wzbogacony nazwami wzorców używa-
nymi powszechnie przez zespół w modelu dziedziny.
Zależności istniejące w modelu stają się zasadami połączeń we wszyst-
kich językach. Znaczenie słów oraz wyrażeń naśladować będzie seman-
tykę modelu.
Język oparty na modelu powinien być stosowany wśród programi-
stów nie tylko w celu opisywania artefaktów systemu, lecz również jego
zadań oraz funkcjonalności. Ten sam model powinien wspierać język
JĘZYK WSZECHOBECNY 53
699b15244ae879c728b313782703a4d7
6
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.
699b15244ae879c728b313782703a4d7
6
Wszystkie problemy rozwiązuj, używając alternatywnych
wyrażeń opisujących alternatywne modele. Następnie refaktoruj
kod, zmieniaj nazwy klas, metod oraz modułów, aby wpasować
je do nowego modelu. Usuwaj niejasności pojęć w konwersacji
w taki sam sposób, w jaki uwspólniamy znaczenie zwykłych słów.
Zdaj sobie sprawę, że zmiana JĘZYKA WSZECHOBECNEGO
stanowi zmianę modelu.
Eksperci dziedzinowi powinni sprzeciwiać się pojęciom lub
strukturom brzmiącym dziwnie lub nieadekwatnie do opisu zna-
czenia dziedziny. Natomiast programiści powinni doszukiwać się
niejasności i niespójności, które mogłyby wypaczyć projekt.
W przypadku JĘZYKA WSZECHOBECNEGO model nie jest jedy-
nie artefaktem projektowym. Staje się on wspólnym mianownikiem dla
wszystkiego, co programiści i eksperci dziedzinowi robią wspólnie. JĘZYK
przekazuje wiedzę w sposób dynamiczny. Dyskusje prowadzone w JĘZYKU
nadają znaczenie diagramom oraz programowi.
***
Idea JĘZYKA WSZECHOBECNEGO zakłada istnienie tylko jednego
modelu. W rozdziale 14., „Utrzymywanie integralności modelu”, zajmiemy
się współistnieniem wielu różnych modeli (oraz JĘZYKÓW), a także
zapobieganiem rozłamowi w modelu.
JĘZYK WSZECHOBECNY jest podstawowym nośnikiem tych
aspektów projektu, które nie pojawiają się w kodzie — struktur dużej
skali, porządkujących cały system (patrz rozdział 16.), KONTEKSTÓW
ZWIĄZANYCH, określających związki pomiędzy różnymi systemami oraz
modelami (patrz rozdział 14.), a także innych wzorców stosowanych do
modelu oraz projektu.
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
699b15244ae879c728b313782703a4d7
6
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.
699b15244ae879c728b313782703a4d7
6
Programista: W porządku. W takim razie w przypadku wprowadzenia
po raz pierwszy punktu odpraw celnych będziemy musieli sprawdzić,
czy w tabeli nie znajdują się stare wartości, i porównać je z nowymi.
Wtedy będziemy wiedzieć, czy musimy je usunąć.
Użytkownik: Nie musisz przejmować się miejscem pochodzenia oraz
przeznaczenia, ponieważ plan podróży zawsze się zmieni.
Programista: Dobrze. Nie będziemy się tym martwić.
Rysunek 2.2.
JĘZYK WSZECHOBECNY 57
699b15244ae879c728b313782703a4d7
6
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ą
699b15244ae879c728b313782703a4d7
6
usłyszysz opis funkcji podany w żargonie biznesowym lub też jego pospo-
litej odmianie. Usłyszysz rozmowę o technicznych artefaktach i kon-
kretnej funkcjonalności. Z pewnością dotrą do Ciebie określenia z modelu
dziedziny, a oczywiste rzeczowniki występujące w powszechnym języku
biznesowego żargonu zostaną zakodowane w postaci obiektów. Z tego
powodu zostaną one wspomniane. Czy jednak usłyszysz zdania, które
nawet w oderwaniu od tej dyskusji mogłyby zostać opisane ze względu
na związki oraz interakcje w obecnym modelu dziedziny?
Jednym z najlepszych sposobów na udoskonalenie modelu jest spraw-
dzenie go za pomocą mowy, poprzez wypróbowanie na głos różnych jego
odmian. Warunki brzegowe są zawsze łatwe do wychwycenia.
„Gdybyśmy podali usłudze Routing Service miejsce pochodzenia,
miejsce docelowe oraz czas przybycia, mogłaby ona sprawdzić, ile
przystanków będzie po drodze i… no cóż, zachować je w bazie”.
(niejasny i techniczny)
„Miejsce pochodzenia, przeznaczenia itd. — wszystkie te parametry
wchodzą do usługi Routing Service i w zamian dostajemy obiekt
opisujący plan podróży Itinerary, który będzie mieć wszystko, czego
potrzebujemy”. (bardziej kompletny, lecz rozwlekły)
„Usługa Routing Service odnajduje plan Itinerary, który będzie spełniać
specyfikację Route Specification”. (zwięzły)
Wykorzystywanie zdolności językowych poprzez zabawę słowami
i zdaniami jest tak samo niezbędne w wysiłkach podczas modelowania,
jak wykorzystanie umiejętności przestrzennych w trakcie rysowania dia-
gramów. To samo dotyczy wykorzystania zdolności analitycznych podczas
metodycznej analizy i projektowania oraz tajemniczego „wyczucia” kodu.
Wszystkie te sposoby myślenia uzupełniają się nawzajem, a do stwo-
rzenia przydatnych modeli i projektów konieczne jest użycie każdego
z nich. Niestety, eksperymentowanie z językiem bywa bardzo często
pomijane (w proces odkrywania zagłębimy się w części III tej książki
i w kilku dialogach pokażemy, jak sposoby te wzajemnie od siebie zależą).
W rzeczywistości nasze mózgi wydają się wyspecjalizowane w radze-
niu sobie ze złożonością występującą w języku mówionym (dla takiego
laika jak ja dobrą nauką jest książka pt. The Language Instinct Stevena Pin-
kera2). Na przykład w chwili, gdy w celach handlowych spotkają się ludzie
o różnych doświadczeniach językowych, jeżeli nie mają oni wspólnego
języka, odkrywają nowy, zwany pidżynowym (ang. pidgin). Nie jest on tak
obszerny jak języki ojczyste rozmówców, lecz jest dopasowany do jednego
2
Książka nie została jeszcze wydana w Polsce — przyp. tłum.
MODELOWANIE NA GŁOS 59
699b15244ae879c728b313782703a4d7
6
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.
699b15244ae879c728b313782703a4d7
6
To tylko kilka z wymówek, które słyszałem, aby stosować dwa języki dla
jednego zespołu. Zapomnij o nich.
Oczywiście, istnieją techniczne elementy projektu, które mogą nie
interesować ekspertów dziedzinowych. Jednak istota modelu powinna ich
raczej zainteresować. Zbyt abstrakcyjne? Skąd wiesz, że pojęcia dziedziny
nie dadzą się opisać? Czy znasz tę dziedzinę tak dogłębnie jak oni? Pewne
wymagania są niekiedy gromadzone od użytkowników niższego szczebla,
dla których odpowiedni może być podzbiór bardziej konkretnej termi-
nologii. Jednak zakłada się, że ekspert dziedzinowy powinien być w stanie
myśleć o swoim własnym obszarze w sposób bardziej dogłębny. Jeżeli
zaawansowani eksperci dziedzinowi nie rozumieją modelu, wtedy na pewno coś
jest z nim nie w porządku.
Na początku, gdy użytkownicy omawiają przyszłe, jeszcze niezamo-
delowane funkcjonalności systemu, wciąż nie mają modelu, który mógłby
zostać wykorzystany. Jednak gdy tylko zaczną pracować nad tymi nowymi
pomysłami z programistami, rozpocznie się proces poszukiwania współ-
dzielonego modelu. Na początku może on być dziwny i niekompletny,
jednak będzie stopniowo ulepszany. Kiedy utworzony zostanie nowy język,
eksperci dziedzinowi muszą włożyć odrobinę wysiłku, aby go zaadapto-
wać, a także dopasować stare, wciąż istotne dokumenty.
W sytuacji, gdy eksperci dziedzinowi w rozmowach pomiędzy sobą
lub z programistami używać będą JĘZYKA, szybko odnajdą obszary
modelu, w których jest on niedostosowany do ich potrzeb lub wydaje się
zupełnie zły. Z pomocą programistów odnajdą oni również obszary,
w których precyzyjny język oparty na modelu odsłoni sprzeczności oraz
niejasności istniejące w tym modelu.
Programiści i eksperci dziedzinowi mogą w sposób nieformalny prze-
testować prawidłowość modelu, krok po kroku przechodząc przez wszyst-
kie scenariusze i używając zawartych w tym modelu obiektów. Prawie
każda rozmowa stanowi dla nich możliwość wzajemnej zabawy modelem
i pogłębienia w jej trakcie wzajemnego zrozumienia i dopracowania pojęć.
Eksperci dziedzinowi mogą użyć języka modelu podczas tworzenia
przypadków użycia lub wykorzystać go w jeszcze większym stopniu
w trakcie definiowania testów akceptacyjnych.
Pomysł wykorzystania języka modelu do zbierania wymagań budzi
niekiedy sprzeciw. Czy w gruncie rzeczy wymagania nie powinny być
niezależne od projektu, który będzie je wypełniać? W tym stwierdzeniu
pominięty został fakt, że wszystkie języki oparte są na tym samym modelu.
Znaczenie słów bywa zwodnicze. Model dziedziny wywodzi się zazwy-
czaj z własnego żargonu ekspertów dziedzinowych, lecz później został
on „wyczyszczony”, aby zawierać wyraźniejsze, bardziej znaczące definicje.
699b15244ae879c728b313782703a4d7
6
Oczywiście, eksperci dziedzinowi powinni sprzeciwiać się definicjom
posiadającym znaczenie inne od akceptowanego w środowisku. W pro-
cesie prowadzonym przy użyciu metodyk zwinnych wymagania rozwijane
są w trakcie trwania projektu, ponieważ rzadko kiedy od początku istnieje
wiedza określająca w sposób adekwatny całą aplikację. Częścią tego roz-
woju powinno być przeredagowanie wymagań przy użyciu JĘZYKA
WSZECHOBECNEGO.
Mnogość języków może być w niektórych przypadkach konieczna,
jednak pomiędzy ekspertami dziedzinowymi a programistami nie powinny
nigdy istnieć różnice językowe. (Współistnieniem wielu modeli w tym
samym projekcie zajmiemy się w rozdziale 14., „Utrzymywanie inte-
gralności modelu”).
Oczywiście, programiści będą używać technicznej terminologii, nie-
zrozumiałej dla ekspertów dziedzinowych. Ci pierwsi mają rozbudowany
żargon, którego będą potrzebować w dyskusji na temat technicznych
aspektów systemu. Z pewnością również użytkownicy będą dysponować
specjalistycznym żargonem, wychodzącym poza wąski zakres aplikacji
i zrozumienie programistów. To wszystko będą jednak rozszerzenia języka.
Te wszystkie jego dialekty nie powinny zawierać alternatywnych wersji
Rysunek 2.3.
słów dla tej samej dziedziny opisującej różne modele.
JĘZYK
WSZECHOBECNY
jest rozwijany
na przecięciu
żargonów
699b15244ae879c728b313782703a4d7
6
Dokumenty i diagramy
Gdy uczestniczę w spotkaniu omawiającym projekt oprogramowania,
prawie nigdy nie mogę obejść się bez rysowania na tablicy lub w szki-
cowniku. Większą część moich rysunków stanowią diagramy UML,
w szczególności diagramy klas oraz interakcji obiektów.
Niektóre osoby są urodzonymi wzrokowcami, a diagramy pomagają
im przyswoić pewien rodzaj informacji. Diagramy UML są szczególnie
przydatne w przekazywaniu związków pomiędzy obiektami i sprawdzają
się też w prezentacji interakcji zachodzących pomiędzy nimi. Nie mogą
one jednak przekazać pojęciowych definicji tych obiektów. W trakcie
spotkania znaczenie obiektów opisuję na głos, rysując diagramy, lub też
wychodzi to w trakcie rozmowy z innymi uczestnikami.
Proste, nieformalne diagramy UML mogą dobrze zdefiniować pod-
łoże dyskusji. Gdy w centralnym miejscu narysujemy od trzech do pię-
ciu istotnych dla zagadnienia obiektów, przykuje to uwagę uczestników.
Będą oni mogli dojrzeć ten sam widok relacji pomiędzy obiektami i, co
istotne, także ich nazwy. Dzięki tej pomocy dyskusja w języku naturalnym
może być bardziej efektywna. Diagram może być zmieniany w trakcie,
gdy ludzie będą próbować różnych eksperymentów myślowych, a szkic
może podążać za płynnością wypowiadanych zdań, stanowiących istotę
dyskusji. Nic dziwnego, że nazwa UML pochodzi od angielskiego określe-
nia „ujednolicony język modelowania” (ang. Unified Modeling Language).
Kłopoty pojawiają się, gdy ludzie czują się w obowiązku zapisać cały
model lub projekt przy użyciu UML. Wiele diagramów modeli obiek-
towych jest zbyt złożonych, a jednocześnie pozostawia zbyt wiele rzeczy
z boku. Są one tak pełne, ponieważ wiele osób czuje się w obowiązku
umieszczać w narzędziu do modelowania wszystkie obiekty, które chcą
zaprogramować. W tym gąszczu drzew nikt nie będzie w stanie dojrzeć lasu.
Biorąc pod uwagę całą tę złożoność, atrybuty oraz relacje stanowią
jedynie połowę historii w modelu obiektowym. Zachowanie obiektów oraz
nałożone na nie ograniczenia nie są tak proste do zilustrowania. Dia-
gramy interakcji obiektów mogą wprawdzie zobrazować pewne problema-
tyczne miejsca w projekcie, jednak wciąż wiele interakcji nie może być
w ten sposób uwidocznionych. TO po prostu wymagałoby zbyt wiele
pracy, zarówno przy tworzeniu diagramów, jak i ich odczytywaniu. Dia-
gram interakcji może jedynie zasugerować cel leżący u podstaw modelu.
Aby jednak dołączyć do niego ograniczenia i asercje, UML polega na
tekście umieszczonym w małych nawiasach i w ten sposób dodanym do
diagramu.
DOKUMENTY I DIAGRAMY 63
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
mierze reszty książki). Dodatkowe pomocnicze diagramy oraz dokumenty
mogą jedynie skupić uwagę obserwatorów na głównych elementach
modelu. Rozmowa prowadzona w języku naturalnym może wyjaśnić
niuanse znaczeniowe. Z tego właśnie powodu lubię wywracać rzeczy na
drugą stronę, odchodząc od tradycyjnego sposobu ich opisu na diagramie
UML. Zamiast tworzyć diagram z opisami tekstowymi, zazwyczaj tworzę
dokument tekstowy zilustrowany wybranymi i uproszczonymi diagramami.
Należy zawsze pamiętać, że model nie jest diagramem. Przeznaczeniem
tego ostatniego jest pomoc w zakomunikowaniu oraz wyjaśnieniu modelu.
Kod programu może za to służyć w charakterze repozytorium zawierają-
cego szczegółowe informacje o projekcie. Na przykład poprawnie napisany
kod w języku Java może być tak samo pełen wyrazu, jak diagram UML.
Uważnie wybrane i utworzone diagramy mogą skoncentrować uwagę
oraz pomóc w omawianiu modelu, pod warunkiem że nie są zagmatwane
wskutek chęci całkowitego odwzorowania modelu lub projektu.
DOKUMENTY I DIAGRAMY 65
699b15244ae879c728b313782703a4d7
6
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.
699b15244ae879c728b313782703a4d7
6
Najważniejszą wartością dokumentu projektowego jest wyjaśnienie
pojęć modelu, pomoc w przebrnięciu przez szczegóły kodu oraz być
może rzucenie światła na zamierzony styl wykorzystania modelu. W zależ-
ności od filozofii wyznawanej przez zespół, cały dokument projektowy
mógłby być tak prosty jak zestaw szkiców na ścianie lub też mógłby być
bardzo znaczący.
Dokument musi uczestniczyć w działania projektowych. Najprostszym spo-
sobem na ocenę tego jest obserwowanie współistnienia dokumentu z JĘZY-
KIEM WSZECHOBECNYM. Czy dokument został napisany przy użyciu
języka, jakim ludzie posługują się w projekcie (w danej chwili)? Czy też
jest napisany w języku osadzonym w kodzie programu?
Przysłuchuj się JĘZYKOWI WSZECHOBECNEMU i jego zmia-
nom. Jeżeli pojęcia wyjaśnione w dokumencie projektowym nie będą poja-
wiać się w rozmowach lub kodzie, wtedy sam dokument nie spełnia swojej
roli. Może jest on zbyt duży lub skomplikowany? Może nie jest skon-
centrowany na wystarczająco ważnym zagadnieniu? Z jakiegoś powodu
ludzie albo go nie czytają, albo nie uznają za ważny. Jeśli dokument nie
ma wpływu na JĘZYK WSZECHOBECNY, wtedy coś jest nie tak, jak
powinno być.
Z drugiej strony, możesz słyszeć o tym, że JĘZYK WSZECH-
OBECNY zmienia się w sposób naturalny, podczas gdy dokument jest
nieuaktualniany. Najwidoczniej z jakiejś przyczyny dokument ten nie
wydaje się ludziom odpowiedni lub na tyle ważny, aby go uaktualnić.
Równie dobrze mógłby zostać bezpiecznie przeniesiony do archiwum,
jednak jeżeli pozostawi się go w aktywnych dokumentach projektowych,
mógłby potencjalnie wprowadzić zamieszanie i w ten sposób narazić pro-
jekt na straty. A jeżeli dokument nie odgrywa istotnej roli, wtedy utrzy-
mywanie go w aktualnym stanie marnuje moce projektowe.
JĘZYK WSZECHOBECNY pozwala na utrzymanie innych doku-
mentów, takich jak specyfikacja wymagań, w stanie bardziej spójnym i mniej
niejednoznacznym. Gdy model dziedziny zacznie odzwierciedlać najbar-
dziej istotną wiedzę biznesową, wymagania aplikacji staną się scenariuszami
w modelu, a JĘZYK WSZECHOBECNY będzie mógł zostać użyty do
opisu takiego scenariusza w sposób bezpośrednio powiązany z PROJEK-
TOWANIEM STEROWANYM MODELEM (patrz rozdział 3.). W rezul-
tacie specyfikacje mogą być napisane w prostszy sposób, ponieważ nie
będą musiały przekazywać wiedzy biznesowej nieujętej w modelu.
Utrzymując dokumenty w minimalnej postaci i skupiając się na
traktowaniu ich jako uzupełnienia kodu oraz rozmów, można sprawić,
że będą one wciąż połączone z projektem. Pozwólmy rozwijającemu się
JĘZYKOWI WSZECHOBECNEMU wyznaczać, które dokumenty są
aktywne i powiązane z działaniami projektowymi.
DOKUMENTY I DIAGRAMY 67
699b15244ae879c728b313782703a4d7
6
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.
699b15244ae879c728b313782703a4d7
6
Modele mogą być także wartościowe, stanowiąc pomoc edukacyjną
w nauce dziedziny. Model sterujący projektem jest jednym z widoków
dziedziny, jednak może służyć pomocą w poznaniu jej innych widoków,
jako narzędzie przekazujące ogólną wiedzę o tej dziedzinie. W tym celu
można używać obrazów lub słów opisujących inne modele, niezwiązane
z danym projektem oprogramowania.
Jednym ze szczególnych powodów, dla których inne modele są rów-
nież przydatne, jest zakres dziedziny. Aby spełniać swoją rolę, model tech-
niczny sterujący procesem tworzenia oprogramowania musi być ograni-
czony do niezbędnego minimum.
Model objaśniający natomiast może obejmować te aspekty dziedziny,
które udostępniają dodatkowy kontekst, wyjaśniający szerszy zakres modelu.
Modele objaśniające dają wolność tworzenia bardziej opisowych stylów
zdefiniowanych dla określonego tematu. Bardzo często wizualne metafory
używane przez ekspertów dziedzinowych w swoim środowisku przekazują
znacznie bardziej klarowne wyjaśnienia, mogące zwiększać wiedzę progra-
mistów oraz harmonizować pracę ekspertów. Modele objaśniające ukazują
dziedzinę w sposób odmienny, a wiele różnych spojrzeń pomaga w pro-
cesie nauki.
Oczywiście, nie jest konieczne, aby tego rodzaju modele były
modelami obiektowymi, a wręcz najlepiej, jeżeli takie nie są. Właściwie
w modelach tych przydaje się zupełne unikanie języka UML, aby zapo-
biec fałszywej interpretacji, że są one związane z projektem oprogramo-
wania. Nawet jeżeli modele objaśniające nawiązują do modelu sterującego
projektem, to jednak podobieństwa te bardzo rzadko są bliźniacze. W celu
uniknięcia pomyłek wszyscy muszą być świadomi istniejących pomiędzy
nimi różnic.
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
699b15244ae879c728b313782703a4d7
6
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.
699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 3.
Związanie modelu
z implementacją
71
699b15244ae879c728b313782703a4d7
6
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.
699b15244ae879c728b313782703a4d7
6
Projektowanie sterowane modelem
Ś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.
699b15244ae879c728b313782703a4d7
6
być tylko luźno związany z modelem analitycznym. Twórcy modelu anali-
tycznego nie zawracają sobie głowy sprawami projektowymi, dlatego też
prawdopodobnie będzie on mało przydatny do potrzeb projektu.
W trakcie tego procesu analitycznego zachodzi szczątkowe przetwa-
rzanie wiedzy, lecz większość jego efektów jest tracona w momencie roz-
poczęcia kodowania, gdy programiści muszą wprowadzać do projektu
pewne abstrakcje. Nie ma żadnej gwarancji, czy przemyślenia zebrane
przez analityków i umieszczone w modelu zostaną utrzymane lub ponow-
nie odkryte. W tym momencie utrzymywanie jakiegokolwiek odwzoro-
wania pomiędzy projektem i luźno związanym modelem nie będzie efek-
tywne pod względem kosztów.
Czysto teoretyczny model analityczny może nawet nie spełnić swo-
jego podstawowego celu, jakim jest zrozumienie dziedziny, ponieważ
istotne odkrycia powstają zwykle w trakcie projektowania/implementacji.
Zawsze pojawiają się bardzo konkretne i nieprzewidziane problemy.
Początkowy model może uwikłać się w nieistotne zagadnienia, przeoczyw-
szy inne, o wiele ważniejsze. Niektóre z tych zagadnień mogą zostać
przedstawione w sposób zupełnie nieprzydatny dla aplikacji. W rezultacie
ten czysto teoretyczny model analityczny jest szybko porzucany zaraz po
rozpoczęciu kodowania i większość pracy trzeba wykonać ponownie. Jeżeli
programiści postrzegają analizę jako osobny proces, modelowanie będzie
zachodzić w mniej uporządkowany sposób. Jeżeli z kolei menedżerowie
również postrzegają analizę w ten sposób, wtedy zespół programistów może
nie otrzymać odpowiedniego dostępu do ekspertów dziedzinowych.
Niezależnie od przyczyny, program, którego projektowi brakuje pod-
staw pojęciowych, w najlepszym razie wykonuje przydatne zadania bez
ich wyjaśniania.
Jeżeli projekt lub jego kluczowe części nie odwzorowują
modelu dziedziny, wtedy model ten nie ma żadnej wartości, a cała
poprawność programu staje pod znakiem zapytania. Jednocześnie
złożone mapowania pomiędzy funkcjami modelu i projektu są
trudne do zrozumienia i w praktyce utrzymanie zmian w pro-
jekcie staje się niemożliwe. Pomiędzy analizą a projektem tworzy
się śmiertelna przepaść, uniemożliwiająca im wymianę między
sobą zebranych przemyśleń.
Analiza musi uchwycić podstawę pojęcia dziedziny w sposób zro-
zumiały i wyrazisty. Projekt musi określić zbiór elementów możliwych
do utworzenia przy użyciu narzędzi programistycznych stosowanych
w projekcie, które następnie będą wydajnie działać w docelowym śro-
dowisku uruchomieniowym i w sposób poprawny rozwiązywać problemy
postawione przed aplikacją.
699b15244ae879c728b313782703a4d7
6
PROJEKTOWANIE STEROWANE MODELEM usuwa tę rozłącz-
ność modelu analitycznego i projektu, a tym samym umożliwia zdefiniowa-
nie pojedynczego modelu funkcjonującego w obu obszarach. Odsuwając
na bok całkowicie techniczne problemy, każdy obiekt w projekcie będzie
mieć znaczenie opisane w modelu. Wymagać to będzie większej skrupulat-
ności w wyborze modelu, ponieważ musi on spełniać dwa całkiem różne
zadania.
Zawsze istnieje wiele sposobów na stworzenie abstrakcyjnego opisu
dziedziny, jak również wiele projektów, które mogą rozwiązać zadanie
stojące przed aplikacją. Dlatego praktyczne jest powiązanie modelu z pro-
jektem. Ten związek nie może być osiągnięty za cenę osłabienia analizy,
narażonej na fatalny kompromis, wymuszony wyborami technologicz-
nymi. Nie można też zaakceptować topornych projektów, które wprawdzie
odzwierciedlają założenia dziedziny, jednak odrzucają pryncypia projek-
towania oprogramowania. Takie podejście wymaga modelu, który będzie
sprawnie działać zarówno podczas analizy, jak i projektowania. Gdy model
nie wydaje się przydatny do implementacji, należy poszukać innego. Jeżeli
nie będzie on poprawnie wyrażać kluczowych pojęć dziedziny, również
trzeba będzie zrobić to samo. Proces modelowania oraz projektowania
stanie się jedną cykliczną pętlą.
Nadrzędny cel ścisłego powiązania modelu dziedziny z projektem
doda do już istniejących kryteriów dodatkowe — wyboru przydatnego
modelu spośród wielu istniejących we wszechświecie. Cały proces będzie
bardziej wyczerpujący umysłowo i zazwyczaj będzie wymagać wielu
powtórzeń i przeróbek kodu, jednak w końcowym rezultacie uczyni
model bardziej adekwatnym do rzeczywistości.
Dlatego:
W celu odwzorowania modelu dziedziny projektuj system
informatyczny stopniowo, tak aby odzwierciedlał go w bardzo
dosłowny sposób, czyniąc mapowanie oczywistym. Wielokrotnie
sprawdzaj model i modyfikuj go w taki sposób, aby w programie
był implementowany bardziej naturalnie, nawet wtedy, gdy starasz
się, aby lepiej odzwierciedlał założenia dziedziny. Domagaj się
wspólnego modelu dobrze spełniającego obie te funkcje, a także
wykorzystującego JĘZYK WSZECHOBECNY.
Inspiruj się terminologią użytą w modelu i wykorzystuj ją
w projekcie, razem z podstawowym przypisaniem odpowiedzial-
ności obiektów. Kod zacznie wyrażać model, dlatego dowolna
zmiana wprowadzona do niego będzie mogła stanowić zmianę
do modelu. Jej konsekwencje będą odczuwalne odpowiednio rów-
nież w innych obszarach aktywności projektowej.
699b15244ae879c728b313782703a4d7
6
Niewolnicze powiązanie implementacji z modelem wymaga
zazwyczaj użycia narzędzi oraz języków programistycznych wspie-
rających paradygmat modelowania, jak chociażby programowa-
nia obiektowego.
Niekiedy dla różnych podsystemów mogą istnieć różne modele (patrz
rozdział 14.). Jednak do danej części systemu powinien odnosić się tylko
jeden model, który powinien być używany w wszystkich fazach progra-
mowania, począwszy od kodowania do analizy wymagań.
Wspólny model zmniejsza ryzyko popełnienia pomyłki, ponieważ
projekt będzie bezpośrednim rezultatem szczegółowo przeanalizowanego
modelu. Projekt, a nawet sam kod będzie przekazywać te same informa-
cje, co model.
***
Łatwo o tym mówić, lecz trudniej stworzyć wspólny model, właściwie
wychwytujący problem i dostarczający praktyczne wskazówki projektowe.
Nie można tak po prostu wziąć dowolnego modelu i zmienić go w działa-
jący projekt. Aby model mógł przydać się w praktycznej implementacji,
musi on zostać starannie obrobiony. W tym celu trzeba zastosować techniki
projektowania oraz implementacji pozwalające na efektywne wyrażenie
modelu w kodzie programu (patrz część II). Osoby dokonujące przetwa-
rzania wiedzy będą analizować możliwości modelu, a następnie dostosują
go do praktycznych elementów projektu informatycznego. Projektowanie
stanie się pojedynczym cyklicznym procesem, począwszy od doskonalenia
modelu, przez tworzenie projektu, do programowania (patrz część III).
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-
699b15244ae879c728b313782703a4d7
6
Rysunek 3.1.
699b15244ae879c728b313782703a4d7
6
dziedzin niematematycznych w językach proceduralnych nie poddaje się
łatwo PROJEKTOWANIU STEROWANEMU MODELEM, ponieważ
nie jest łatwo je zaprojektować w postaci funkcji matematycznych lub kro-
ków w procedurze.
W tej książce zostanie wykorzystane podejście oparte na projekto-
waniu obiektowym, będącym paradygmatem, w którym powstaje obecnie
większość ambitnych projektów.
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
699b15244ae879c728b313782703a4d7
6
Dzięki jednoczesnemu połączeniu wielu ścieżek w szyny (za każdym
razem w 8, 16 lub 256) inżynierowie mogą ograniczyć zadanie do łatwiej
zarządzanego rozmiaru, zwiększając produktywność oraz zmniejszając liczbę
błędów. Niestety, problem stanowi brak pojęcia szyny w narzędziu do
projektowania. Reguły muszą być przypisywane do dziesiątek tysięcy ście-
żek po jednej za każdym razem.
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
...
699b15244ae879c728b313782703a4d7
6
4. Dodaj nazwę ścieżki z tekstem reguły do pliku z regułami.
5. Powtarzaj czynności od kroku 3., aż nie będzie już ścieżki odpowiada-
jącej nazwie szyny.
W ten sposób wejściowa reguła szyny w postaci:
Nazwa ścieżki Rodzaj reguły Parametry
------------- ------------- ---------
Xyz max_vias 3
699b15244ae879c728b313782703a4d7
6
Rysunek 3.3.
Diagram klas
zoptymalizowany
pod kątem
efektywnego
przypisywania
reguł projektu
Set assignedRules() {
return rules;
}
}
Set assignedRules() {
Set result = new HashSet();
result.addAll(super.assignedRules());
result.addAll(bus.assignedRules());
return result;
}
}
699b15244ae879c728b313782703a4d7
6
Usługa Rola
Import listy ścieżek Odczytuje plik z listą ścieżek i dla każdej z nich
(NetListImport) tworzy obiekt klasy Net
Eksport reguł ścieżek Otrzymując kolekcję ścieżek, zapisuje wszystkie
(NetRuleExport) dołączone reguły do pliku reguł
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));
}
699b15244ae879c728b313782703a4d7
6
również zostać wczytana z pliku. Użyta fasada ułatwia dostęp do każdego
z interfejsów, a jej implementację naśladuje odpowiedni test:
public void assignBusRule(String busName, String ruleType, double
parameter){
Bus bus = BusRepository.getByName(busName);
bus.assignRule(NetRule.create(ruleType, parameter));
}
1
Ten przykład podał mi Brian Maric.
699b15244ae879c728b313782703a4d7
6
Użytkownik tej przeglądarki może myśleć o Ulubionych jak o liście
nazw stron w sieci WWW, które są przechowywane pomiędzy sesjami.
Jednak obecna implementacja traktuje Ulubione jako plik zawierający adres
URL, którego nazwa jest dopiero umieszczana na tej liście. Stanowić to
może problem w przypadku, gdy tytuł strony WWW zawiera znaki nie-
dozwolone w nazwach plików systemu Windows. Przypuśćmy, że użyt-
kownik pragnie ręcznie dodać do Ulubionych stronę i jako jej nazwę poda
„Lenistwo: Sekret zadowolenia”. System wyświetli komunikat o błędzie,
mówiący: „Nazwa pliku nie może zawierać następujących znaków \ / : *
? " < > | ”. Jaka nazwa pliku? Z drugiej strony, jeżeli tytuł strony zawiera
już niedozwolone znaki, Internet Explorer bez pytania usunie je z nazwy.
Utrata danych w tym przypadku może być zbawienna, lecz na pewno nie
tego oczekuje użytkownik. W większości aplikacji zmiana danych bez
pytania jest całkowicie nieakceptowalna.
PROJEKTOWANIE STEROWANE MODELEM wzywa do uży-
wania tylko jednego modelu (w pojedynczym kontekście, co omówimy
w rozdziale 14.). Większość rad oraz przykładów dotyczy problemu ist-
nienia rozłącznych modeli, tj. analitycznego i projektowego, lecz w tym
przypadku problem wynika z innej pary modeli: modelu użytkownika
oraz modelu projektowego/implementacyjnego.
Oczywiście, w większości przypadków goły widok modelu dziedziny
nie byłby wygodny dla użytkownika. Jednak tworzenie w interfejsie użyt-
kownika złudzenia istnienia modelu innego niż model dziedziny będzie
powodować nieporozumienia (o ile to złudzenie nie będzie perfekcyjne).
Jeśli Ulubione są w rzeczywistości folderem skrótów systemowych, nale-
żałoby ten fakt zaprezentować użytkownikowi i zapobiec istnieniu wpro-
wadzającego w błąd alternatywnego modelu. Spowodowałoby to nie tylko
mniej nieporozumień, lecz również umożliwiłoby użytkownikowi sko-
rzystanie z całej swojej wiedzy na temat systemu plików w trakcie uży-
wania Ulubionych. Mógłby na przykład przeorganizować je przy użyciu
eksploratora plików, zamiast używać w tym celu dziwnych narzędzi wbu-
dowanych w przeglądarkę. Poinformowani użytkownicy byliby w stanie
wykorzystać możliwość przechowywania skrótów do stron internetowych
w dowolnym miejscu systemu plików. Tylko poprzez usunięcie tego
dodatkowego mylącego modelu możliwości aplikacji wzrosłyby znacznie,
a sama aplikacja byłaby bardziej przejrzysta. Dlaczego zmuszać użytkow-
nika do nauki nowego modelu, jeśli programiści uznawali stary za wystar-
czająco dobry?
W przeciwnym razie można byłoby zachować Ulubione w alterna-
tywny sposób, powiedzmy w pliku danych, tak aby mogły rządzić się
własnymi regułami, które prawdopodobnie byłyby zgodne z regułami
699b15244ae879c728b313782703a4d7
6
nazewnictwa stron internetowych. Taka zmiana wprowadziłaby ponownie
jeden model, mówiący użytkownikowi, że wszystko, co wie o nazewnictwie
stron internetowych, może być również zastosowane do Ulubionych.
Gdy projekt oparty jest na modelu odzwierciedlającym podstawowe
interesy użytkowników oraz ekspertów dziedzinowych, szkielet projektu
może zostać ujawniony użytkownikom w większym stopniu niż w innych
podejściach projektowych. Ujawnienie modelu umożliwia użytkownikom
większy dostęp do ukrytego potencjału aplikacji i w rezultacie skutkuje jej
stałym i przewidywalnym działaniem.
699b15244ae879c728b313782703a4d7
6
Modelowanie praktyczne
Popularna metafora określająca tworzenie oprogramowania nazywa ten
proces manufakturą. Co się z tym wiąże? Bardzo doświadczeni inżyniero-
wie tworzą projekt, a mniej doświadczeni pracownicy składają produkt.
Z prostego powodu użycie tej metafory namieszało już w wielu projek-
tach — tworzenie oprogramowania jest przecież całkowicie związane
z projektowaniem. Wszystkie zespoły mają specjalizowane role dla swoich
członków, lecz nadmierne rozdzielanie odpowiedzialności za analizę, mode-
lowanie, projektowanie i programowanie stoi w sprzeczności z PROJEK-
TOWANIEM STEROWANYM MODELEM.
W jednym z projektów moim zadaniem była koordynacja działań
różnych zespołów pracujących nad aplikacją oraz pomoc w utworzeniu
modelu dziedziny, który sterowałby projektem. Jednak kadra zarządzająca
uważała, że osoby zajmujące się modelowaniem powinny poświęcić się
temu całkowicie, a programowanie byłoby marnowaniem ich umiejęt-
ności. W rezultacie zabroniono mi programowania lub pracy z progra-
mistami nad szczegółami modelu.
Przez pewien czas projekt wydawał się przebiegać bez zakłóceń.
Pracując z ekspertami dziedzinowymi oraz liderami różnych zespołów
programistycznych, byliśmy w stanie przetwarzać wiedzę i stworzyć ładny
model zasadniczy. Jednak z dwóch powodów ten model nigdy nie został
wdrożony.
Po pierwsze, pewne intencje modelu zostały utracone podczas jego
przekazywania. Całkowity efekt modelu może być bardzo czuły na zmiany
w szczegółach (opowiemy o tym w częściach II oraz III), które nie zawsze
wychodzą na jaw na diagramach UML lub w ogólnej dyskusji. Gdybym
mógł zakasać rękawy i pracować bezpośrednio z programistami, udostęp-
niając im przykładowe fragmenty kodu lub bezpośrednio ich wspierając,
cały zespół mógłby skorzystać z abstrakcji modelu i wykorzystywać ją dalej.
Drugi problem stanowił brak bezpośredniego sygnału zwrotnego
dotyczącego współpracy modelu z jego implementacją oraz technologią.
Na przykład pewne aspekty modelu okazały się bardzo nieefektywne na
naszej platformie technologicznej, jednak pełny obraz konsekwencji tego
faktu nie dotarł do mnie z powrotem przez długie miesiące. Problem
mogłyby rozwiązać relatywnie drobne poprawki, jednak później już nie
miało to żadnego znaczenia. Programiści byli już bardzo zaawansowani
w tworzeniu swojego programu, który okazał się dobrze działać — bez
modelu, ograniczonego zaledwie do struktury danych wszędzie tam, gdzie
jeszcze szczątkowo był używany. Programiści wylali dziecko z kąpielą,
699b15244ae879c728b313782703a4d7
6
ale jakie mieli inne wyjście? Nie mogli przecież dłużej ryzykować obcią-
żenia zaleceniami architekta oderwanego od rzeczywistości.
Początkowe okoliczności, w jakich znajdował się ten projekt, jak
żadne inne sprzyjały praktycznemu modelowaniu. Już wcześniej zdoby-
łem rozległe doświadczenie w praktycznym stosowaniu większości tech-
nologii używanych w projekcie. Co więcej, zanim zmieniła się moja rola,
w tym samym projekcie prowadziłem nawet jeden z mniejszych zespołów
programistycznych. Dlatego też byłem zaznajomiony z procesem wytwa-
rzania oprogramowania w projekcie oraz jego środowiskiem programi-
stycznym. Jednak nawet te wszystkie aspekty nie pomogły mi w efek-
tywnej pracy w sytuacji, gdy osoba tworząca model została oddzielona od
jego implementacji.
Jeżeli osoby tworzące kod nie czują odpowiedzialności za
model lub też nie rozumieją, jak go zastosować w aplikacji, wtedy
model ten nie może w żaden sposób wpłynąć na oprogramowa-
nie. Jeżeli programiści nie uświadomią sobie, że zmiana kodu
pociąga za sobą zmiany w modelu, wtedy każda przeróbka kodu
osłabi model, zamiast go wzmocnić. Jeżeli jednocześnie osoba two-
rząca model jest oddzielona od procesu jego implementacji, wtedy
nigdy nie zdobędzie wiedzy o jej ograniczeniach lub ją szybko
utraci. Podstawowe ograniczenie PROJEKTOWANIA STEROWA-
NEGO MODELEM, mówiące o tym, że model wspiera efektywną
implementację oraz tworzy abstrakcję podstawowej wiedzy dzie-
dzinowej, prawie przestanie istnieć, a powstałe modele będą nie-
praktyczne. Ostatecznie, jeżeli podział pracy nie będzie pozwalać
na współpracę umożliwiającą przekazywanie subtelności PRO-
JEKTOWANIA STEROWANEGO MODELEM, wtedy wiedza oraz
umiejętności doświadczonych projektantów nie zostaną przeka-
zane programistom.
Potrzeba wyodrębnienia z zespołu DOŚWIADCZONYCH OSÓB
TWORZĄCYCH MODEL nie oznacza wcale, że członkowie zespołu nie
mogą posiadać specjalistycznych ról. Każdy proces w metodykach zwin-
nych, włączając programowanie ekstremalne, definiuje role dla członków
zespołu, a inne nieformalne specjalizacje wydają się powstawać w sposób
naturalny. Problem wynika z oddzielenia dwóch połączonych ze sobą
w PROJEKTOWANIU STEROWANYM MODELEM zadań, tj. mode-
lowania oraz implementacji.
Efektywność ostatecznego projektu jest bardzo wrażliwa na jakość
i spójność szczegółów projektu oraz decyzji implementacyjnych. W PRO-
JEKTOWANIU STEROWANYM MODELEM fragment kodu stanowi
wyrażenie modelu — zmiana kodu zmienia również model. Czy im się to
MODELOWANIE PRAKTYCZNE 87
699b15244ae879c728b313782703a4d7
6
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.
699b15244ae879c728b313782703a4d7
6
II
Elementy
składowe
projektu
sterowanego
modelem
699b15244ae879c728b313782703a4d7
6
Aby w obliczu niesprzyjających okoliczności utrzymać implementację
w zgodzie z modelem, należy odwołać się do najlepszych praktyk mode-
lowania oraz projektowania. Niniejsza książka nie jest wprowadzeniem
do projektowania obiektowego ani też nie zawiera propozycji zasadni-
czych podstaw projektowania. Idea projektowania sterowanego dziedziną
najzwyczajniej przesuwa nacisk na pewne tradycyjne zagadnienia z dzie-
dziny projektowania.
Niektóre decyzje utrzymują model oraz implementację we wspól-
nym ścisłym związku, pozwalającym na wzmocnienie efektywności każ-
dego z tych elementów. Takie powiązanie wymaga zwrócenia uwagi na
detale poszczególnych elementów projektowych. Precyzyjne dopracowanie
drobiazgów w małej skali udostępnia programistom stabilną platformę,
na której mogą już stosować różne techniki modelowania, opisane w czę-
ściach III oraz IV.
Styl projektowania opisany w tej książce w dużej mierze odzwierciedla
zasadę „projektowania sterowanego odpowiedzialnością”1, zdefiniowaną
przez Rebekę Wirfs-Brock w roku 1990 i uaktualnioną w roku 20032.
Został on także bardzo mocno zainspirowany (szczególnie w części III)
przez idee „projektowania kontraktowego”, opisanego przez Meyera w roku
19883. W znacznej części jest on również spójny z ogólnym przesłaniem
innych szeroko rozpowszechnionych najlepszych praktyk projektowania
obiektowego, opisanych na przykład w książce Larmana z roku 19984.
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
699b15244ae879c728b313782703a4d7
6
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ół.
699b15244ae879c728b313782703a4d7
6
Mapa nawigacji po języku PROJEKTU STEROWANEGO MODELEM
92 CZĘŚĆ II
699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 4.
Wyizolowanie
dziedziny
93
699b15244ae879c728b313782703a4d7
6
Architektura warstwowa
699b15244ae879c728b313782703a4d7
6
umieszczona w kodzie interfejsów użytkownika oraz skryptach
bazodanowych. Dzieje się tak dlatego, iż w krótkiej perspektywie
jest to najprostszy sposób na utworzenie działającego kodu.
W sytuacji, gdy kod związany z dziedziną jest rozproszony
w tak dużej ilości innego kodu, niezmiernie trudno jest go dojrzeć
i zrozumieć. Pobieżne zmiany do interfejsu użytkownika mogą
w rzeczywistości zmienić logikę biznesową. A zmiana reguły biz-
nesowej może wymagać skrupulatnego śledzenia kodu obsługują-
cego interfejs użytkownika, bazę danych lub inne elementy pro-
gramu. Implementacja spójnych obiektów sterowanych modelem
staje się niepraktyczna, a testowanie automatyczne jest niewy-
godne. Z powodu wszystkich tych technologii i logiki zaangażo-
wanych w każdą czynność program musi albo być bardzo prosty,
albo też stanie się praktycznie niemożliwy do zrozumienia.
Tworzenie programów obsługujących bardzo złożone zadania wymaga
rozdzielenia zagadnień, umożliwiając tym samym samodzielne i wyłączne
skupienie się na różnych częściach projektu. W tym samym czasie złożone
interakcje w systemie muszą być przeprowadzane z myślą o utrzymaniu
tej rozłączności.
Istnieje wiele sposobów podziału systemu informatycznego, jednak
doświadczenie przemysłowe pozwoliło utworzyć i zdefiniować ARCHI-
TEKTURY WARSTWOWE, składające się w szczególności z kilku całkiem
standardowych warstw. Metafora podziału systemu na warstwy jest tak
powszechnie stosowana, że dla większości programistów wydaje się intu-
icyjna. W literaturze dostępnych jest wiele dobrych dyskusji na temat
podziału systemów na warstwy. Niektóre z nich są nawet ujęte w postaci
wzorców (jak w książce Buschmana1 z roku 1996 — strony 31 – 51).
Podstawowa zasada stanowi, że dowolny element w danej warstwie jest
zależny jedynie od innych elementów w tej samej warstwie lub w war-
stwach niższych. Komunikacja w górę musi przechodzić przez mechanizm
pośredni, który zostanie omówiony nieco później.
Podstawową wartością warstw jest fakt, iż każda z nich specjalizuje
się w konkretnym aspekcie programu komputerowego. Ta specjalizacja
umożliwia utworzenie bardziej spójnych projektów każdego z nich, a co
za tym idzie, projekty te są prostsze w interpretacji. Oczywiście, niezmier-
nie istotne jest wybieranie warstw, które będą zawierać najbardziej spójne
i ważne aspekty projektu. Z pomocą ponownie przychodzą doświadczenie
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
699b15244ae879c728b313782703a4d7
6
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:
699b15244ae879c728b313782703a4d7
6
Dlatego:
Podziel złożony program na warstwy. Utwórz projekt każ-
dej z nich, czyniąc ją zarazem spójną, jak i zależną jedynie od
warstw położonych niżej. Aby utrzymać luźne powiązanie z wyż-
szymi warstwami, stosuj standardowe wzorce architektoniczne.
Umieść kod związany z modelem dziedziny w pojedynczej war-
stwie i odizoluj go od kodu interfejsu użytkownika, aplikacji oraz
infrastruktury. Dzięki takiemu podejściu obiekty dziedziny, pozba-
wione konieczności obsługi wyświetlania, przechowywania, zarzą-
dzania zadaniami aplikacji itd., mogą skoncentrować się jedynie
na wyrażeniu modelu dziedziny. To umożliwia wzbogacenie
i uproszczenie modelu na tyle, aby mógł on wychwycić wiedzę
biznesową i wykorzystać ją w praktyce.
Oddzielenie warstwy dziedziny od warstw infrastruktury oraz inter-
fejsu użytkownika umożliwia utworzenie bardziej przejrzystego projektu
każdej z nich. Utrzymanie wyizolowanych warstw jest zdecydowanie mniej
kosztowne, ponieważ są one rozwijane we własnym tempie i odpowia-
dają na różne własne potrzeby. Podział na warstwy pomaga również we
wdrożeniu systemu rozproszonego, pozwalając na umieszczenie poszcze-
gólnych warstw na różnych serwerach oraz klientach w celu zmniejsze-
nia narzutu komunikacyjnego i zwiększenia wydajności (Fowler 1996).
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
699b15244ae879c728b313782703a4d7
6
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.
699b15244ae879c728b313782703a4d7
6
Powiązanie warstw
Jak dotąd nasza dyskusja skupiała się na rozdzieleniu warstw oraz na tym,
w jaki sposób partycjonowanie ulepsza projekt każdego aspektu programu,
w szczególności warstwy dziedziny. Jednak warstwy, oczywiście, muszą
być ze sobą połączone. Wykonanie takiego połączenia bez utraty korzyści
wynikających z ich rozdzielenia leży u podstaw wielu wzorców.
Warstwy mają być luźno związane, a zależności projektowe muszą
być tylko jednokierunkowe. Warstwy wyższe mogą w prosty sposób używać
elementów warstw niższych poprzez wykorzystanie ich interfejsów publicz-
nych, przechowując odwołania do nich (choćby tymczasowo) — ogólnie
rzecz biorąc, używając standardowych sposobów komunikacji. Jednakże
w przypadku, gdy obiekt warstwy niższej potrzebuje skomunikować się
w górę (nie mówimy o zwykłej odpowiedzi na zapytanie), musimy skorzy-
stać z innego mechanizmu, używając w tym celu wzorca architektonicz-
nego do łączenia warstw w rodzaju odwołań zwrotnych (callbacks) albo
OBSERWATORÓW (patrz Gamma 1995 r.).
Dziadkiem wszystkich wzorców używanych do łączenia interfejsu
użytkownika z warstwami aplikacji oraz dziedziny jest MODEL-WIDOK-
-KONTROLER (ang. model-view-controler — MVC). Wzorzec ten został
zapoczątkowany w okresie świetności Smalltalka w latach 70. ubiegłego
wieku i zainspirował wiele późniejszych architektur interfejsu użytkow-
nika. Wraz ze swoimi wieloma przydatnymi odmianami został on omó-
wiony przez Fowlera (2003 r.). Również Larman (1998 r.) sprawdzał ten
temat we WZORCU ROZDZIELENIA MODELU-WIDOKU, a opra-
cowany przez niego KOORDYNATOR APLIKACJI jest jednym z podejść
stosowanych do połączenia z warstwą aplikacji.
Oczywiście, istnieją inne style łączenia interfejsu użytkownika z apli-
kacją. Do naszych zastosowań wszystkie z nich będą poprawne, o ile będą
zachowywać izolację warstwy dziedziny, pozwalając projektować obiekty
dziedziny bez jednoczesnego przejmowania się interfejsem użytkownika,
który mógłby ich później używać.
Warstwa infrastruktury nie inicjuje zazwyczaj żadnych akcji w war-
stwie dziedziny. Istniejąc „poniżej” warstwy dziedziny, nie powinna ona
posiadać żadnej konkretnej wiedzy o dziedzinie, której służy. Rzeczywiście
tego rodzaju techniczne możliwości są bardzo często udostępniane w cha-
rakterze USŁUG. Jeżeli na przykład aplikacja chce wysłać wiadomość
e-mail, wtedy w warstwie infrastruktury może zostać zlokalizowany inter-
fejs służący do wysyłania wiadomości, zaś elementy warstwy aplikacji
mogłyby tylko zażądać jej wysłania. Tego rodzaju rozdzielenie daje dodat-
kową wielofunkcyjność. Interfejs do wysyłania wiadomości mógłby zostać
ARCHITEKTURA WARSTWOWA 99
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
do rozwiązania ważnych problemów. To właśnie zespół musi zaadapto-
wać szkielet, nawet jeżeli będzie to oznaczać pominięcie pewnych jego
funkcjonalności. Na przykład wczesne aplikacje J2EE bardzo często imple-
mentowały wszystkie obiekty dziedziny w postaci „ziaren encyjnych”
(ang. entity beans). Takie podejście wpływało negatywnie zarówno na
wydajność, jak i tempo programowania. Obecną praktyką jest raczej zasto-
sowanie szkieletu J2EE do większych obiektów oraz implementacja
większości logiki biznesowej przy użyciu prostych obiektów Javy. Wiele
ograniczeń szkieletu architektury może być zminimalizowanych poprzez
selektywne zastosowanie go do rozwiązania trudnych problemów, bez
poszukiwania cudownego rozwiązania na wszystkie bolączki. Rozsądne
stosowanie tylko wybranych, najbardziej wartościowych części rozwią-
zania zmniejsza związanie implementacji ze szkieletem architektury, co
daje większą elastyczność w późniejszych decyzjach projektowych. Biorąc
pod uwagę, jak bardzo skomplikowanych jest wiele z obecnie dostępnych
szkieletów aplikacji, podejście minimalistyczne w ich stosowaniu pomaga
utrzymać obiekty biznesowe w przejrzystej i wymownej postaci.
Szkielety architektury oraz inne narzędzia wspomagające będą wciąż
rozwijane. Nowsze z nich będą automatyzować i dostarczać wzorce dla
coraz to większej liczby aspektów technicznych aplikacji. Jeżeli tylko
zostaną one poprawnie zastosowane, programiści aplikacji będą mogli
w coraz większym stopniu koncentrować się na modelowaniu podsta-
wowych problemów biznesowych, co zwiększy produktywność zespołu
oraz jakość pracy. Wybierając takie podejście, musimy wystrzegać się nad-
miernego optymizmu w stosunku do gotowych rozwiązań technicznych
— bardzo rozwlekłe szkielety mogą również ograniczać programistów.
699b15244ae879c728b313782703a4d7
6
Sytuacja, w której logika dziedziny jest wymieszana z innym kodem
programu, nie jest zbyt praktyczna. Wyizolowanie dziedziny w procesie
jej implementacji jest warunkiem wstępnym dla projektu sterowanego
dziedziną.
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).
699b15244ae879c728b313782703a4d7
6
Narzut spowodowany przez zarządzanie infrastrukturą oraz
wszystkimi warstwami spowoduje wydłużenie prostych zadań.
Proste projekty mają zazwyczaj krótkie terminy i średnio skom-
plikowane oczekiwania. Projekt zostanie z pewnością przerwany,
na długo zanim zespół dokończy przypisane doń zadania, nie mając
szans na zaprezentowanie wspaniałych możliwości zastosowa-
nego podejścia.
Nawet jeżeli zespół będzie mieć więcej czasu, to bez pomocy
eksperta członkowie zespołu prawdopodobnie nie dadzą rady opa-
nować wymaganych technik. Jeżeli nawet na koniec pokonają te
wszystkie przeciwności, to i tak w końcu utworzą prosty system,
w którym nigdy nie będą wymagane bogate funkcjonalności.
Bardziej doświadczone zespoły nie będą musiały iść na takie kom-
promisy. Oczywiście, wieloletni programiści mogliby skrócić czas potrzebny
na naukę i zarządzanie warstwami. Projektowanie sterowane dziedziną
sprawdza się najlepiej w przypadku ambitnych projektów i będzie wyma-
gać dużych umiejętności programistycznych. Nie wszystkie projekty są
ambitne i nie wszystkie zespoły projektowe mogą opanować wymagane
umiejętności.
Dlatego jeżeli tylko pozwalają na to okoliczności:
Umieść całą logikę biznesową w interfejsie użytkownika.
Podziel aplikację na małe funkcje i zaimplementuj je jako
oddzielne moduły interfejsu, umieszczając w nich reguły bizne-
sowe. Wykorzystaj bazę danych w charakterze współdzielonego
repozytorium danych. Użyj najbardziej zautomatyzowanych
narzędzi do tworzenia interfejsu użytkownika oraz programowa-
nia wizualnego, jakie tylko są dostępne na rynku.
Herezja! Przecież dobra nowina mówi (i jest głoszona wszędzie,
również w tej książce), że dziedzina powinna być oddzielona od interfejsu
użytkownika. W rzeczywistości bez tego rozdzielenia trudno będzie zaim-
plementować jakąkolwiek metodę omawianą w tej książce, dlatego też
wzorzec INTELIGENTNEGO INTERFEJSU UŻYTKOWNIKA w kon-
tekście projektowania sterowanego dziedziną może być traktowany jako
„antywzorzec”. Jednak w niektórych sytuacjach skorzystanie z tego wzorca
jest uprawnione. Mówiąc szczerze, ma on pewne zalety i w niektórych
okolicznościach może sprawdzać się najlepiej — co częściowo tłumaczy,
dlaczego jest tak popularny. Omówienie go w tym miejscu pomoże zro-
zumieć, dlaczego musimy oddzielać warstwę aplikacji od dziedziny i, co
więcej, w jakich sytuacjach moglibyśmy nie chcieć tego robić.
699b15244ae879c728b313782703a4d7
6
Zalety:
Dostępna od razu duża wydajność dla prostych aplikacji.
Mniej doświadczeni programiści mogą używać tego wzorca
po pobieżnym przeszkoleniu.
Nawet braki w wymaganiach mogą zostać rozwiązane
po udostępnieniu prototypu użytkownikom, a następnie szybkim
zaadaptowaniu go do ich potrzeb.
Aplikacje są od siebie oddzielone, więc harmonogram dostarczania
małych modułów może być opracowany z dość dużą dokładnością.
Rozszerzenie sytemu o dodatkowe proste funkcjonalności może
być łatwe.
Z tym wzorcem dobrze współpracują bazy danych, które umożliwiają
integrację na poziomie danych.
Dobra współpraca z narzędziami 4GL.
Po udostępnieniu aplikacji osoby ją utrzymujące będą w stanie
szybko zmienić jej części, nawet te, których nie będą w stanie
zrozumieć. Ewentualne efekty zmian będą mieć tylko lokalny wpływ
na konkretny fragment interfejsu użytkownika.
Wady:
Integracja aplikacji jest trudna, o ile nie jest zrobiona poprzez bazę
danych.
Nie występuje współdzielenie kodu oraz nie ma żadnego modelu
abstrakcyjnego logiki biznesowej. Reguły biznesowe muszą być
duplikowane w każdej operacji, której dotyczą.
Szybkie prototypowanie oraz przeróbki kodu mają swoje naturalne
ograniczenia, ponieważ brak abstrakcji dziedziny ogranicza
możliwości zmian.
Złożoność szybko okaże się ograniczeniem nie do przezwyciężenia.
Dlatego naturalną ścieżką rozwoju będzie tworzenie kolejnych
prostych aplikacji. Nie ma prostej recepty na wzbogacanie istniejącej
logiki.
W przypadku świadomego zastosowania tego wzorca zespół może
uniknąć dużego narzutu pracy związanej z innymi podejściami. Częstą
pomyłką jest podejmowanie się korzystania ze złożonego podejścia do
projektowania w sytuacji, gdy zespół nie jest gotów kontynuować go na
całej ścieżce projektu. Kolejnym często popełnianym błędem jest tworzenie
złożonej warstwy infrastrukturalnej i korzystanie z najlepszych narzędzi
na rynku w projekcie, który właściwie tego nie potrzebuje.
699b15244ae879c728b313782703a4d7
6
Użycie większości uniwersalnych języków (takich jak Java) w przy-
padku tych aplikacji będzie stanowić dość dużą przesadę i będzie bardzo
kosztowne. W takiej sytuacji skorzystanie z rozwiązań 4GL będzie najlep-
szym sposobem.
Należy jednak pamiętać, iż konsekwencją wyboru tego wzorca jest
utrudniona migracja do innego podejścia projektowego — wybór ogra-
nicza się właściwie do zastąpienia całych aplikacji nowymi. Samo uży-
cie języków programowania ogólnego przeznaczenia, w rodzaju Javy, nie
umożliwi w przyszłości łatwego porzucenia wzorca INTELIGENTNEGO
INTERFEJSU UŻYTKOWNIKA. Jeżeli więc wybrałeś tę drogę, powi-
nieneś również dostosować swoje narzędzia. Nie próbuj się asekurować.
Z samego zastosowania uniwersalnego języka nie będzie wynikać elastycz-
ność systemu. Może się to jednak skończyć zwiększeniem kosztów.
Analogicznie zespół, który wybrał drogę PROJEKTOWANIA STE-
ROWANEGO MODELEM, powinien dostosować swój projekt od samego
początku. Oczywiście, nawet bardzo doświadczone, ambitne zespoły pro-
jektowe muszą rozpocząć od prostej funkcjonalności i rozwijać swą pracę
w kolejnych etapach. Jednakże te pierwsze czynności muszą być STE-
ROWANE MODELEM z wydzieloną warstwą dziedziny, gdyż w prze-
ciwnym razie projekt prawdopodobnie skończy na podejściu wykorzystu-
jącym INTELIGENTNY INTERFEJS UŻYTKOWNIKA.
***
Wzorzec INTELIGENTNEGO INTERFEJSU UŻYTKOWNIKA zo-
stał tutaj omówiony jedynie po to, aby wyjaśnić przyczyny sytuacji, w
których wzorzec ARCHITEKTURY WARSTWOWEJ jest niezbędny do
wyizolowania warstwy dziedziny.
Oczywiście, istnieją inne rozwiązania, leżące pomiędzy omówionymi
w tym rozdziale wzorcami. Na przykład Fowler w swojej książce opisuje
SKRYPT TRANSAKCYJNY, który oddziela interfejs użytkownika od apli-
kacji, jednak nie udostępnia modelu obiektowego. Płynie z tego nastę-
pujący wniosek: Jeżeli aplikacja wydziela kod związany z dziedziną w postaci
spójnego projektu dziedziny luźno związanego z pozostałą częścią systemu, wtedy
taka architektura prawdopodobnie mogłaby zostać zmodyfikowana do postaci projektu
sterowanego dziedziną.
Każdy z innych stylów programowania ma swoje zastosowanie, a Ty,
drogi Czytelniku, musisz nauczyć się akceptować kompromis pomiędzy
złożonością a elastycznością. W niektórych sytuacjach brak wydzielenia
projektu dziedziny może być naprawdę tragiczny. Jeżeli masz złożoną
aplikację i zdecydowałeś się na PROJEKT STEROWANY MODELEM,
wytrwaj w tym do końca, zatrudnij niezbędnych ekspertów i unikaj INTE-
LIGENTNEGO INTERFEJSU UŻYTKOWNIKA.
699b15244ae879c728b313782703a4d7
6
Inne rodzaje izolacji
Niestety, poza użytkownikiem oraz infrastrukturą istnieje wiele innych
czynników, które mogą zepsuć delikatny model dziedziny. Będziesz
musiał radzić sobie z innymi elementami dziedziny, które nie będą w pełni
zintegrowane z Twoim modelem. Będziesz musiał także współpracować
z innymi zespołami programistów, które będą wykorzystywać inne modele
tej samej dziedziny. To wszystko będą czynniki wpływające na zaciem-
nienie modelu oraz zmniejszenie jego przydatności. Tymi zagadnieniami
zajmiemy się w rozdziale 14., „Utrzymywanie integralności modelu”,
w którym wprowadzimy takie wzorce jak KONTEKST ZWIĄZANY oraz
WARSTWA OCHRONY PRZED USZKODZENIEM (ang. anticorruption
layer). Bardzo złożony model dziedziny może sam stać się bezużyteczny.
W rozdziale 15., „Destylacja”, zajmiemy się omówieniem metod umoż-
liwiających warstwie dziedziny oddzielenie pojęć zasadniczych od tych
wspierających.
Tym wszystkim zajmiemy się jednak później. W kolejnym rozdziale
natomiast przyjrzymy się podstawom umożliwiającym wspólny rozwój
efektywnego modelu dziedziny wraz z jego funkcjonalną implementacją.
Ostatecznie największym zyskiem z jej wydzielenia jest usunięcie wszyst-
kich pozostałych rzeczy z drogi, co ułatwia rzeczywiste skoncentrowanie
się na projektowaniu dziedziny.
699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 5.
Wyrażenie modelu
w programie
107
699b15244ae879c728b313782703a4d7
6
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.
699b15244ae879c728b313782703a4d7
6
Asocjacje
Wzajemne oddziaływanie faz modelowania oraz implementacji jest szcze-
gólnie skomplikowane w przypadku występowania pomiędzy obiektami
asocjacji.
Dla każdej połączonej asocjacji w modelu istnieje odpowiadający jej mecha-
nizm w aplikacji mający te same właściwości.
I tak na przykład model obrazujący asocjację pomiędzy klientem
a sprzedawcą dotyka dwóch obszarów. Po pierwsze, stanowi on abstrakcję
związku pomiędzy dwoma rzeczywistymi postaciami, którą programista
uznał za istotną dla projektu. Z drugiej strony, reprezentuje on wskaźnik
pomiędzy dwoma obiektami Javy albo zahermetyzowaną operację bazo-
danową lub też jakąkolwiek inną porównywalną implementację.
Dajmy na to, asocjacja jeden do wielu mogłaby zostać zaimplemen-
towana jako kolekcja w zmiennej instancji. Jednak projekt nie musi być
aż tak bezpośredni. Może w nim przecież nie być żadnych kolekcji — to
metoda dostępu (ang. accessor method) może odpytywać bazę danych, wyszu-
kując odpowiednie rekordy, a następnie na ich podstawie tworzyć instancje
obiektów. Oba te projekty odzwierciedlałyby ten sam model. Projekt
musiałby jedynie zapewniać określony mechanizm, którego działanie byłoby
spójne z asocjacją zdefiniowaną w modelu.
W naturze istnieje wiele asocjacji typu wiele do wielu, a duża ich część
jest w sposób oczywisty dwukierunkowa. Ta sama prawidłowość okazuje
się spełniona w przypadku wstępnych stadiów modelu, które zostaną
utworzone podczas odkrywania dziedziny. Niemniej jednak tego rodzaju
ogólne asocjacje komplikują implementację oraz późniejsze utrzymanie
projektu. Co więcej, nie przekazują one zbyt wielu informacji na temat
natury takiej relacji.
Dlatego istnieją co najmniej trzy sposoby uproszczenia późniejszej
implementacji asocjacji:
1. Narzucenie kierunku interpretacji asocjacji.
2. Dodanie kwalifikatora, skutecznie zmniejszającego wielorakość relacji1.
3. Usunięcie zbędnych asocjacji.
Bardzo ważne jest, aby jak najskuteczniej ograniczyć relacje. Asocjacja
dwukierunkowa oznacza, że oba obiekty będą mogły być zinterpretowane
jedynie wspólnie. Jeżeli z wymagań dla aplikacji nie wynika konieczność
modelowania relacji dwukierunkowych, narzucenie kierunku interpretacji
1
Czyli na przykład zmianę relacji wiele do wielu na jeden do wielu —
przyp. tłum.
ASOCJACJE 109
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
Ograniczenie kierunku interpretacji początkowej asocjacji wiele do
wielu w sposób skuteczny ograniczyło tę relację w implementacji do postaci
jeden do wielu. Taka relacja jest o wiele prostsza do zaprojektowania.
Stałe ograniczanie asocjacji jest jednym ze sposobów na odzwiercie-
dlenie wpływu dziedziny na model. Takie podejście powoduje, że aso-
cjacje przekazują więcej informacji i są prostsze do zaimplementowania.
Co więcej, nadaje też większe znaczenie pozostałym asocjacjom dwu-
kierunkowym. W takim przypadku zachowanie relacji dwukierunkowej
przekazuje informacje o jej specjalnym znaczeniu w dziedzinie oraz o tym,
że zachowanie dwukierunkowości jest niezbędne w aplikacji.
Oczywiście, w najbardziej skrajnym przypadku, jeżeli asocjacja nie
jest krytyczna dla zadania lub nie odzwierciedla zasadniczej właściwości
obiektów w modelu, uproszczenie polegać mogłoby na jej całkowitym
usunięciu.
Przykład
Asocjacje w modelu konta inwestycyjnego
Rysunek 5.3.
ASOCJACJE 111
699b15244ae879c728b313782703a4d7
6
return investments;
}
}
Tabela: BROKERAGE_ACCOUNT
ACCOUNT_NUMBER CUSTOMER_SS_NUMBER
Tabela: CUSTOMER
SS_NUMBER NAME
Tabela: INVESTMENT
699b15244ae879c728b313782703a4d7
6
Ulepszmy teraz nieco model, wprowadzając kwalifikację asocjacji
pomiędzy klasami Brokerage Account oraz Investment. Reguła będzie
stanowić, że może być tylko jedna inwestycja w dany typ akcji (ang. stock).
Rysunek 5.4.
ASOCJACJE 113
699b15244ae879c728b313782703a4d7
6
+ 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);
}
}
699b15244ae879c728b313782703a4d7
6
ENCJE (zwane również
obiektami referencyjnymi)
***
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
699b15244ae879c728b313782703a4d7
6
roku. Kiedy tylko ją odnalazła i sprawdziła, że moje dane nie uległy zmianie
(i znajdowały się obok danych mojego imiennika), zdała sobie sprawę,
że nie jestem osobą, która zamierzała pozwać. Wtedy przeprosiła mnie
i obiecała wycofać pozew.
Komputery nie mają aż tylu danych. Przypadek pomylonej tożsa-
mości w oprogramowaniu komputerowym może prowadzić do uszko-
dzenia danych oraz błędów w programie.
W tym przypadku istnieją wyzwania techniczne, o których opowiemy,
lecz zacznijmy od przyjrzenia się podstawowemu problemowi. Wiele róż-
nych rzeczy określanych jest przez tożsamość, a nie przez atrybuty.
W naszym typowym przykładzie osoba (aby wciąż skupić się na nietech-
nicznym przykładzie) ma tożsamość, która jest z nią od chwili narodzin
aż do śmierci, a może nawet dalej. Fizyczne atrybuty tej osoby ulegają
zmianom, a nawet ostatecznie zanikają. Nazwisko może ulec zmianie.
Relacje finansowe pojawiają się i znikają. Nie istnieje ani jeden atrybut
danej osoby, który nie mógłby ulec zmianie, chociaż jej tożsamość wciąż
niezmiennie istnieje. Czy jestem wciąż tą samą osobą, która byłem w wieku
pięciu lat? Tego rodzaju metafizyczne pytanie jest bardzo istotne w pro-
cesie poszukiwania skutecznych modeli dziedziny. Spróbujmy je nieco
zmienić: Czy dla użytkownika aplikacji istotne jest, czy jestem tą samą
osobą, co w wieku pięciu lat?
W przypadku systemów informatycznych do śledzenia płatności „zwy-
czajny” obiekt klienta może mieć znacznie bardziej bujną naturę. Obiekt
ten może śledzić poprawne płatności lub też w przypadku zalegania z nimi
może zostać przekazany do agencji ds. windykacji długów. Co więcej,
może nawet wieść podwójne życie w innym systemie, w którym przed-
stawiciel handlowy wydobywa dane klientów i pobiera je do swojego
systemu zarządzania kontaktami. W każdym z tych przypadków obiekt ten
ostatecznie jest bezceremonialnie kompresowany do płaskiej postaci, aby
mógł zostać zachowany w tabeli w bazie danych. Jeżeli z tym obiektem
nie będzie już związany żaden interes, może zostać on wycofany na eme-
ryturę do archiwum, gdzie będzie stanowić tylko swój cień.
Każda z powyższych postaci klienta jest jego inną implementacją,
opartą na innym języku programowania lub technologii. Jednak w chwili,
gdy zadzwoni telefon z nowym zamówieniem, istotne jest, aby wiedzieć:
Czy jest to ten sam klient, który posiadał dane konto? Czy to ten sam
klient, z którym Jack (imię przedstawiciela handlowego) pracował przez
długie tygodnie? A może jest to nowy klient?
Tożsamość w tym pojęciu musi być porównywalna z jej różnymi
implementacjami obiektowymi, z formą zapisu w bazie danych, a nawet
z rzeczywistymi aktorami, w rodzaju rozmówcy telefonicznego. Atrybuty
699b15244ae879c728b313782703a4d7
6
nie muszą się zgadzać. Przedstawiciel handlowy mógł zaktualizować
w swoim programie adres klienta, a ta zmiana może być dopiero przeno-
szona do programu obsługującego konta. Dwaj klienci mogą mieć to samo
nazwisko. W systemach rozproszonych wielu użytkowników mogłoby
jednocześnie wprowadzać dane z różnych źródeł, co powodowałoby asyn-
chroniczne wprowadzanie do bazy danych transakcji zmian wciąż wędru-
jących po systemie.
Modelowanie obiektowe skłania nas do koncentrowania się na atry-
butach obiektów, jednak podstawowym założeniem ENCJI (ang. entity)
jest abstrakcyjna ciągłość w całym cyklu życia obiektu, a nawet rozciąga-
jąca się na wiele jego postaci, w których jest on implementowany.
Niektóre obiekty nie są określane jedynie przez swoje atry-
buty. Reprezentują one tożsamość stałą w czasie, a bardzo często
również w różnych sposobach ich reprezentacji. Niekiedy tego
rodzaju obiekt musi być porównywany z innym, nawet jeżeli jego
atrybuty różnią się między sobą. Co więcej, musi również istnieć
sposób na odróżnienie obiektu spośród wielu obiektów o tych
samych atrybutach. Pomyłka w tożsamości może prowadzić do
uszkodzenia danych.
Obiekt definiowany głównie przez swoją tożsamość nazywany jest
ENCJĄ2.
ENCJE wymagają specjalnego podejścia podczas modelowania oraz
projektowania. W trakcie ich całego cyklu życia ich postać oraz zawartość
mogą ulec radykalnym zmianom, jednak ich ciągłość musi być zacho-
wana. Tożsamość encji musi być zdefiniowana w taki sposób, aby można
było ją skutecznie śledzić. Ich definicje klas, odpowiedzialności, atrybuty
oraz asocjacje powinny wszystkie razem koncentrować się na tym, co
reprezentują, a nie jakie konkretnie wartości przechowują. Nawet w przy-
padku ENCJI, które nie zmieniają się tak drastycznie lub nie mają takiego
złożonego cyklu życia, dołączenie ich do kategorii semantycznej modelu
czyni go bardziej przejrzystym i prowadzi do bardziej wszechstronnych
implementacji.
Oczywiście, większość ENCJI w programach komputerowych nie
reprezentuje ludzi lub encji w zwyczajowym pojęciu. ENCJĄ może być
wszystko, co ma ciągłość w całym cyklu życia i wyróżniki niezależne od
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.
699b15244ae879c728b313782703a4d7
6
atrybutów, które są istotne z punktu widzenia użytkownika aplikacji. Encją
może być osoba, miasto, samochód, kupon loteryjny lub transakcja bankowa.
Z drugiej strony, nie wszystkie obiekty w modelu są ENCJAMI
z istotnymi tożsamościami. Nieporozumienie wynika z tego, że języki
obiektowe mają wbudowane w każdy obiekt operacje „na tożsamości”
(na przykład operator == w języku Java). Te operacje określają, czy dwie
referencje wskazują na ten sam obiekt, porównując ich położenie w pamięci
lub używając innego mechanizmu. W tym zrozumieniu każda instancja
obiektu ma pewną „tożsamość”. W przypadku modelowania dziedziny,
dajmy na to środowiska uruchomieniowego Javy lub technicznego szkie-
letu umożliwiającego lokalne składowanie w pamięci podręcznej obiek-
tów zdalnych, każda instancja obiektów mogłaby rzeczywiście stanowić
ENCJĘ. Jednak ten rodzaj mechanizmu określającego tożsamość nie będzie
znaczyć zbyt wiele w innych dziedzinach aplikacyjnych. Tożsamość jest
subtelnym, mającym duże znaczenie atrybutem ENCJI, który nie może być
oddelegowany do automatycznych właściwości języka programowania.
Rozważmy na przykład transakcje w aplikacji bankowej. Dwie lokaty
o tym samym saldzie, założone w ten sam dzień dla tego samego rachunku
wciąż będą oznaczać oddzielne transakcje. Dlatego mają one tożsamość
i są ENCJAMI. Z drugiej strony, atrybuty określające saldo tych dwóch
transakcji są prawdopodobnie instancjami tego samego obiektu.
Dlatego one z kolei nie będą mieć tożsamości, ponieważ nie ma żad-
nej korzyści z ich rozróżniania. W rzeczywistości dwa obiekty mogą mieć
tę samą tożsamość, nie posiadając tych samych atrybutów lub nawet nie
będąc instancjami tej samej klasy. Kiedy na przykład klient banku porów-
nuje transakcje z wyciągu bankowego z transakcjami wykonanymi przy
użyciu czeków3, ma głównie za zadanie porównać transakcje o tej samej
tożsamości, nawet jeżeli zostały one zarejestrowane przez różne organi-
zacje w różnych dniach (data obciążenia konta jest zazwyczaj późniejsza
niż data wystawienia czeku). W tym celu za unikalny identyfikator może
służyć numer czeku w książeczce, niezależnie od tego, czy takiej weryfikacji
dokonywać będzie człowiek, czy też program komputerowy. Uznania oraz
obciążenia rachunków nieposiadające identyfikatora mogą stanowić poważ-
niejszy problem, jednak w obu przypadkach zastosowanie ma to samo
pryncypium, tj. każda transakcja jest ENCJĄ, która jest reprezentowana
przynajmniej w dwóch postaciach.
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.
699b15244ae879c728b313782703a4d7
6
Bardzo często tożsamość jest istotna również poza kontekstem kon-
kretnego systemu informatycznego, jak ma to miejsce dla przykładów
z operacjami bankowymi lub najemcą apartamentu. Niekiedy jednak toż-
samość jest istotna jedynie w kontekście danego systemu, jak chociażby
w przypadku procesu komputerowego.
Dlatego:
W sytuacji, gdy obiekt jest wyróżniany przez swoją tożsamość,
a nie swoje atrybuty, umieść to jako podstawowe założenie jego
definicji w modelu. Utrzymuj definicję klasy w prostej postaci,
koncentrując się na ciągłości w jej cyklu życia oraz jej tożsamości.
Określ sposoby rozróżnienia każdego z obiektów niezależnie od
jego postaci lub historii. Uważaj na wymagania dotyczące porów-
nywania obiektów przy użyciu ich atrybutów. Dla każdego z tych
obiektów zdefiniuj operację, która gwarantuje zwrócenie nie-
powtarzalnego wyniku. Możesz to zapewnić, dołączając symbol
o gwarantowanej niepowtarzalności. Oznacza to, że identyfikator
obiektu może być nadawany zewnętrznie lub też utworzony spe-
cjalnie dla systemu. W obu przypadkach musi on zapewniać roz-
różnienie tożsamości w modelu. Model musi definiować, co
oznacza, że dwie rzeczy są identyczne.
Tożsamość nie jest cechą wrodzoną wszystkich rzeczy na tym świecie.
Jest ona narzuconym znaczeniem, ponieważ jest przydatna. W rzeczywi-
stości ta sama rzeczywista rzecz może, lecz nie musi być reprezentowana
w modelu dziedziny w postaci ENCJI.
Na przykład aplikacja obsługująca rezerwacje miejsc na stadionie
mogłaby traktować miejsca oraz widzów jako ENCJE. W przypadku
miejsca dedykowanego, w którym każdy bilet ma naniesiony jego numer,
będzie ono stanowić ENCJĘ. Jej identyfikatorem będzie numer miejsca,
niepowtarzalny na całym stadionie. Miejsce mogłoby mieć wiele innych
atrybutów, w rodzaju położenia, stopnia widoczności płyty stadionu, ceny
biletu. Niemniej jednak do jednoznacznej identyfikacji i rozróżnienia miejsc
będzie używany tylko numer miejsca lub też niepowtarzalny numer rzędu
i miejsca w nim.
Jeżeli jednak impreza ma mieć „wstęp wolny”, wtedy posiadacz biletu
będzie mógł zająć każde wolne miejsce, które tylko znajdzie. Nie będzie
więc konieczności rozróżniania poszczególnych miejsc, istotna będzie
tylko całkowita ich liczba. Chociaż numer miejsca będzie wciąż umiesz-
czony przy konkretnym siedzeniu, to jednak program nie będzie musiał
go śledzić. W rzeczywistości skojarzenie określonych numerów miejsc
699b15244ae879c728b313782703a4d7
6
z biletami w modelu mogłoby być przyczyną błędów, ponieważ w przy-
padku imprez masowych z wolnym wstępem nie ma takiego ograniczenia.
W takiej sytuacji miejsca nie będą ENCJAMI, a identyfikator nie będzie
wymagany.
***
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).
699b15244ae879c728b313782703a4d7
6
Rysunek 5.5.
Atrybuty związane
z tożsamością pozostają
w ENCJI
699b15244ae879c728b313782703a4d7
6
Na przykład dzienniki mogłyby być identyfikowane przez tytuł gazety,
miasto oraz datę wydania (jednak należy przy tym uważać na wydania
specjalne oraz zmiany tytułów!).
W sytuacji, gdy nie istnieje prawdziwy klucz unikalny złożony z atry-
butów obiektu, kolejnym powszechnie stosowanym rozwiązaniem jest
dołączenie do każdej z jego instancji symbolu (liczby lub łańcucha zna-
kowego), który będzie unikalny w obrębie klasy. W chwili ustanowienia
takiego symbolu ID oraz jego zapisania w jednym z atrybutów ENCJI
zostanie on oznaczony jako niezmienny. Atrybut ten nie będzie mógł być
zmieniony nawet wtedy, gdy środowisko programistyczne nie jest w stanie
wymusić tej reguły bezpośrednio. Na przykład atrybut ID będzie zacho-
wywany, gdy obiekt zostanie spłaszczony4 w celu zapisu do bazy danych,
a następnie z niej odtworzony. W tym procesie pomaga niekiedy sam szkie-
let techniczny aplikacji. W przeciwnym razie będzie to jednak wymagać
dyscypliny projektanta.
Bardzo często identyfikator jest również automatycznie generowany
przez system. Algorytm tworzący takie identyfikatory musi gwarantować
ich niepowtarzalność w systemie, co samo w sobie może stanowić wyzwa-
nie w przypadku przetwarzania równoległego lub rozproszonego. Sam
proces może wymagać technik będących poza zakresem tej książki. W tym
miejscu chciałbym jedynie wskazać programistom miejsca, w których
mogą wystąpić wątpliwości, tak aby byli oni świadomi istnienia problemu
do rozwiązania i wiedzieli, w jaki sposób zawęzić swoją analizę do krytycz-
nych obszarów aplikacji. Kluczowe wydaje się zrozumienie, iż problemy
związane z tożsamością zależą od określonych aspektów modelu. Bardzo
często identyfikacja obiektów będzie wymagać również skrupulatnego zapo-
znania się z dziedziną.
W sytuacji automatycznego tworzenia identyfikatora może nigdy nie
wystąpić potrzeba pokazania go użytkownikowi. Identyfikator ten może
być tylko potrzebny wewnątrz aplikacji, jak na przykład w programie do
zarządzania kontaktami, który umożliwia odszukanie rekordów użytkow-
nika po jego nazwie. Program będzie musiał mieć możliwość rozróżnienia
w prosty i jednoznaczny sposób dwóch kontaktów o tej samej nazwie.
Właśnie w tym celu można wykorzystać unikalny i wewnętrzny atrybut
identyfikatora ID. Po wczytaniu dwóch różnych obiektów system będzie
mógł pokazać użytkownikowi dwa różne kontakty, jednak ID nie musi
być nawet wyświetlane. Użytkownik rozróżni kontakty po nazwie firmy,
jej położeniu itp.
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.
699b15244ae879c728b313782703a4d7
6
Ostatecznie istnieć będą również sytuacje, w których wygenerowany
identyfikator mógłby interesować użytkownika. Na przykład w sytuacji,
gdy wysyłam paczkę kurierem, otrzymuję identyfikator umożliwiający jej
śledzenie, który jest generowany przez program firmy kurierskiej. Kiedy
rezerwuję bilet lotniczy lub pokój w hotelu, również otrzymuję potwier-
dzenie rezerwacji z unikalnym numerem transakcji.
W niektórych przypadkach ta niepowtarzalność numeru ID musi
wykraczać poza granice systemu komputerowego. Jeżeli na przykład doku-
mentacja medyczna pacjenta jest wymieniana pomiędzy różnymi szpitalami
używającymi różnych systemów komputerowych, wtedy idealnie byłoby,
gdyby każdy z nich używał tego samego identyfikatora pacjenta, jednak
może być to niezmiernie trudne w sytuacji, gdy generują własne. Tego
rodzaju systemy bardzo często używają identyfikatora wystawionego przez
inne instytucje (zazwyczaj agencje rządowe). W Stanach Zjednoczonych
Ameryki Północnej w tym celu bardzo często używa się numeru ubezpie-
czenia społecznego (Social Security Number5). Jednak tego rodzaju metody
też nie są odporne na wszystkie błędy6. Nie wszyscy obywatele kraju mają
numer Social Security (szczególnie dotyczy to dzieci i nierezydentów),
a wiele osób nie chce go używać z powodu obaw o ochronę prywatności7.
W mniej oficjalnych sytuacjach (na przykład w wypożyczalni kaset
wideo) takimi identyfikatorami mogą być numery telefonów. Jednak telefon
może być pożyczony, a jego numer również może się zmienić. Co wię-
cej, stare numery są bardzo często ponownie przypisywane do nowych
użytkowników.
Z tych przyczyn do odszukiwania rekordów oraz ich weryfikacji
bardzo często wykorzystywane są specjalnie przypisane sztuczne identyfi-
katory (na przykład numer członkowski klubu częstego podróżnika) oraz
inne atrybuty, w rodzaju numeru telefonu, numerów Social Security itp.
W każdej sytuacji, gdy aplikacja będzie wymagać zewnętrznego identyfi-
katora, to na użytkowniku systemu będzie spoczywać odpowiedzialność
5
W Polsce odpowiednikiem byłby numer PESEL — przyp. tłum.
6
Także system PESEL, pomimo zabezpieczeń systemowych, odnotował
przypadki nadania tego samego numeru dwóm różnym osobom. Według staty-
styk w roku 2013 takich przypadków było ponad dwa tysiące — przyp. tłum.
7
Należy pamiętać, że w USA obowiązuje często prawo stanowe. W Polsce
każdy dorosły obywatel ma dowód osobisty z numerem PESEL, a numer ten nada-
wany jest już dzieciom w chwili ich rejestracji. W USA obywatele nie mają wspól-
nego dowodu tożsamości, numer Social Security jest najczęściej nadawany po
uzyskaniu pełnoletności i podjęciu pierwszej pracy, jego podanie w wielu insty-
tucjach jest dobrowolne, a ogromna liczba obywateli o nieuregulowanym statusie
prawnym nie ma tego numeru wcale. Można powiedzieć, że w Polsce sytuacja
jest zdecydowanie mniej skomplikowana pod tym względem — przyp. tłum.
699b15244ae879c728b313782703a4d7
6
za wprowadzenie unikalnego identyfikatora, a system powinien dostarczać
użytkownikowi odpowiednie narzędzia, umożliwiające obsługę sytuacji
wyjątkowych, które z pewnością powstaną.
Biorąc pod uwagę te wszystkie techniczne problemy, bardzo łatwo
jest stracić z oczu istotę modelowanych obiektów. Co tak naprawdę dla
obiektów oznacza, że są takie same? Łatwo jest ostemplować każdy obiekt
identyfikatorem lub napisać funkcję porównującą dwie instancje obiek-
tów. Jednak jeżeli te identyfikatory lub funkcje nie pozwalają na pewne
rozróżnienie w dziedzinie, to cały obraz skomplikuje się jeszcze bardziej.
Dlatego właśnie bardzo często w operacje przypisywania identyfikato-
rów włączony jest człowiek. Na przykład oprogramowanie porównujące
operacje z książeczek czekowych bardzo często wykrywa pasujące wzorce,
jednak to użytkownik musi podjąć ostateczną decyzję.
699b15244ae879c728b313782703a4d7
6
WARTOŚCI
WARTOŚCI 125
699b15244ae879c728b313782703a4d7
6
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Ą.
699b15244ae879c728b313782703a4d7
6
WARTOŚCI mogą nawet odwoływać się do ENCJI. Jeżeli na przy- W programie dla
kład zapytam internetową usługę z mapami o drogę widokową z San Fran- firmy energetycznej
adres odpowiada
cisco do Los Angeles, mogę w rezultacie otrzymać obiekt Route (droga), miejscu docelowemu,
łączący Los Angeles z San Francisco drogą o nawie Pacific Coast Highway. do którego dostarczane
Obiekt Route byłby WARTOŚCIĄ, pomimo że trzy obiekty, do których są linie energetyczne
się odwołuje (dwa miasta oraz autostrada), są ENCJAMI. oraz sama usługa.
Gdyby każdy
WARTOŚCI są często przekazywane jako parametry w komunika- ze współlokatorów
tach pomiędzy obiektami. Bardzo często są one ulotne, tworzone jedynie zadzwonił do firmy,
na potrzeby konkretnej operacji, a później niszczone. Są one używane zamawiając instalację
również w charakterze atrybutów ENCJI (lub innych WARTOŚCI). Na usługi, firma musiałaby
taką sytuację wykryć.
przykład osoba mogłaby zostać zamodelowana jako ENCJA z tożsamością, Adres będzie ENCJĄ.
lecz jej nazwisko byłoby WARTOŚCIĄ. Opcjonalnie model
W sytuacji, gdy zależy Ci tylko na atrybutach danego elementu mógłby skojarzyć
modelu, zakwalifikuj je do WARTOŚCI. Pozwól im wyrażać zna- usługę z „miejscem
zamieszkania”, czyli
czenie atrybutów, które przechowują, i dodaj im wymaganą funk- ENCJĄ z atrybutem
cjonalność. Traktuj WARTOŚCI jako niezmienne. Nie nadawaj w postaci adresu.
im tożsamości i unikaj złożoności projektowych wymaganych W takim przypadku
adres byłby
w ENCJACH.
WARTOŚCIĄ.
Atrybuty stanowiące WARTOŚĆ powinny tworzyć formę pojęciowej
całości8. Na przykład ulica (ang. street), miasto (ang. city) oraz kod pocz-
towy (ang. postal code) nie powinny być oddzielnymi atrybutami obiektu
osoby (ang. Person). Są one częścią całego adresu, co czyni obiekt Person
prostszą oraz bardziej spójną WARTOŚCIĄ.
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
a takim, który będzie niezmiernie wolny, gdyż zapcha go milion powie- Specjalne
lonych obiektów. To jeden z przykładów sztuczek optymalizacyjnych, który przypadki: Kiedy
dopuszczać zmiany?
nie będzie dostępny dla ENCJI. Niezmienność
Różnice w zysku pomiędzy kopiowaniem a współdzieleniem będą jest wspaniałym
zależeć od środowiska implementacyjnego. Chociaż kopie mogą zapchać uproszczeniem
implementacji,
system setkami obiektów, to współdzielenie może spowolnić systemy roz-
pozwalającym
proszone. Jeżeli pomiędzy dwoma komputerami przekazywana jest kopia, na bezpieczne
wysyłany jest pojedynczy komunikat, a kopia zaczyna istnieć niezależnie współdzielenie
oraz przekazywanie
na maszynie docelowej. Natomiast w przypadku współdzielenia pojedyn-
referencji. Jest również
czej instancji przekazywana jest tylko referencja do obiektu, co powoduje spójna ze znaczeniem
konieczność wysyłania komunikatu zwrotnego do obiektu w przypadku wartości. Jeżeli wartość
każdej interakcji. atrybutu ulegnie
zmianie, wtedy
Współdzielenie najbardziej przydaje się w wymienionych poniżej używasz innej
sytuacjach, w których ma ono największą wartość i sprawia najmniej WARTOŚCI, zamiast
problemów: zmieniać już istniejącą.
Pomimo to istnieją
Gdy krytyczne staje się ograniczenie zajmowanej przestrzeni lub przypadki, w których
liczby obiektów w bazie danych. przez wzgląd
na wydajność
Gdy narzut komunikacyjny jest niewielki (na przykład w architek- implementacji
faworyzowane
turze ze scentralizowanym serwerem).
byłoby podejście
Gdy współdzielony obiekt jest niezmienny. umożliwiające zmianę
WARTOŚCI. Poniższe
Niezmienność atrybutu lub obiektu może zostać zadeklarowana tylko czynniki przeważałyby
na korzyść
w niektórych językach lub środowiskach. Tego rodzaju właściwość pomaga dopuszczalności
w przekazywaniu decyzji projektowych, lecz nie jest niezbędna. Wiele zmian:
z wyróżników stosowanych w modelu i tak zazwyczaj nie może być Jeżeli WARTOŚĆ
w sposób bezpośredni zadeklarowanych w trakcie implementacji przy użyciu ulega częstym
zmianom.
najnowszych narzędzi i języków programowania. Na przykład nie można
Jeżeli tworzenie lub
zadeklarować ENCJI i wymusić w sposób automatyczny działań na jej usuwanie obiektu
tożsamości. Niemniej jednak brak bezpośredniego wsparcia w języku dla jest kosztowne.
danego wyróżnika pojęciowego nie oznacza jego nieprzydatności. Wyma- Jeżeli zastąpienie
(zamiast modyfikacji)
gać będzie jedynie większego zdyscyplinowania do utrzymania reguł, które obiektu naruszyłoby
w implementacji będą jedynie zadeklarowane w sposób pośredni. Efekt grupę obiektów
ten można wzmocnić poprzez odpowiednie konwencje nazewnicze, wybiór- (jak zostało
to omówione
czą dokumentację lub rozszerzoną dyskusję.
w jednym
Dopóki WARTOŚCI są niezmienne, zarządzanie ich zmianami jest z poprzednich
bardzo proste, tzn. nie ma żadnej zmiany za wyjątkiem zupełnego zastą- przykładów).
pienia obiektu nowym. Niezmienne obiekty mogą być dowolnie współ-
dzielone, jak ma to miejsce w przykładzie z przyłączami elektrycznymi.
Jeżeli można polegać na procesie czyszczenia pamięci (ang. garbage col-
lector), wtedy usuwanie obiektów jest tak proste jak usunięcie wszystkich
referencji do niego. W sytuacji, gdy w projekcie WARTOŚĆ zostanie
WARTOŚCI 129
699b15244ae879c728b313782703a4d7
6
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.
699b15244ae879c728b313782703a4d7
6
Projektowanie asocjacji
korzystających z WARTOŚCI
Większość wcześniejszych rozważań na temat asocjacji ma takie samo
zastosowanie do ENCJI, jak do WARTOŚCI. Im mniej prostszych aso-
cjacji w modelu, tym lepiej.
Jeżeli jednak dwukierunkowe asocjacje pomiędzy ENCJAMI mogą
być trudne w utrzymaniu, to dwukierunkowe asocjacje pomiędzy dwiema
WARTOŚCIAMI najzwyczajniej nie mają sensu. Bez tożsamości stwier-
dzenie, że obiekt wskazuje na tę samą WARTOŚĆ, która się do niego
odwołuje, nie będzie mieć żadnego znaczenia. Co najwyżej moglibyśmy
powiedzieć, że wskazuje on na obiekt równoważny temu, który się do
niego odwołuje. Czy jednak musielibyśmy ten niezmiennik gdzieś wymu-
szać? A nawet gdybyśmy mogli to zrobić i skonfigurować wskaźniki w obu
kierunkach, trudno byłoby wymyślić jakiekolwiek przydatne ich zastoso-
wanie. Dlatego postaraj się zupełnie usunąć dwukierunkowe asocjacje
pomiędzy WARTOŚCIAMI. Jeżeli ostatecznie tego rodzaju asocjacje
wydadzą Ci się w modelu niezbędne, to w pierwszej kolejności zastanów
się powtórnie, czy deklarować ten obiekt jako WARTOŚĆ. Może posiada
on jeszcze nieodkrytą tożsamość.
WARTOŚCI 131
699b15244ae879c728b313782703a4d7
6
USŁUGI
699b15244ae879c728b313782703a4d7
6
Niekiedy usługi naśladują obiekty modelu, pojawiając się w nim
jako obiekty, które nie mają znaczenia poza kontekstem danej operacji.
Tego rodzaju obiekty, których jedynym zadaniem jest „działanie”, otrzy-
mują zazwyczaj nazwy kończące się słowem „Manager” (zarządca) lub
podobnym. Nie mają one własnego stanu ani żadnego znaczenia w dzie-
dzinie oprócz operacji, za którą są odpowiedzialne. Tego rodzaju rozwiąza-
nie przynajmniej daje tym konkretnym wyróżnionym operacjom miejsce,
do którego przynależą bez gmatwania rzeczywistych obiektów modelu.
Niektóre pojęcia z dziedziny nie dają się w sposób naturalny
zamodelować jako obiekty. Usiłowanie zdefiniowania danej wyma-
ganej funkcjonalności dziedzinowej w charakterze odpowiedzial-
ności ENCJI lub WARTOŚCI może skutkować wypaczeniem defi-
nicji obiektów opartych na modelu lub też dodaniem pozbawionych
znaczenia sztucznych obiektów.
USŁUGA jest operacją, która jest dostępna w postaci występującego
niezależnie w modelu interfejsu, bez żadnego hermetyzowanego stanu
(w przeciwieństwie do ENCJI lub WARTOŚCI). USŁUGI są powszech-
nym wzorcem w szkieletach technicznych, jednak mogą również być
stosowane w warstwie dziedziny.
Sama nazwa usługa uwypukla relację z innymi obiektami. W odróż-
nieniu od ENCJI oraz WARTOŚCI usługi są zdefiniowane całkowicie
w kontekście tego, co mogą zrobić dla klienta. USŁUGA jest raczej nazy-
wana od działania, a nie od encji — jest bardziej czasownikiem niż rze-
czownikiem. USŁUGA wciąż może dysponować abstrakcyjną zamie-
rzoną definicją. Ma ona jedynie nieco inną postać niż definicja obiektu.
USŁUGA powinna wciąż mieć zdefiniowaną odpowiedzialność, która wraz
z interfejsem ją wykonującym powinna stanowić część modelu dziedziny.
Nazwy operacji powinny pochodzić z JĘZYKA WSZECHOBECNEGO
lub być do niego dodane. Parametry oraz wyniki operacji powinny być
obiektami dziedziny.
USŁUGI należy stosować roztropnie i nie zastępować nimi całego
znaczenia ENCJI oraz WARTOŚCI. Jeżeli jednak operacja stanowi ważne
pojęcie dziedziny, wtedy USŁUGA staje się naturalną częścią PRO-
JEKTU STEROWANEGO MODELEM. Taka samodzielna operacja,
będąc zadeklarowaną w modelu w charakterze USŁUGI zamiast dziw-
nego obiektu, który nie reprezentuje sobą czegokolwiek, nie wprowadzi
nikogo w błąd.
Poprawna USŁUGA ma trzy cechy:
1. Operacja odwołuje się do pojęcia dziedziny, która nie stanowi natu-
ralnej części ENCJI ani WARTOŚCI.
USŁUGI 133
699b15244ae879c728b313782703a4d7
6
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.
***
699b15244ae879c728b313782703a4d7
6
już osiągnięty, chociaż to zadanie prawdopodobnie nie wymagałoby
USŁUGI, gdyż dobrze pasowałoby do jednej z odpowiedzialności obiektu
„konto”. Aplikacja bankowa mogłaby być odpowiedzialna za przelewy
środków. Gdyby w takim przypadku utworzona została USŁUGA odpo-
wiedzialna za poprawne obciążenia oraz uznania rachunków, wtedy taka
funkcjonalność przynależałaby do warstwy dziedziny. Przelewy mają prze-
cież określone znaczenie w języku dziedziny bankowej i wymagają pod-
stawowej logiki biznesowej. Technicznie rzecz ujmując, USŁUGI nie
powinny mieć żadnego znaczenia biznesowego.
Wiele USŁUG dziedziny lub aplikacji w rzeczywistości utworzonych
jest przy użyciu zbioru ENCJI oraz WARTOŚCI i zachowuje się podobnie
jak skrypt, który wykorzystuje potencjał dziedziny w celu osiągnięcia
pewnych rezultatów. ENCJE oraz WARTOŚCI bardzo często są zbyt
szczegółowe, aby pozwalać na wygodny dostęp do możliwości warstwy
dziedziny. W ten sposób napotykamy bardzo dobre rozgraniczenie pomiędzy
warstwami dziedziny oraz aplikacji. Na przykład, jeżeli aplikacja bankowa
może przekonwertować i wyeksportować nasze transakcje do pliku Excel
w celu ich późniejszej analizy, wtedy taka funkcjonalność będzie USŁUGĄ
aplikacji. W dziedzinie bankowości „formaty plików” nie mają przecież
żadnego znaczenia, a z całym tym procesem nie będą związane żadne
reguły.
Z drugiej strony, funkcjonalność przesyłająca pieniądze z jednego konta
na drugie będzie USŁUGĄ dziedziny, ponieważ wymaga ona znacznej
wiedzy biznesowej (chociażby do obciążenia oraz uznania rachunków),
a także ponieważ „przelew pieniężny” jest istotnym określeniem banko-
wym. W tym przypadku sama USŁUGA nie będzie robić zbyt wiele.
O wykonanie większości wymaganych zadań najzwyczajniej poprosi dwa
obiekty reprezentujące konto. Niemniej jednak umieszczenie samej operacji
„przelew” w obiekcie reprezentującym konto byłoby nieco dziwne, ponie-
waż ta operacja wymaga użycia dwóch kont i pewnych reguł globalnych.
Moglibyśmy również chcieć utworzyć specjalny obiekt o nazwie
Funds Transfer (transfer środków), reprezentujący te dwa konta oraz reguły
i historię przelewu. Wciąż jednak musielibyśmy wywoływać USŁUGI
w sieci międzybankowej. Co więcej, w większości środowisk programi-
stycznych dziwne jest wykonywanie bezpośrednich interfejsów pomiędzy
obiektami dziedziny a zewnętrznymi zasobami. Dlatego moglibyśmy dodat-
kowo przykryć tę zewnętrzną USŁUGĘ FASADĄ, przyjmującą parametry
wejściowe zgodne z modelem, a zwracającą być może obiekt reprezen-
tujący przelew środków. Jakichkolwiek jednak nie użylibyśmy warstw
pośrednich (nawet tych nienależących do nas), to i tak te USŁUGI będą
obsługiwać odpowiedzialność dziedziny za przelew środków.
USŁUGI 135
699b15244ae879c728b313782703a4d7
6
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-
699b15244ae879c728b313782703a4d7
6
datną w chwili opakowywania komponentów w systemach dużych lub roz-
proszonych. Niekiedy też USŁUGA jest najbardziej naturalnym sposo-
bem na wyrażenie pojęć dziedziny.
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
699b15244ae879c728b313782703a4d7
6
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.
699b15244ae879c728b313782703a4d7
6
Zawsze wtedy, gdy dwa elementy modelu są wydzielane do różnych
modułów, ich związki stają się mniej bezpośrednie niż wcześniej, co zwięk-
sza narzut związany ze zrozumieniem miejsca, jakie zajmują w całym
projekcie. Słabe związanie pomiędzy MODUŁAMI minimalizuje koszt
i umożliwia analizę zawartości jednego z MODUŁÓW bez wnikania
w szczegóły innych, z którymi ma on kontakt.
Równocześnie jednak między elementami dobrego modelu zachodzi
synergia, a dobrze wybrane MODUŁY skupiają w sobie elementy modelu
o szczególnie bliskich związkach pojęciowych. Wysoka spójność obiektów
o powiązanych odpowiedzialnościach pozwala na koncentrację zadań zwią-
zanych z modelowaniem oraz projektowaniem w obrębie pojedynczego
MODUŁU, czyli w skali złożoności łatwo akceptowalnej przez ludzki
umysł.
MODUŁY oraz inne, mniejsze elementy powinny być rozwijane
wspólnie, lecz zazwyczaj tak nie jest. Te pierwsze są zwykle wybierane
w celu uporządkowania początkowych postaci obiektów. Następnie obiekty
zmieniane są w sposób utrzymujący je w granicach istniejącej definicji
MODUŁU. Refaktoring MODUŁÓW będzie oznaczać więcej pracy
i będzie bardziej destrukcyjny dla projektu niż refaktoring klas. Dlatego
też będzie on z pewnością wykonywany rzadziej. Jednak chociaż obiekty
modelu na początku są zazwyczaj proste, a następnie stopniowo przekształ-
cane w bardziej złożone, w miarę odkrywania głębszego znaczenia dzie-
dziny, to MODUŁY mogą wciąż pozostać subtelne i posiadać wysoki
poziom abstrakcji. Umożliwienie MODUŁOM nadążania za zmieniają-
cym się zrozumieniem dziedziny da również większą swobodę rozwoju
zawartym w nich obiektom.
Podobnie jak wszystkie pozostałe zagadnienia w projekcie sterowanym
dziedziną, MODUŁY również stanowią mechanizm komunikacyjny. Podział
na MODUŁY wyznaczany jest przez znaczenie partycjonowanych obiek-
tów. Umieszczenie klas w jednym MODULE nakazuje kolejnemu progra-
miście patrzącemu na ten projekt analizowanie tych klas łącznie. Jeżeli
wyobrazimy sobie, że model opowiada pewną historię, to MODUŁY mogą
być jej rozdziałami. Nazwa MODUŁU będzie przekazywać jego znaczenie
i zostanie dodana do JĘZYKA WSZECHOBECNEGO. Możemy na przy-
kład powiedzieć do eksperta biznesowego: „A teraz porozmawiajmy o mo-
dule klienta”. Takie stwierdzenie ustawi od razu cały kontekst dyskusji.
Dlatego:
Wybierz MODUŁY, które przekazują pewną historię o sys-
temie i zawierają spójny zestaw pojęć. Takie podejście często skut-
kuje słabym związaniem pomiędzy MODUŁAMI. Jeżeli jednak
tak się nie stanie, poszukaj sposobów na zmianę modelu w taki
699b15244ae879c728b313782703a4d7
6
sposób, aby rozdzielić pojęcia, lub też poszukaj przeoczonego
zagadnienia, które mogłoby stanowić podstawę MODUŁU w istotny
sposób łączącego ze sobą jego elementy. Poszukaj słabego zwią-
zania, biorąc pod uwagę pojęcia, które mogą być zrozumiane lub
uzasadnione niezależnie od siebie. Modyfikuj model, dopóki nie
będzie on, wraz z odpowiadającym mu kodem, podzielony w spo-
sób zgodny z pojęciami dziedziny wysokiego poziomu.
Nadaj MODUŁOM nazwy, które staną się częścią JĘZYKA
WSZECHOBECNEGO. MODUŁY wraz ze swoimi nazwami
powinny odzwierciedlać wiedzę dziedzinową.
Przyjrzenie się związkom pojęciowym nie stanowi żadnej alterna-
tywy dla użycia miar technicznych. Oba te środki stanowią różne poziomy
tego samego problemu i oba muszą zostać wykonane. Niemniej jednak
rozumowanie poparte modelem zazwyczaj prowadzi do utworzenia roz-
wiązania bardziej przemyślanego niż przypadkowego. A jeżeli gdziekol-
wiek konieczne będzie zastosowanie kompromisu, to lepiej jest zachować
przejrzystość pojęciową, nawet kosztem większej liczby odwołań pomiędzy
MODUŁAMI lub też niezamierzonych efektów podczas wprowadzania
do nich zmian. Programiści będą sobie w stanie z tymi wszystkimi rzeczami
poradzić, pod warunkiem że zrozumieją historię, którą będzie przekazy-
wać im model.
***
699b15244ae879c728b313782703a4d7
6
Niektóre narzędzia programistyczne oraz środowiska programowania
jedynie nasilają ten problem. Niezależnie od wybranej technologii, na
której oparta będzie implementacja, musimy poszukać sposobu na zminima-
lizowanie pracy niezbędnej do refaktoringu MODUŁÓW, a także zmniej-
szenie bałaganu związanego z komunikacją z resztą programistów.
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;
. . .
699b15244ae879c728b313782703a4d7
6
Pułapki tworzenia pakietów
na podstawie wymogów infrastruktury
Bardzo duży wpływ na decyzję o podziale na pakiety mają szkielety tech-
niczne. Niektóre z nich są przydatne, podczas gdy przed innymi powin-
niśmy się bronić.
Przykładem bardzo przydatnego standardu jest wymuszenie ARCHI-
TEKTURY WARSTWOWEJ poprzez umieszczenie kodu infrastruktury
oraz interfejsu użytkownika w oddzielnych grupach pakietów oraz fizyczne
wydzielenie warstwy dziedziny w jej własnym zestawie pakietów.
Z drugiej strony, architektura warstwowa może spowodować frag-
mentację w implementacji obiektów modelu. Niektóre szkielety architek-
tury tworzą warstwy poprzez rozrzucenie odpowiedzialności pojedynczego
obiektu dziedziny pomiędzy wiele różnych obiektów, a następnie ich
umieszczenie w oddzielnych pakietach. I tak na przykład w przypadku
J2EE częstą praktyką jest umieszczenie danych oraz kodu odpowiedzial-
nego za dostęp do nich w tzw. ziarnie encyjnym (ang. entity bean), podczas
gdy związana z tymi danymi logika biznesowa umieszczana jest w ziar-
nie sesji (ang. session bean). Taki podział, oprócz zwiększonej złożoności
implementacji każdego z komponentów, oznacza natychmiastowy zanik
spójności modelu obiektowego. Jednym z najbardziej podstawowych zało-
żeń obiektów jest hermetyzacja danych wraz z operującą na nich logiką.
Tego rodzaju implementacja warstwowości nie jest tragiczna, ponieważ
oba komponenty mogą być wciąż traktowane wspólnie jako implemen-
tacja pojedynczego elementu modelu, jednak — co gorsza — bardzo
często ziarna encyjne oraz sesji są rozdzielone do różnych pakietów.
W takiej sytuacji ponowne złączenie tych obiektów oraz ich rozpatrywanie
jako pojedynczej pojęciowej ENCJI stanowi często zbyt duże wyzwanie.
W ten sposób tracimy połączenie pomiędzy modelem a projektem. Dlatego
najlepszą praktyką będzie wykorzystanie EJB na większym poziomie ziar-
nistości niż obiekty ENCJI, co pozwoli zminimalizować wady podziału na
warstwy. Niemniej jednak podział na warstwy dotyka też bardzo często
bardzo drobnych obiektów.
I tak na przykład podobny problem napotkałem w projekcie, który
był prowadzony w sposób raczej przemyślany. Wszystkie obiekty koncep-
tualne były tam podzielone na cztery warstwy. Za każdym z tych podzia-
łów przemawiało rozsądne uzasadnienie. Pierwsza warstwa odpowiadała
za zachowywanie danych, obsługując mapowanie oraz dostęp do relacyjnej
bazy danych. Następnie istniała warstwa obsługująca wewnętrzne działanie
obiektów we wszystkich możliwych sytuacjach. Nad nią była warstwa
nakładająca funkcjonalność specyficzną dla aplikacji. A czwarta warstwa
699b15244ae879c728b313782703a4d7
6
miała w zamierzeniu stanowić publiczny interfejs, oddzielony od całej
implementacji leżącej pod nią. Cały ten schemat był zbyt skomplikowany,
jednak warstwy były zdefiniowane poprawnie i na pewno istniał porzą-
dek w oddzieleniu zagadnień. Byliśmy w stanie połączyć ze sobą wszystkie
obiekty fizyczne, tworząc jeden obiekt pojęciowy. Sam podział aspektów
aplikacji niekiedy nawet w tym pomagał. W szczególności dysponowanie
wydzieloną warstwą odpowiedzialną za trwałość danych usuwało dużo
zamieszania.
Niemniej jednak na koniec szkielet aplikacji wymagał, aby każda war-
stwa umieszczona była w swoim własnym zestawie pakietów i nazwana
zgodnie z konwencją wskazującą na rodzaj warstwy. To dawało dużo miej-
sca do wykazania się inwencją twórczą podczas podziału kodu. W rezul-
tacie programiści dziedziny unikali tworzenia zbyt wielu MODUŁÓW
(ponieważ każdy musiał być powtórzony czterokrotnie) i prawie nigdy
nie zmieniali już istniejących, ponieważ ilość pracy wymaganej do zmiany
MODUŁU była zaporowa. Co gorsze, odszukanie tych wszystkich danych
i kodu składających się na pojedynczą klasę pojęciową było tak trudne
(dodawszy jeszcze ten pośredni podział na warstwy), że programiści nie
zawracali sobie zbyt mocno głowy myśleniem o modelu. Ostatecznie apli-
kacja została dostarczona, jednak zawierała jedynie anemiczny model dzie-
dziny, który w podstawowym stopniu spełniał wymagania związane
z dostępem do bazy danych, a cała logika aplikacji była wspierana przez
kilka USŁUG. Korzyści, które powinny wynikać z PROJEKTU STERO-
WANEGO MODELEM, były bardzo ograniczone, ponieważ kod nie
odkrywał w sposób przejrzysty modelu i nie pozwalał programiście na
pracę z nim.
Tego rodzaju projekt, oparty na szkielecie aplikacji, próbuje poradzić
sobie z dwoma uzasadnionymi problemami. Jednym z nich jest logiczny
podział obszarów zainteresowań. Jeden obiekt odpowiada za dostęp do
bazy danych, inny za logikę biznesową itd. Taki podział ułatwia zrozu-
mienie działania poszczególnych warstw (na poziomie technicznym),
a także ułatwia ich wymianę. Jednak problem polega na tym, że nie bierze
on pod uwagę kosztu tworzenia aplikacji. Nie jest to książka zajmująca się
projektowaniem szkieletów aplikacyjnych, więc nie będę opisywać alter-
natywnych rozwiązań tego problemu, jednak takie na pewno istnieją.
Nawet jeżeli nie mielibyśmy innego wyjścia, lepiej byłoby poświęcić te
korzyści na rzecz bardziej spójnej warstwy dziedziny.
Innym uzasadnieniem takiego schematu pakietów byłoby rozłożenie
warstw. To mógłby być bardzo mocny argument w przypadku, gdyby
kod rzeczywiście był umieszczany na różnych serwerach, jednak zazwy-
czaj tak się nie dzieje. Dlatego elastyczność jest uwzględniana na wszelki
699b15244ae879c728b313782703a4d7
6
wypadek, gdyby była potrzebna. W projekcie, który ma nadzieję wyko-
rzystać zalety PROJEKTU STEROWANEGO MODELEM, takie poświę-
cenie byłoby zbyt wielkie, chyba że rozwiązywałoby jakiś naglący i ważny
problem.
Bardzo rozbudowane i wynikające z założeń technicznych sche-
maty podziału na pakiety narzucają dwa koszty:
Jeżeli standardy podziału na partycje używane przez dany szkielet
aplikacji rozrzucą elementy implementujące ten sam obiekt poję-
ciowy, wtedy kod nie będzie już dłużej odzwierciedlał modelu.
Korzyści z partycjonowania są istotne, dopóki umysł jest wciąż
w stanie poskładać części razem. Jeżeli dany szkielet aplikacji nad-
używa dostępnych możliwości, programiści dziedziny tracą zdolność
do podziału modelu na istotne fragmenty.
Najlepiej jest zachować jak największą prostotę. Wybierz minimalny
zestaw technicznych reguł partycjonowania istotnych dla środowiska,
w którym istnieje aplikacja, lub rzeczywiście wspomagających proces pro-
gramowania. Na przykład oddzielenie złożonego kodu odpowiedzialnego
za zachowanie danych od aspektów związanych z logiką obiektów może
uprościć późniejszy refaktoring.
O ile nie ma rzeczywistych zamiarów rozmieszczenia kodu
na różnych serwerach, staraj się trzymać razem cały kod, który
implementuje pojedynczy obiekt pojęciowy, w tym samym
MODULE, a najlepiej w tym samym obiekcie.
Do takiego samego wniosku moglibyśmy dojść, korzystając ze starego
standardu „duża spójność/słabe związanie”. Związki pomiędzy „obiektem”
implementującym logikę biznesową a tym odpowiedzialnym za dostęp do
bazy danych są tak rozległe, że ich związanie jest bardzo silne.
Istnieją też inne pułapki wynikające z sytuacji, w której projekt szkie-
letu aplikacji lub nawet same standardy stosowane w firmie lub projekcie
mogą osłabić korzyści wynikające z PROJEKTU STEROWANEGO
MODELEM poprzez zagmatwanie naturalnej spójności obiektów dzie-
dziny. Niemniej jednak żelazna zasada jest tylko jedna. Wszystkie na-
rzucone ograniczenia lub nawet najzwyczajniej duża liczba wymaga-
nych pakietów wykluczają wykorzystanie innych schematów podziału na
pakiety, lepiej dostosowanych do potrzeb modelu dziedziny.
Wykorzystuj podział na pakiety, aby oddzielić warstwę dzie-
dziny od reszty kodu. W przeciwnym razie zostaw jak najwięcej
swobody programistom odpowiedzialnym za dziedzinę, aby mogli
oni opakować obiekty dziedziny w sposób, który będzie wspierać
ich wybory dotyczące modelu i projektu.
699b15244ae879c728b313782703a4d7
6
Jedyny wyjątek pojawia się w sytuacji, gdy kod jest generowany na
podstawie projektu deklaratywnego (opisanego w rozdziale 10.). W takim
przypadku programiści nie muszą czytać kodu i najlepiej umieścić go
w oddzielnym pakiecie, aby nie przeszkadzał w pracy z innymi elemen-
tami projektu, rzeczywiście używanymi przez programistów.
699b15244ae879c728b313782703a4d7
6
Paradygmaty modelowania
PROJEKT STEROWANY MODELEM wymaga technologii implemen-
tacji spójnej z danym paradygmatem modelowania stosowanym w tym
projekcie. Dotychczas eksperymentowano z wieloma takimi paradygma-
tami, chociaż jedynie niewiele z nich zyskało popularność w praktyce.
W chwili obecnej dominującym paradygmatem jest projektowanie obiek-
towe, a większość obecnie realizowanych złożonych projektów używa
właśnie obiektów. Taka przewaga wynika z wielu kwestii: niektóre z nich są
związane z właściwościami obiektów, niektóre z okolicznościami, a inne
biorą się z zalet, które związane są właśnie z powszechnym zaadaptowa-
niem tego paradygmatu.
699b15244ae879c728b313782703a4d7
6
z biegiem lat rozwiązano już dla obiektów, a powszechne stosowanie tego
paradygmatu zminimalizowało ich znaczenie (to na innych podejściach
spoczywa teraz obowiązek dostosowania się do powszechnie obowiązu-
jącego paradygmatu obiektowego). Większość nowych technologii udo-
stępnia środki umożliwiające ich integrację z popularnymi platformami
obiektowymi. Upraszcza to znacznie integrację, a nawet umożliwia łączenie
podsystemów na podstawie innych paradygmatów modelowania (co omó-
wimy w dalszej części tego rozdziału).
Równie istotna jest dojrzałość środowiska programistów oraz samej kultury
projektowania. Projekt adaptujący nowy paradygmat może nie być w stanie
znaleźć programistów z doświadczeniem w danej technologii lub w two-
rzeniu wydajnych modeli przy użyciu danego paradygmatu. Może też oka-
zać się niemożliwe wykształcenie programistów w rozsądnym czasie,
ponieważ wzorce używania większej części paradygmatu oraz technologii
nie okrzepły jeszcze na dobre. Być może też pionierzy działają wydajnie,
lecz nie opublikowali jeszcze swoich przemyśleń w przystępnej formie.
Obiekty są już poznane przez społeczność tysięcy programistów,
menedżerów projektów, a także wszystkich innych specjalistów związanych
z pracą projektową.
Pewna historia z pracy nad projektem obiektowym nie dalej niż dzie-
sięć lat temu ilustruje ryzyka wynikające z pracy z niedojrzałym paradyg-
matem. Na początku lat 90. projekt zobowiązał się wykorzystywać na
dużą skalę kilka nowatorskich technologii, włączając w to obiektową bazę
danych. Było to niesamowite przeżycie. Członkowie zespołu mogli z dumą
mówić gościom, że tworzą największą bazę danych, jaka była kiedykol-
wiek wspierana przez tę technologię. Kiedy dołączyłem do projektu, różne
zespoły przeciągały projekt, bezskutecznie starając się umieścić swoje
obiekty w bazie danych. Stopniowo też zaczynało do nas docierać, że
większość dostępnych zasobów bazy danych zajmowaliśmy danymi testo-
wymi. Rzeczywista baza danych miałaby rozmiar dziesiątki razy większy,
a liczba transakcji również byłaby dziesiątki razy większa. Czy ta tech-
nologia nie mogła być używana z tą aplikacją? Czy użyliśmy jej w nie-
poprawny sposób? Byliśmy prawie na samym dnie.
Na szczęście, byliśmy w stanie dołączyć do zespołu jednego z naj-
zręczniejszych ludzi na świecie, posiadającego umiejętności, które mogły
wydobyć nas z kłopotów. Podał on swoją cenę, którą oczywiście zapłaci-
liśmy. Były trzy źródła naszego problemu. Po pierwsze, standardowa infra-
struktura dostarczana z bazą danych nie skalowała się zbyt dobrze do naszych
potrzeb. Po drugie, przechowywanie zbyt rozdrobnionych obiektów oka-
zało się bardziej kosztowne, niż sądziliśmy. Po trzecie, części naszego
modelu obiektowego miały tyle wzajemnych zależności, że w przypadku
699b15244ae879c728b313782703a4d7
6
całkiem niewielkiej liczby równoległych transakcji dużym problemem
okazała się rywalizacja o zasoby.
Przy pomocy wynajętego eksperta zwiększyliśmy infrastrukturę,
a zespół — świadomy negatywnego wpływu drobnych obiektów — roz-
począł poszukiwania modeli, które mogłyby lepiej działać z wybraną
technologią. Wszyscy zdaliśmy sobie sprawę z konieczności ograniczenia
w modelu sieci zależności. Nasze nowe zrozumienie przedmiotu zaczę-
liśmy też stosować w praktyce, poszukując lepszych modeli, z większym
rozdzieleniem blisko związanych agregatów.
Na ratowanie projektu poświęciliśmy kilka miesięcy, do których należy
dodać wcześniejsze miesiące spędzone na podążaniu zgodnie z błędnymi
założeniami. I nie było to wcale nasze pierwsze niepowodzenie wynika-
jące z niedojrzałości wybranej technologii oraz naszego własnego braku
doświadczenia w temacie. Niestety, wkrótce potem projekt musiał szukać
oszczędności i stał się całkiem zachowawczy. Wcześniej wybrane egzo-
tyczne technologie są przez zespół projektowy używane do dziś, jednak
tylko dla ostrożnie wybranych aplikacji, z których prawdopodobnie nie
czerpią korzyści.
Dekadę później technologia obiektowa stała się relatywnie dojrzała.
Większość obecnych wymagań w stosunku do infrastruktury może być
spełniona przy użyciu rozwiązań pudełkowych, wykorzystywanych
powszechnie w przemyśle. Niezbędne do działania biznesu systemy
dostarczane są przez największych dostawców oprogramowania, których
jest już często bardzo wielu. Mogą one pochodzić również ze stabilnych
projektów open source. Wiele z tych elementów infrastruktury jest
powszechnie i szeroko stosowanych, co sprawia, że istnieje już grupa
ludzi dobrze je rozumiejących, a także opisujących je książek itp. Dobrze
znane są również ograniczenia technologii, dlatego istnieje mniejsze praw-
dopodobieństwo, że zaznajomione z nimi zespoły przeliczą się ze swoimi
siłami.
Inne interesujące paradygmaty modelowania najzwyczajniej nie mają
takiej dojrzałości. Niektóre z nich są trudne do opanowania i nie będą
nigdy powszechnie używane poza niewielką grupą specjalistów. Inne mają
duży potencjał, lecz ich infrastruktura techniczna jest wciąż pełna błędów
i zawodna i tylko niewielu ludzi rozumie subtelności tworzenia dla nich
dobrych modeli. Oczywiście, mogą z biegiem czasu dojrzeć, ale aktualnie
nie nadają się do większości projektów.
Dlatego też obecnie większość projektów stosujących PROJEKTO-
WANIE STEROWANE MODELEM w charakterze głównej technologii
ich systemu rozsądnie wybiera technologię obiektową. Oczywiście, projekty
takie nie będą ograniczać się jedynie do systemów obiektowych — ponie-
699b15244ae879c728b313782703a4d7
6
waż obiektowość stała się dominującym trendem w przemyśle, powstały
narzędzia integracyjne do połączenia ich z prawie każdą obecnie używaną
inną technologią.
Wszystko to razem nie znaczy, że ludzie powinni ograniczać się na
zawsze tylko do obiektów. Podążanie za tłumem daje poczucie bezpieczeń-
stwa, jednak nie zawsze jest odpowiednią drogą. Modele obiektowe odpo-
wiadają na szereg praktycznych problemów programistycznych, jednak
wciąż istnieją dziedziny, które nie nadają się do zamodelowania jako przeli-
czalny zbiór pakietów z zahermetyzowaną logiką. Na przykład dziedziny
bardzo matematyczne lub zdominowane przez globalne logiczne rozu-
mowanie nie pasują zbyt dobrze do paradygmatu obiektowego.
699b15244ae879c728b313782703a4d7
6
Jest to właśnie jeden z głównych motywatorów integracji w syste-
mie obiektowym takich nieobiektowych komponentów jak silniki reguł
biznesowych (ang. rules engines) lub przepływ zadań (ang. workflow engines).
Łączenie ze sobą paradygmatów pozwala programistom zamodelować
określone zagadnienia w stylu, który do nich najlepiej pasuje. Co więcej,
większość systemów musi używać nieobiektowej infrastruktury technicznej,
najczęściej relacyjnych baz danych. Jednak utworzenie spójnego modelu
używającego wielu paradygmatów jest trudne, a sprawienie, aby narzędzia
je wspierające ze sobą współgrały, jest wręcz skomplikowane. Jeśli pro-
gramiści nie będą widzieć przejrzystego spójnego modelu zawartego
w programie, można zupełnie wyrzucić PROJEKT STEROWANY
MODELEM, nawet jeśli takie wymieszanie paradygmatów będzie zwięk-
szać potrzebę istnienia modelu.
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.
699b15244ae879c728b313782703a4d7
6
Podczas pracy z regułami ważne jest, aby wciąż myśleć w katego-
riach modelu. Zespół musi znaleźć pojedynczy model, który mógłby działać
z oboma paradygmatami implementacyjnymi. Nie jest to proste zadanie,
jednak powinno być wykonalne, jeżeli tylko silnik reguł będzie dopusz-
czać ekspresywną implementację. W przeciwnym razie dane oraz reguły
nie będą połączone. Reguły umieszczone w silniku bardziej przypominają
niewielkie programy niż reguły pojęciowe w modelu dziedziny. Dzięki
ścisłym i jasnym relacjom pomiędzy regułami oraz obiektami znaczenie
obu tych elementów jest zachowane.
Bez jednego spójnego środowiska to na programistach będzie spoczy-
wać obowiązek destylacji modelu złożonego z jasnych podstawowych pojęć,
który mógłby spajać cały projekt.
Najskuteczniejszym narzędziem spajającym razem wszystkie części
jest solidny JĘZYK WSZECHOBECNY, stanowiący podstawę całego
heterogenicznego modelu. Zniwelować różnice pomiędzy nimi może
stałe nadawanie nazw w obu środowiskach i dodawanie ich do JĘZYKA
WSZECHOBECNEGO.
Samo to zadanie zasługuje na osobną książkę. Głównym celem tego
rozdziału jest jedynie pokazanie, że niekoniecznie należy od razu pod-
dawać się i pozbywać PROJEKTU STEROWANEGO MODELEM i że
warto podejmować próby w celu jego utrzymania.
699b15244ae879c728b313782703a4d7
6
UML na przykład posiada funkcjonalności pozwalające na odwzoro-
wanie ograniczeń, jednak nie zawsze są one wystarczające. Niekiedy
lepszym wyjściem od adaptowania na siłę stylu rysowania zapro-
jektowanego do pewnego rodzaju obiektów będzie użycie innego
stylu (być może bardziej pasującego do innych paradygmatów) lub
nawet prostych opisów słownych w języku.
Bądź sceptyczny. Czy narzędzie jest rzeczywiście warte uwagi? Na
przykład fakt, że silnik reguł oferuje pewne reguły, nie oznacza auto-
matycznie, że będą one warte nakładu pracy, który wprowadza jego
implementacja. Reguły mogą być wyrażone w postaci obiektów.
Być może będzie to nieco mniej schludne, lecz i tak użycie wielu
paradygmatów niesamowicie komplikuje sprawy.
699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 6.
Cykl życia
obiektu dziedziny
153
699b15244ae879c728b313782703a4d7
6
Rysunek 6.1.
Cykl życia
obiektu dziedziny
699b15244ae879c728b313782703a4d7
6
AGREGATY
AGREGATY 155
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
utworzony z tego rodzaju pomysłów zawiera zestaw reguł modyfikujących
obiekty oraz ich właścicieli1.
Zacznijmy od zdefiniowania abstrakcji dla hermetyzacji odwołań
w modelu. AGREGAT będzie zbiorem skojarzonych obiektów, które na
potrzeby zmiany danych będziemy traktować łącznie. Każdy AGREGAT
posiada korzeń oraz granicę. Ta ostatnia definiuje wszystko, co znajduje się
wewnątrz AGREGATU. Korzeń jest tylko pojedynczą określoną ENCJĄ
zawartą w AGREGACIE. Będzie ona jedynym elementem AGREGATU,
do którego będą mogły odwoływać się obiekty z zewnątrz. Tym niemniej
obiekty wewnątrz granicy będą mogły mieć referencje do siebie nawza-
jem. ENCJE różne od korzenia będą posiadać lokalną tożsamość, jednak
będzie ona rozróżnialna jedynie wewnątrz AGREGATU, ponieważ nie
będzie mógł jej widzieć żaden zewnętrzny obiekt spoza kontekstu ENCJI
będącej korzeniem.
W programie dla warsztatu samochodowego mógłby zostać zamo-
delowany samochód (ang. car). Jest on ENCJĄ o globalnej tożsamości.
Chcielibyśmy bowiem odróżnić ten samochód od wszystkich innych na
świecie, nawet jeżeli istnieją bardzo podobne do niego. W tym celu mogli-
byśmy użyć numeru identyfikacyjnego nadwozia (ang. vehicle identification
number), stanowiącego unikatowy identyfikator przypisany do każdego
nowego samochodu. Moglibyśmy również chcieć śledzić historię zmian
rozmieszczenia (ang. position) opon (ang. tire) na czterech kołach (ang. wheel).
Co więcej, moglibyśmy chcieć znać przebieg auta (ang. mileage) oraz zużycie
bieżnika na każdej z opon. W tym celu również opony musiałyby być
identyfikowalnymi ENCJAMI. Mało prawdopodobne jednak byłoby,
aby jakikolwiek klient (ang. customer) interesował się tożsamością każdej
z tych opon poza kontekstem tego konkretnego samochodu. Jeżeli wymie-
nimy opony i wyślemy stare do zakładu przetwarzającego odpady, to albo
program przestanie je śledzić, albo też staną się one anonimowymi ele-
mentami na stercie starych opon. Nikt już nie będzie się przejmował ich
historią zmian na kołach. Co więcej, nawet jeżeli ktoś użyje ich w nowym
samochodzie, żaden użytkownik nie będzie odszukiwał tych opon w syste-
mie, aby sprawdzić, w jakim samochodzie obecnie funkcjonują. Już raczej
mógłby on chcieć znaleźć samochód w bazie danych, aby sprawdzić tym-
czasową referencję do obecnie zamontowanych opon. Dlatego też samo-
chód będzie główną ENCJĄ (ang. korzeniem) AGREGATU (ang. Aggregate
Root), w którego granicach znajdować się będą również opony. Z drugiej
1
Ten system odkrył i wykorzystał David Siegel w projektach w latach 90.,
jednak nigdzie go nie opublikował.
AGREGATY 157
699b15244ae879c728b313782703a4d7
6
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ń
699b15244ae879c728b313782703a4d7
6
Rysunek 6.3.
może przekazać kopię WARTOŚCI do innego obiektu i nie przej- Niezmienniki
mować się tym, co się z nim dalej stanie, ponieważ jest to tylko AGREGATU
WARTOŚĆ i nie będzie już dłużej skojarzona z AGREGATEM.
Z poprzednich reguł wynika, że w sposób bezpośredni może być
uzyskany jedynie korzeń AGREGATU poprzez odczyt z bazy danych.
Dostęp do wszystkich innych obiektów musi być uzyskany poprzez
podążanie zgodnie z asocjacjami.
Obiekty wewnątrz AGREGATU mogą przechowywać referencje
do korzeni innych AGREGATÓW.
Operacja usuwania musi za jednym zamachem zniszczyć wszystko
wewnątrz granic AGREGATU (w przypadku automatycznego pro-
cesu czyszczenia pamięci, tj. garbage collector, takie wymaganie jest
proste do spełnienia. Ponieważ nie ma żadnych referencji na zewnątrz
AGREGATU poza korzeniem, należy usunąć ten ostatni, a wszystko
pozostałe zostanie wyczyszczone automatycznie).
W przypadku zatwierdzania zmiany do dowolnego obiektu we-
wnątrz granic AGREGATU muszą zostać spełnione jego wszystkie
niezmienniki.
Zgrupuj ENCJE oraz WARTOŚCI w AGREGATACH i zde-
finiuj wokół nich granice. Wybierz jedną ENCJĘ, pełniącą rolę
korzenia AGREGATU, i kontroluj dostęp do obiektów wewnątrz
AGREGATY 159
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
1. Wymuszanie niezmienników. Podczas dodawania nowego składnika
zamówienia obiekt zamówienia (ang. Purchase Order) sprawdza sumę
i sygnalizuje błąd, gdy cena składnika przekracza ustalony limit. Jak
zobaczymy wkrótce, nie będzie to właściwe zabezpieczenie.
2. Zarządzanie zmianą. W przypadku usunięcia lub zarchiwizowania
zamówienia dotyczyć to będzie również jego składników, jednak
model nie zawiera wskazówki, w którym momencie ten związek
powinien zostać zakończony. Niejasny jest także wpływ zmian cen
(ang. price) artykułów (ang. part) w różnych momentach.
3. Współdzielenie bazy danych. Wielu użytkowników może spowodować
wystąpienie problemów w bazie danych.
Wielu użytkowników równocześnie będzie wprowadzać zamówienia
do systemu oraz je uaktualniać, a to naszym zadaniem będzie zapobiegnięcie
sytuacji, w której będą oni sobie nawzajem przeszkadzać. Zacznijmy od
bardzo prostej strategii, w której każdy obiekt jest blokowany w chwili
rozpoczęcia jego edycji przez użytkownika, aż do chwili, gdy użytkow-
nik zatwierdzi transakcję. Dlatego gdy Jurek rozpocznie edycję wiersza
o numerze 001, Amanda nie będzie już mogła mieć do tego wiersza dostępu.
Będzie natomiast mogła edytować dowolny inny składnik dla dowolnego
innego zamówienia (również inne składniki z zamówienia, nad którym
pracuje Jurek).
Rysunek 6.5.
Początkowy stan
zamówienia (PO)
w bazie danych
AGREGATY 161
699b15244ae879c728b313782703a4d7
6
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)
699b15244ae879c728b313782703a4d7
6
Rysunek 6.8.
Nawet przy zakładaniu wielu małych zamówień zawsze istnieją inne
Zablokowanie
możliwości naruszenia asercji. Rozważmy „Artykuł” (ang. part). Jeżeli ktoś całego
zmieniłby cenę puzonu w chwili, gdy Amanda dodawała go do swojego zamówienia
zamówienia, czy nie naruszyłoby to niezmiennika? pozwala
Spróbujmy zatem dodatkowo, oprócz blokowania zamówienia, nało- na wymuszenie
żyć też blokadę na Artykuł. Oto, co w takim przypadku stanie się w chwili, niezmienników
gdy Jurek, Amanda i Samuel będą pracować nad różnymi zamówieniami.
Rysunek 6.9.
Przesadne
blokowanie
może utrudniać
pracę ludzi
AGREGATY 163
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
Implementacja spójna z tym modelem gwarantowałaby zachowa-
nie niezmiennika związanego z zamówieniem oraz jego składnikami.
Równocześnie zmiany cen artykułów nie byłyby od razu odzwierciedla-
ne w obiektach, które się do nich odwołują. Szersze reguły spójności mo-
głyby zostać wymuszone w inny sposób. Na przykład każdego dnia
system mógłby prezentować użytkownikom listę nieaktualnych cen, tak
aby mogli je uaktualnić lub odrzucić. Nie jest to jednak niezmiennik, który
musi być wymuszony przez cały czas. Osłabiając zależność składników
zamówienia od artykułów, unikamy współzawodnictwa i w lepszy sposób
odzwierciedlamy realia biznesowe. W tym samym momencie wzmoc-
nienie relacji pomiędzy zamówieniem a jego składnikami gwarantuje
przestrzeganie ważnej reguły biznesowej.
AGREGAT narzuca sposób funkcjonowania zamówienia oraz jego
elementów spójny z praktyką biznesową. Tworzenie i usuwanie zamówie-
nia w danym limicie autoryzacji oraz jego składników są ze sobą związane,
natomiast tworzenie i usuwanie artykułów zachodzi niezależnie.
***
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
699b15244ae879c728b313782703a4d7
6
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,
699b15244ae879c728b313782703a4d7
6
jak i człowiek są bardziej skomplikowani od silnika, który składają. Zadanie
montażu części jest całkowicie niezwiązane z zadaniem przekręcenia kor-
bowodu. Montażyści działają jedynie w chwili tworzenia samochodu —
nie potrzebujemy robota lub mechanika, kiedy prowadzimy samochód.
Ponieważ samochody nigdy nie są jednocześnie montowane i prowadzone,
nie ma żadnego zysku z połączenia tych obu funkcji w jednym mecha-
nizmie. Analogicznie składanie złożonego obiektu jest zadaniem, które
najlepiej jest oddzielić od jakiegokolwiek zadania, które będzie jego udzia-
łem po zakończeniu montażu.
Jednak przeniesienie odpowiedzialności na inną zainteresowaną stronę,
tj. klienta w aplikacji, prowadzi do nawet większych problemów. Klient
wie, jakie zadanie musi być wykonane i polega na obiektach dziedziny
wykonujących wymagane obliczenia. Jeżeli od klienta oczekuje się zło-
żenia obiektów dziedziny, których sam potrzebuje, musiałby wiedzieć coś
o ich wewnętrznej budowie. Aby wymusić wszystkie niezmienniki mające
zastosowanie do relacji elementów obiektu dziedziny, klient musiałby rów-
nież posiadać informacje o regułach obiektu. Nawet wywoływanie kon-
struktorów wiąże klienta z konkretnymi klasami obiektów, które tworzy.
Bez zmiany klienta nie będzie mogła być wykonana żadna zmiana do
obiektów dziedziny, co będzie zdecydowanie utrudniać refaktoring.
Klient podejmujący się tworzenia obiektu staje się niepotrzebnie
skomplikowany, co rozmywa jego odpowiedzialność. Co więcej, narusza
hermetyzację tworzonych obiektów dziedziny oraz AGREGATÓW.
A co gorsze, jeżeli klient jest częścią warstwy aplikacji, wtedy te odpo-
wiedzialności wyciekną razem z nim z warstwy dziedziny. Tego rodzaju
mocne związanie aplikacji z niuansami implementacji pozbawia nas więk-
szości zalet zastosowania abstrakcji w warstwie dziedziny i czyni zmiany
jeszcze bardziej kosztownymi.
Tworzenie obiektu może być samo w sobie dużą operacją,
jednak złożone operacje montażu nie należą do odpowiedzialności
tworzonych obiektów. Połączenie tego rodzaju odpowiedzialności
może stworzyć niezgrabne projekty, które trudno będzie później
zrozumieć. Uczynienie klienta bezpośrednim konstruktorem
zaciemnia projekt tego klienta, narusza hermetyzację stworzo-
nego obiektu lub AGREGATU i zbyt mocno wiąże klienta z imple-
mentacją tworzonego obiektu.
Złożona operacja tworzenia obiektów jest odpowiedzialnością war-
stwy dziedziny, chociaż zadanie to nie należy do obiektów wyrażających
model. Istnieje kilka sytuacji, w których tworzenie oraz montaż obiektu
należą do milowych zadań istotnych dla dziedziny, w rodzaju „otwarcia
konta bankowego”. Niemniej jednak zazwyczaj tworzenie oraz montaż
FABRYKI 167
699b15244ae879c728b313782703a4d7
6
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)
699b15244ae879c728b313782703a4d7
6
czy BUDOWNICZY (ang. builder), zostały już dokładnie opisane w książce
Gammy z roku 1995. W tamtej książce wyjaśnione zostały także wzorce
dla najtrudniejszych problemów, które mogą wystąpić w przypadku two-
rzenia obiektów. Tutaj natomiast nie będziemy zagłębiać się znacznie
w tworzenie FABRYK, a raczej wskażemy na ich miejsce jako ważnych
komponentów projektu dziedziny. Poprawne użycie FABRYK może
utrzymać PROJEKT STEROWANY MODELEM na właściwym kursie.
Dwa podstawowe wymagania dotyczące dobrej FABRYKI:
1. Każda metoda tworząca jest atomowa i wymusza wszystkie niezmien-
niki tworzonego obiektu lub AGREGATU. FABRYKA powinna
jedynie móc wyprodukować obiekt w spójnym stanie. Dla ENCJI
oznaczać to będzie utworzenie całego AGREGATU ze spełnionymi
wszystkimi niezmiennikami, jednak prawdopodobnie wciąż będzie
brakować opcjonalnych elementów. Dla niezmiennej WARTOŚCI
będzie to oznaczać inicjalizację wszystkich jej atrybutów przy użyciu
ich poprawnego ostatecznego stanu. Jeżeli interfejs umożliwia zażąda-
nie utworzenia obiektu, którego nie będzie można poprawnie stwo-
rzyć, wtedy powinien zostać wygenerowany wyjątek lub też wywo-
łany równoważny mu mechanizm, uniemożliwiający zwrócenie
niepoprawnej wartości.
2. FABRYKA powinna być abstrakcją oczekiwanego tworzonego typu,
a nie konkretnej klasy (klas). znacznie W tym pomagają zaawansowane
wzorce FABRYK umieszczone w książce Gammy z roku 1995.
FABRYKI 169
699b15244ae879c728b313782703a4d7
6
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.
699b15244ae879c728b313782703a4d7
6
Rysunek 6.14.
FABRYKA jest bardzo ściśle związana ze swoim produktem, dlatego
METODA
też powinna ona być dołączana jedynie do obiektu, który ma z nim bli- WYTWÓRCZA
skie naturalne związki. Jeżeli istnieje coś, co chcielibyśmy ukryć — nie- rozciąga swe
zależnie od tego, czy będzie to jakaś forma implementacji, czy też zwy- działanie
kła złożoność montażu — musimy utworzyć dedykowaną FABRYKĘ na ENCJĘ,
lub USŁUGĘ, chociaż nie będzie wydawać się to naturalnym miejscem. niebędącą częścią
Samodzielna FABRYKA produkuje zazwyczaj cały AGREGAT, przekazu- tego samego
jąc referencję do jego korzenia i zapewniając wymuszenie niezmienników AGREGATU
tego AGREGATU. Jeżeli jakiś element AGREGATU wymaga FABRYKI,
a jego korzeń nie wydaje się odpowiednim miejscem do jej umieszczenia,
wtedy bez wahania należy utworzyć samodzielną FABRYKĘ. Należy jed-
nak przy tym respektować reguły ograniczania dostępu w AGREGACIE
i upewnić się, że produktom spoza AGREGATU przekazywane są jedynie
tymczasowe, ulotne referencje.
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
699b15244ae879c728b313782703a4d7
6
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ę
699b15244ae879c728b313782703a4d7
6
z interfejsem i być oddzielony od implementacji. Wybór klas kolekcji spo-
czywa zazwyczaj na obiekcie, który będzie ją przechowywać, lub na zawie-
rającej go FABRYCE.
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
699b15244ae879c728b313782703a4d7
6
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.
699b15244ae879c728b313782703a4d7
6
Istnieją jeszcze problemy związane z przypisywaniem ENCJI jej toż-
samości, które nie będą mieć przecież znaczenia w przypadku WARTOŚCI.
Jak opisaliśmy to już w rozdziale 5., identyfikator może być przypisany
automatycznie przez programy lub też dostarczony z zewnątrz (zazwyczaj
przez użytkownika). Jeżeli tożsamość klienta musi być śledzona przy użyciu
numeru telefonicznego, numer ten musi być oczywiście przekazany jako
argument do FABRYKI. Natomiast jeżeli to program przypisuje identyfi-
kator, to wtedy również FABRYKA wydaje się dobrym miejscem do kon-
trolowania tego procesu. Chociaż rzeczywisty proces tworzenia unikatowego
identyfikatora jest zazwyczaj wykonywany przez „sekwencję” w bazie
danych lub inny mechanizm infrastrukturalny, to jednak FABRYKA będzie
wiedzieć, kogo i o co spytać i gdzie ten wynik umieścić.
FABRYKI 175
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
nia instancji. Z tej potrzeby wynikać będzie nowa konstrukcja w projek-
cie — FABRYKI. Nie wyrażają one zazwyczaj żadnej części modelu, cho-
ciaż są częścią projektu dziedziny, pomagającą w wyraźnym wyrażaniu
obiektów w modelu.
FABRYKA hermetyzuje przejścia w cyklu życia obiektu obejmujące
jego tworzenie oraz odtwarzanie. Inną zmianą stanów, która narzuca
złożoność techniczną mogącą skomplikować projekt dziedziny, jest przej-
ście do miejsca przechowywania obiektów i z niego. Ta zmiana stanu będzie
odpowiedzialnością innej struktury w projekcie dziedziny — REPO-
ZYTORIUM.
FABRYKI 177
699b15244ae879c728b313782703a4d7
6
REPOZYTORIA
699b15244ae879c728b313782703a4d7
6
wszystkie niezbędne asocjacje. Członkowie zespołu musieli jedynie wystar-
czająco dobrze je przeanalizować, tworząc spójny model całej dziedziny.
To wyznaczone przez nich samych ograniczenie zmusiło ich do utwo-
rzenia nieskończonej sieci powiązań, której staraliśmy się zapobiec przez
kilka poprzednich rozdziałów. Dysponowali też dokładnymi implemen-
tacjami ENCJI oraz stosowali AGREGATY. Nie wytrzymali długo w tej
strategii, jednak nigdy nie zastąpili jej innym spójnym podejściem. Osta-
tecznie stworzyli tymczasowe rozwiązania i stali się mniej ambitni.
O takim podejściu mogłoby pomyśleć tylko niewielu ludzi, a jeszcze
mniejsza ich liczba dałaby mu się skusić, ponieważ ostatecznie większość
obiektów i tak musiałaby zostać zachowana w relacyjnych bazach danych.
Taka technologia składowania obiektów czyni naturalnym trzeci sposób
pobierania referencji, tzn. wykonanie zapytania w celu odnalezienia obiektu
w bazie danych na podstawie jego atrybutów, odnalezienie wszystkich
elementów obiektu, a następnie jego odtworzenie.
Zapytanie do bazy danych jest dostępne globalnie i umożliwia bez-
pośrednie odczytanie dowolnego obiektu. Nie istnieje dłużej potrzeba, aby
wszystkie obiekty były ze sobą powiązane, co czyni sieć obiektów znacz-
nie prostszą w utrzymaniu. Niezależnie od tego, która metoda zostanie
zastosowana, stanie się ona decyzją projektową, która będzie musiała pro-
wadzić do kompromisu pomiędzy wydzieleniem niezależnego zapytania
do bazy danych a spójnością asocjacji. Czy obiekt klienta Customer powi-
nien przechowywać kolekcję złożonych zamówień (ang. Orders)? Czy to
jednak obiekty Orders powinny być odczytywane z bazy danych przy uży-
ciu identyfikatora klienta? Poprawna kombinacja zapytań oraz asocjacji
czyni projekt zrozumiałym.
Niestety, programiści zazwyczaj nie zastanawiają się zbyt długo nad
tego rodzaju subtelnościami, ponieważ mają na głowie konieczność użycia
wielu mechanizmów pozwalających na zachowanie obiektu i jego odtwo-
rzenie, a na końcu całkowite usunięcie z miejsca, gdzie jest przechowywany.
Z technicznego punktu widzenia odczytywanie zachowanego obiektu
stanowi tylko podzbiór czynności wymaganych do jego utworzenia, ponie-
waż dane pochodzące z bazy danych są wykorzystywane do utworzenia
nowych obiektów. W rzeczywistości kod, który musi zostać stworzony,
bardzo utrudnia zapomnienie o tej zależności. Jednak patrząc na to logicz-
nie, jest to środek cyklu życia ENCJI. Obiekt Customer nie reprezentuje
nowego klienta jedynie dlatego, że został zachowany w bazie danych,
a następnie z niej odczytany. Aby wciąż o tym lepiej pamiętać, tworzenie
instancji z zachowanych danych określam mianem odtwarzania.
Celem projektowania sterowanego dziedziną jest utworzenie lepszego
oprogramowania dzięki skoncentrowaniu się na modelu dziedziny, a nie
REPOZYTORIA 179
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
musimy zajmować się obiektami tymczasowymi. Tego rodzaju obiekty
(zazwyczaj WARTOŚCI) żyją krótko, ponieważ są używane w operacjach
klienta, który je tworzy, a następnie usuwa. Nie musimy też zawracać
sobie głowy budowaniem zapytań dla obiektów trwałych, które najłatwiej
odszukać poprzez użycie asocjacji. Na przykład adres osoby mógłby zostać
odczytany z obiektu Person. A co najważniejsze, do żadnego obiektu będącego
wewnątrz AGREGATU nie można odwołać się w sposób inny niż przez rozpo-
częcie poszukiwania od korzenia.
Trwałe WARTOŚCI są zazwyczaj znajdowane poprzez przeszukanie
rozpoczęte od ENCJI działającej w charakterze korzenia AGREGATU
hermetyzującego te wartości. W rzeczywistości tworzenie globalnej metody
dostępu do WARTOŚCI jest zazwyczaj bez znaczenia, ponieważ operacja
ich odszukania na podstawie ich atrybutów byłaby równoważna użyciu
tych atrybutów do utworzenia nowej instancji tej WARTOŚCI. Istnieją
jednak od tego wszystkiego wyjątki. Jeżeli na przykład planuję w inter-
necie podróż, bardzo często zachowuję kilka obiecujących planów podróży
i wracam do nich później, aby dokonać rezerwacji. Te plany są WAR-
TOŚCIAMI (gdyby dwa plany były złożone z tych samych lotów, nie
przejmowałbym się, który jest którym), jednak zostały one zachowane
przy użyciu mojego nazwiska i w takim samym nienaruszonym stanie
później mi zwrócone. Innym przykładem byłoby „wyliczenie”, w którym
reprezentujący je typ dysponuje ściśle ograniczonym, zdefiniowanym
wcześniej zestawem dopuszczalnych wartości. Globalny dostęp do WAR-
TOŚCI jest jednak znacznie rzadszy od tego dla ENCJI. Jeżeli odkryjesz,
że musisz odszukać pewne istniejące wcześniej WARTOŚCI w bazie
danych, warto, abyś się zastanowił, czy w rzeczywistości nie jest to ENCJA,
której tożsamość nie została jeszcze odkryta.
Z tej całej dyskusji wynika wniosek, że większość obiektów nie
powinna być odszukiwana globalnie. Byłoby przyjemnie, gdyby ten sam
wniosek wynikał z projektu.
W tym momencie problem może być zdefiniowany na nowo, w spo-
sób bardziej precyzyjny.
Podzbiór trwałych obiektów musi być dostępny globalnie
poprzez mechanizm wyszukiwania oparty na atrybutach obiektu.
Taki mechanizm jest wymagany dla korzeni AGREGATU, które
nie jest łatwo odszukać w inny sposób. Są one zazwyczaj ENCJAMI,
niekiedy WARTOŚCIAMI ze złożoną strukturą wewnętrzną,
innym zaś razem WARTOŚCIAMI wyliczeniowymi. Umożliwienie
dostępu do innych obiektów zaciemnia ważne rozróżnienia w mo-
delu. Korzystanie z zapytań do bazy danych w dowolny sposób
może w rzeczywistości naruszyć hermetyzację obiektów dziedziny
REPOZYTORIA 181
699b15244ae879c728b313782703a4d7
6
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ą.
699b15244ae879c728b313782703a4d7
6
Dlatego: Rysunek 6.18.
Dla każdego rodzaju obiektu wymagającego dostępu global- REPOZYTORIUM
nego utwórz obiekt, który będzie udawać kolekcję wszystkich wykonujące
operację odszukania
obiektów tego rodzaju przechowywaną w pamięci. Skonfiguruj
obiektu dla klienta
dostęp poprzez dobrze znany interfejs globalny. Udostępnij metody
służące do dodawania i usuwania obiektów, hermetyzujące wła-
ściwe operacje wstawiania danych do zbioru danych i usuwania
z niego. Zdefiniuj metody wybierające obiekty na podstawie okre-
ślonych kryteriów i zwracające w pełni utworzone instancje obiek-
tów lub ich kolekcje, których atrybuty je spełniają. W ten sposób
utwórz hermetyzację rzeczywistego magazynu danych oraz tech-
nologii zapytań. Pozwalaj na używanie REPOZYTORIÓW jedy-
nie korzeniom AGREGATÓW, które potrzebują bezpośredniego
dostępu. Postaraj się utrzymać klienta skoncentrowanego na mo-
delu, delegując wszystkie kwestie przechowywania i dostępu do
obiektów do REPOZYTORIÓW.
***
REPOZYTORIA mają wiele zalet, między innymi:
Udostępniają klientom prosty model, umożliwiający otrzymanie
trwałych obiektów oraz zarządzanie ich cyklem życia.
Rozdzielają projekt aplikacji i dziedziny od technologii zapewniającej
trwałość, od wielu różnych strategii bazodanowych, a nawet od wielu
źródeł danych.
Komunikują decyzje projektowe dotyczące dostępu do obiektów.
Umożliwiają proste zastępowanie początkowych implementacji w celu
zastosowania w testowaniu (takie implementacje zazwyczaj używają
kolekcji umieszczonych w pamięci).
REPOZYTORIA 183
699b15244ae879c728b313782703a4d7
6
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.
699b15244ae879c728b313782703a4d7
6
Rysunek 6.20.
Zapytania oparte na SPECYFIKACJI są elegancje i elastyczne.
Uniwersalna,
W zależności od dostępnej infrastruktury może to skutkować utworzeniem deklaratywna
szkieletu średnio skomplikowanego lub też trudnego w stopniu niemożli- SPECYFIKACJA
wym do przezwyciężenia. Bardziej szczegółowe omówienie technicznych kryteriów
problemów związanych z projektowaniem tego rodzaju REPOZYTORIÓW wyszukiwania
zawarli w książce Fowlera z roku 2003 Rob Mee oraz Edwart Hieatt. w zaawansowanym
Nawet projekt takiego REPOZYTORIUM z elastycznymi zapyta- REPOZYTORIUM
niami powinien umożliwiać dodanie specjalizowanych, zapisanych na
sztywno zapytań. Mogłyby one hermetyzować zapytania często wykorzy-
stywane lub te, które nie muszą zwracać obiektów, jak chociażby pod-
sumowanie na bazie wyselekcjonowaych obiektów. Szkielety REPO-
ZYTORIÓW, które nie udostępniają takich funkcjonalności, powodują
zazwyczaj zniekształcenie projektu dziedziny i nie są stosowane przez
programistów.
REPOZYTORIA 185
699b15244ae879c728b313782703a4d7
6
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ć
699b15244ae879c728b313782703a4d7
6
Rysunek 6.21.
REPOZYTORIUM
hermetyzuje
wybraną
abstrakcyjną klasą bazową hierarchii. Na przykład klasa TradeOrder, technologię
reprezentująca zlecenie transakcji, mogłaby mieć klasy pochodne dla przechowywania
danych
operacji kupna (BuyOrder) oraz sprzedaży (SellOrder). Typ mógłby
być interfejsem, a klasy je implementujące mogłyby nawet nie być
powiązane. Mógłby też być najzwyczajniej konkretną klasą. Pamiętaj,
że w przypadku takiego polimorfizmu można napotkać na pewne
ograniczenia, spowodowane brakiem jego implementacji w wybra-
nej bazie danych.
Wykorzystaj oddzielenie od klienta. Możesz mieć więcej wolności
w zmianie implementacji REPOZYTORIUM niż w przypadku, gdyby
klient bezpośrednio odwoływał się do mechanizmów przechowywania.
Można to wykorzystać do optymalizacji wydajności, modyfikując
technikę zapytań lub przechowując w pamięci obiekty tymczasowe,
a także dowolnie zmieniając strategie przechowywania w dowol-
nym momencie. Możesz również uprościć testowanie kodu klienta
oraz obiektów dziedziny, tworząc łatwą w użyciu testową strategię
zachowywania danych w pamięci.
Pozostaw kontrolę nad transakcjami klientowi. Chociaż to REPOZYTO-
RIUM będzie wstawiało rekordy do bazy danych oraz je usuwało, to
zazwyczaj nie będzie ono zatwierdzać żadnych zmian. Zatwierdzanie
transakcji po zapisie wydaje się kuszące, ale to klient prawdopodobnie
REPOZYTORIA 187
699b15244ae879c728b313782703a4d7
6
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.).
699b15244ae879c728b313782703a4d7
6
jak entity beans. Jeżeli masz taką wolność wyboru, spróbuj użyć szkieletu
architektury lub jego elementów, które będą harmonizować z wybranym
stylem projektowania.
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
związana z modelem obiektowym znacznie bliżej niż większość innych
komponentów. Baza danych nie tylko współdziała z obiektami, lecz prze-
chowuje trwałą część danych, które te obiekty tworzą. O mapowaniu
obiektów na relacyjne tabele oraz ich efektywnym przechowywaniu
i odczytywaniu napisano już wiele. Ostatnia dyskusja może być na przy-
kład odnaleziona w książce Fowlera z roku 2003. Istnieje również całkiem
duża grupa dopracowanych narzędzi do tworzenia oraz utrzymywania
mapowań pomiędzy tymi dwoma światami. Pomijając zagadnienia tech-
niczne, to niedopasowanie może mieć znaczący wpływ na model obiektowy.
Istnieją trzy powszechnie występujące przypadki:
1. Baza danych jest głównie repozytorium obiektów.
2. Baza danych była zaprojektowana dla innego systemu.
3. Baza danych została zaprojektowana dla danego systemu, jednak funk-
cjonuje w innych rolach niż przechowywanie obiektów.
W sytuacji, gdy schemat bazy danych został utworzony specjalnie do
przechowywania obiektów, warto zaakceptować pewne ograniczenia modelu
tylko po to, aby zachować bardzo proste mapowanie. Bez innych wyma-
gań co do projektu schematu baza danych może zostać zaprojektowana,
aby ułatwić i usprawnić zachowywanie integralności agregatów podczas
dokonywania w nich zmian. Z technicznego punktu widzenia relacyjny
projekt tabeli nie musi odzwierciedlać modelu dziedziny. Narzędzia ma-
pujące są już na tyle zaawansowane, aby zniwelować znaczące różnice.
Problem jednak tkwi w fakcie, iż wiele nakładających się modeli stanowi
po prostu zbyt złożoną konfigurację. Do tego niedopasowania będzie
mieć też zastosowanie wiele argumentów użytych w celu uzasadnienia
PROJEKTU STEROWANEGO MODELEM, jak chociażby unikanie
oddzielania modeli analitycznego i projektowego. Będzie ono również
wymagać pewnych ograniczeń w różnorodności modelu obiektowego,
a także niekiedy dokonania pewnych kompromisów w projekcie bazy
danych (w rodzaju wybiórczej denormalizacji). Inaczej ryzyko stanowić
będzie utrata mocnego związania modelu z implementacją. To podejście
nie będzie wymagać jedynie uproszczonego mapowania jednego obiektu
z jedną tabelą. W zależności od siły narzędzia modelującego mogą być
możliwe również pewne agregacje lub złożenia obiektów. Istotne jest jed-
nak, aby mapowania były przejrzyste, łatwe do zrozumienia podczas analizy
kodu oraz konfiguracji w narzędziu do mapowania.
Jeżeli baza danych jest postrzegana jako magazyn obiektów, nie
pozwól, aby model danych i model obiektowy za bardzo od siebie
odbiegały, niezależnie od siły narzędzia modelującego. Poświęć nieco
699b15244ae879c728b313782703a4d7
6
różnorodności relacji obiektowych, aby nie oddalać się zbytnio od
modelu relacyjnego. Jeżeli pozwoli to uprościć mapowania obiektowe,
zrezygnuj z pewnych formalnych standardów relacyjnych, w rodzaju
normalizacji.
Procesy na zewnątrz systemu obiektowego nie powinny mieć dostępu
do magazynu obiektów. Mogłyby one wtedy naruszyć niezmienniki
wymuszone przez obiekty. Co więcej, umożliwienie im tego dostępu
może zamknąć model danych w taki sposób, że będzie go trudno
zmienić po refaktoringu obiektów.
Z drugiej strony, istnieje wiele przypadków, w których dane pochodzą
ze starych lub zewnętrznych systemów, które nigdy nie były przezna-
czone do przechowywania obiektów. W takiej sytuacji w rzeczywistości
będą współistnieć ze sobą w tym samym systemie dwa modele dziedziny.
Ten problem zostanie opisany szerzej w rozdziale 14., „Utrzymywanie
integralności modelu”. Być może lepiej będzie dostosować się do niejaw-
nego modelu w tym innym systemie lub też zupełnie rozdzielić te dwa
modele.
Innym powodem występowania wyjątków może być wydajność.
Aby rozwiązać problemy z wydajnością systemu, może istnieć koniecz-
ność wprowadzenia dziwnych zmian projektowych.
Jednak najczęściej w przypadku używania relacyjnej bazy danych jako
środka do zachowania dziedziny obiektowej najlepsza będzie prosta bez-
pośredniość modelowania. Wiersz tabeli powinien zawierać obiekt, być
może wraz z jego pomocnikami w formie AGREGATU. Klucze obce
w tabeli powinny być przetłumaczone na referencje do innego obiektu
ENCJI. Występująca niekiedy konieczność odejścia od tego prostego i bez-
pośredniego modelowania nie powinna powodować zupełnej rezygnacji
z prostych mapowań.
JĘZYK WSZECHOBECNY może pomóc zwiększyć wspólne zwią-
zanie obiektów oraz komponentów relacyjnych. Nazwy oraz asocjacje
elementów w obiektach powinny odpowiadać szczegółowo tym w tabe-
lach relacyjnych. Chociaż możliwości niektórych narzędzi modelujących
mogą sprawiać wrażenie, że nie jest to konieczne, to jednak subtelne różni-
ce w relacjach spowodują dużo zamieszania.
Tradycja wykonywania refaktoringu, która zadomowiła się już na
dobre w świecie obiektowym, w rzeczywistości nie dotknęła zbyt mocno
projektów relacyjnych baz danych. Co więcej, poważne problemy podczas
migracji danych skutecznie zniechęcają do częstych zmian. Oczywiście,
699b15244ae879c728b313782703a4d7
6
może to spowolnić refaktoring modelu obiektowego, jednak jeżeli model
obiektowy oraz model bazodanowy zaczną się różnić, przejrzystość
w mapowaniu może zostać szybko utracona.
Ostatecznie istnieją powody, dla których można zdecydować się na
schemat całkiem odmienny od modelu obiektowego, nawet w przypadku,
gdy baza danych projektowana jest specjalnie dla danego systemu. Baza
danych może być również używana przez inne aplikacje, które nie będą
tworzyć obiektów. Wtedy nie będzie ona potrzebować wielu zmian, nawet
gdy zachowanie obiektów będzie się zmieniać i ewoluować w sposób
dynamiczny. Dlatego też luźne związanie tych dwóch elementów jest
bardzo kuszącym wyjściem. Często jest ono nawet stosowane w sposób
niezamierzony w sytuacji, gdy zespołowi nie uda się nadążać za zmia-
nami do bazy danych w stosunku do modelu. Jeżeli decyzja o rozdziele-
niu podjęta zostaje świadomie, może skutkować czystym schematem bazy
danych, zamiast dziwnego i pełnego kompromisów wynikających z koniecz-
ności zachowania zgodności z zeszłorocznym modelem obiektowym.
699b15244ae879c728b313782703a4d7
6
194 ROZDZIAŁ 6. CYKL ŻYCIA OBIEKTU DZIEDZINY
699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 7.
Użycie języka
— przykład
rozszerzony
195
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
„Cel zostanie osiągnięty dzięki serii operacji spedycyjnych (Carrier
Movements), zgodnie ze specyfikacją podróży (Specification)”.
699b15244ae879c728b313782703a4d7
6
Klasa Carrier Movement określa jedną konkretną podróż wyko-
naną przez danego spedytora (Carrier) i może być wykonana ciężarówką
lub statkiem z jednej lokalizacji (Location) do innej. Ładunki mogą być
przemieszczane z jednego miejsca do innego przez spedytora w czasie
trwania jednej lub wielu operacji spedycyjnych (Carrier Movements).
Obiekt Delivery History odzwierciedla wszystko to, co w rzeczy-
wistości stało się z ładunkiem, w przeciwieństwie do obiektu Delivery
Specification, który określa cele. Obiekt klasy Delivery History może
obliczyć bieżącą lokalizację ładunku poprzez analizę ostatniej operacji
załadunku lub rozładunku oraz miejsca przeznaczenia odpowiadającej im
operacji spedycyjnej (Carrier Movement). Skuteczne dostarczenie ładunku
spowoduje powstanie historii podróży (Delivery History) spełniającej
wszystkie cele specyfikacji podróży (Delivery Specification).
Wszystkie pojęcia niezbędne do pracy z opisanymi wcześniej wyma-
ganiami są już obecne w modelu, oczywiście pod warunkiem, że odpo-
wiednie mechanizmy do zachowywania obiektów, ich odszukiwania itp.
są też zdefiniowane w obiektach. Takie zagadnienia implementacyjne nie
są uwzględnione w modelu, lecz muszą zostać przewidziane w projekcie.
Aby móc stanowić podstawę solidnej implementacji, model wciąż
jeszcze wymaga uszczegółowienia oraz wyjaśnienia.
Pamiętaj, że uszczegóławianie modelu, projekt oraz implementacja
powinny zazwyczaj zachodzić w iteracyjnym procesie rozwoju. Jednak
w celu zachowania przejrzystości objaśnień w tym rozdziale zaczniemy
od całkiem dojrzałego modelu, a zmiany w nim będą powodowane głów-
nie przez konieczność połączenia go z praktyczną implementacją, używa-
jącą wzorców dla jego poszczególnych elementów składowych.
W trakcie rozwijania modelu w sposób powodujący lepsze wsparcie
dla projektu powinien być on również zazwyczaj rozwijany, aby odzwier-
ciedlać także lepsze zrozumienie dziedziny. Jednak powtórzmy to jeszcze
raz: ze względu na przejrzystość wyjaśnienia w tym rozdziale zmiany będą
powodowane głównie poprzez konieczność połączenia modelu z prak-
tyczną implementacją używającą wzorców projektowych.
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.
699b15244ae879c728b313782703a4d7
6
Nie wchodząc w szczegółową analizę, możemy zidentyfikować trzy
funkcje aplikacji na poziomie użytkownika, które możemy przyporząd-
kować do trzech klas warstwy aplikacji:
1. Zapytanie śledzące (Tracking Query), które będzie mieć dostęp do
przeszłych oraz obecnych operacji obsługi danego ładunku (Cargo).
2. Aplikacja do rezerwacji (Booking Application), która umożliwia
rejestrację nowego ładunku oraz przygotowanie na niego systemu.
3. Aplikacja do rejestrowania incydentów (Incident Logging Appli-
cation), która może zachować każdą operację obsługi ładunku
(zakładając, że informacja może zostać odnaleziona przez zapytanie
śledzące).
Te klasy aplikacji będą koordynatorami. Nie powinny jednak poszu-
kiwać odpowiedzi na zadawane przez siebie pytania. To będzie zadanie
warstwy dziedziny.
699b15244ae879c728b313782703a4d7
6
on generowany automatycznie, widoczny dla użytkownika i w tym przy-
padku prawdopodobnie przekazywany mu w trakcie rezerwacji.
Handling Event (ang. operacja obsługi)
oraz Carrier Movement (ang. operacja spedycyjna)
Tego rodzaju poszczególne incydenty będą również nas interesować,
ponieważ pozwalają one śledzić wszystko to, co zachodzi w procesie.
Będą one odzwierciedlać rzeczywiste zdarzenia w otoczeniu, które zazwy-
czaj nie są ze sobą wymienne, dlatego będą ENCJAMI. Każda operacja
spedycyjna (Carrier Movement) będzie identyfikowalna po kodzie uzy-
skanym z rozkładu rejsów.
Kolejna dyskusja z ekspertami dziedzinowymi odkrywa, że operacje
obsługi (Handling Event) mogą być jednoznacznie identyfikowane przez
kombinację identyfikatora ładunku (Cargo ID), czasu zakończenia (ang.
completion type) oraz rodzaju operacji (ang. type). Na przykład ten sam ładu-
nek nie może być w tym samym czasie ładowany oraz rozładowywany.
Location (ang. lokalizacja)
Dwa miejsca o tej samej nazwie nie są identyczne. Jako unikalny klucz
mogłaby prawdopodobnie służyć szerokość oraz długość geograficzna,
jednak jej stosowanie nie byłoby raczej zbyt praktyczne, ponieważ te
wielkości nie byłyby przedmiotem zainteresowania większości zadań
systemu, a ich stosowanie byłoby dość skomplikowane. Bardziej prawdo-
podobne jest, że obiekt Location byłby częścią pewnego rodzaju modelu
geograficznego, przedstawiającego miejsca według tras żeglugowych lub
innych specyficznych zainteresowań dziedziny. Dlatego też w tym przy-
padku wystarczy jednoznaczny wewnętrzny identyfikator, generowany
automatycznie.
Delivery History (ang. historia podróży)
Ta klasa jest dość skomplikowana. Historie podróży nie mogą być ze sobą
wymieniane, więc są ENCJAMI. Jednak obiekt klasy Delivery History
jest w relacji jeden do jednego z obiektem klasy Cargo, dlatego nie posiada
własnej tożsamości. Jest ona w zamian pożyczana z ładunku mającego
daną historię podróży. Takie podejście stanie się bardziej oczywiste, gdy
zamodelujemy AGREGATY.
Delivery Specification (ang. specyfikacja podróży)
Chociaż ta klasa reprezentuje cel ładunku, to jednak jej abstrakcja nie
zależy od niego. Raczej wyraża ona hipotetyczny stan pewnej historii
podróży. Ufamy, że historia podróży skojarzona z danym ładunkiem osta-
tecznie spełni również przypisaną do niego specyfikację podróży. Gdybyśmy
699b15244ae879c728b313782703a4d7
6
mieli dwa ładunki zmierzające w to samo miejsce, mogłyby one współ-
dzielić obiekt Delivery Specification, co nie byłoby możliwe w przy-
padku Delivery History, pomimo że obie historie zaczynają się w ten
sam sposób (są puste). Dlatego klasa Delivery Specification reprezen-
tuje WARTOŚĆ.
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
699b15244ae879c728b313782703a4d7
6
Rysunek 7.2.
Niektóre asocjacje
mają ograniczoną
kierunkowość
699b15244ae879c728b313782703a4d7
6
utrzymaniem i ograniczonym narzutem podczas dodawania obiektów
Handling Events. Jeżeli natomiast zapytanie jest wykonywane dość często,
wtedy lepiej jest porzucić to rozwiązanie i zdecydować się na utrzymy-
wanie bezpośredniego wskaźnika. Te wszystkie kompromisy projektowe
muszą równoważyć prostotę implementacji z wydajnością. Model pozo-
staje stale ten sam: zawiera on asocjacje cykliczne i dwukierunkowe.
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.
699b15244ae879c728b313782703a4d7
6
Rysunek 7.3.
Granice
AGREGATU
narzucone
w modelu (uwaga:
ENCJA położona
poza granicą
w konsekwencji
staje się korzeniem
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-
699b15244ae879c728b313782703a4d7
6
sitory). Użytkownik będzie również musiał powiedzieć systemowi, który
ładunek został załadowany, z czego będzie wynikać potrzeba posiadania
repozytorium ładunków (Cargo Repository).
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.
699b15244ae879c728b313782703a4d7
6
Przeglądanie scenariuszy
Aby sprawdzić wzajemnie wszystkie podjęte decyzje, musimy stale prze-
glądać scenariusze i potwierdzać, że możemy efektywnie rozwiązywać pro-
blemy aplikacji.
699b15244ae879c728b313782703a4d7
6
że będą one odgrywać te same role w nowych podróżach. Powin-
niśmy jednak również zachować ostrożność i nie kopiować samych
obiektów klientów (Customer). Musimy na koniec mieć te same
referencje do tych samych obiektów klientów, co w przypadku starego
ładunku, ponieważ są one ENCJAMI poza granicami AGREGATU.
Identyfikator (Tracking ID). Musimy dostarczyć taki sam identy-
fikator, jaki byłby pobrany podczas tworzenia od początku nowego
obiektu Cargo.
Zauważ, że skopiowaliśmy wszystko wewnątrz granic AGREGATU
Cargo, wprowadziliśmy kilka zmian do kopii, jednak nie zmienialiśmy niczego
poza granicami AGREGATU.
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)
699b15244ae879c728b313782703a4d7
6
obejmującego Delivery History. Dlatego nie możemy pozwolić kon-
struktorowi klasy Cargo lub FABRYCE na utworzenie obiektu klasy
Delivery History. To konstruktory tej klasy muszą przyjmować obiekt
klasy Cargo jako swój argument. W rezultacie moglibyśmy otrzymać kod
podobny do poniższego:
public Cargo(String id) {
trackingID = id;
deliveryHistory = new DeliveryHistory(this);
customerRoles = new HashMap();
}
699b15244ae879c728b313782703a4d7
6
METODY WYTWÓRCZEJ dla każdego rodzaju operacji. Metoda ta przyj-
mowałaby jednocześnie wszystkie niezbędne argumenty. Na przykład
„operacja załadunku” mogłaby wymagać operacji spedycyjnej (Carrier
Movement).
public static HandlingEvent newLoading(
Cargo c, CarrierMovement loadedOnto, Date timeStamp) {
HandlingEvent result =
new HandlingEvent(c, LOADING_EVENT, timeStamp);
result.setCarrierMovement(loadedOnto);
return result;
}
699b15244ae879c728b313782703a4d7
6
Rysunek 7.5.
Dodanie obiektu Jak dotąd było kilka kłopotliwych aspektów tego projektu, jednak
Handling Event ogólnie biorąc, działał i dość dobrze odzwierciedlał model. Problemy,
wymaga
które nie wydawały się ważne w momencie rozpoczynania projektu, zaczy-
wstawienia go
nają być jednak frustrujące. Cofnijmy się zatem o krok i wykorzystując
do obiektu
Delivery History doświadczenie zebrane z perspektywy czasu, zmodyfikujmy projekt
w odpowiadający nam sposób.
Konieczność uaktualnienia Delivery History podczas tworzenia
obiektu klasy Handling Event wikła AGREGAT Cargo w transakcje.
Gdyby w tym samym czasie inny użytkownik chciał modyfikować obiekt
Cargo, transakcja Handling Event mogłaby zakończyć się niepowo-
dzeniem lub zostać opóźniona. Wprowadzanie nowej instancji operacji
obsługi jest czynnością operacyjną, która powinna być szybka i prosta,
dlatego istotnym wymogiem aplikacji byłoby dodawanie obiektów Han-
dling Event bez rywalizacji z innymi obiektami. To skłania nas do wyboru
innego projektu.
Zastąpienie w klasie Delivery History kolekcji z obiektami Han-
dling Event zapytaniem umożliwiłoby dodawanie ich bez powodowania
problemów z integralnością poza AGREGATEM. Taka zmiana umożli-
wiłaby wprowadzanie tego rodzaju transakcji bez przeszkadzania sobie
nawzajem. W sytuacji dodawania wielu obiektów klasy Handling Event
oraz relatywnie niewielu zapytań ten projekt mógłby być bardziej wydajny.
W rzeczywistości, jeżeli wybraną technologią przechowywania byłaby
699b15244ae879c728b313782703a4d7
6
relacyjna baza danych, to zapytanie i tak byłoby prawdopodobnie używane
w sposób niejawny do zasymulowania kolekcji. Używanie zapytania zamiast
kolekcji zmniejszyłoby również trudność w utrzymywaniu spójności
w cyklicznych odwołaniach pomiędzy obiektami Cargo oraz Handling
Event.
Aby dodać odpowiedzialność za zapytania, stworzymy teraz REPO-
ZYTORIUM dla obiektów klasy Handling Event. Repozytorium operacji
obsługi (Handling Event Repository) będzie wspierać zapytanie dla
operacji związanych z danym ładunkiem. Dodatkowo REPOZYTORIUM
może udostępnić zapytania zoptymalizowane do wydajnej odpowiedzi na
konkretne pytania. Jeżeli na przykład w obiekcie klasy Delivery History
w celu wywnioskowania obecnego stanu ładunku wykonywane byłoby
bardzo często odszukiwanie operacji załadunku oraz rozładunku, wtedy
zapytanie mogłoby zostać zoptymalizowane do zwracania tylko właści-
wego obiektu operacji obsługi (Handling Event). A gdybyśmy chcieli
mieć zapytanie odnajdujące wszystkie ładunki załadowane w konkretnej
operacji spedycyjnej, moglibyśmy łatwo ją dodać.
Sprawia to, że obiekt klasy Delivery History nie musi mieć trwałej
postaci. W tym momencie nie ma potrzeby, aby się tym zajmować. Gdyby
jednak istniała taka konieczność, w celu odpowiedzi na konkretne pyta-
nie moglibyśmy odtworzyć obiekt klasy Delivery History. Moglibyśmy
to wykonać, ponieważ chociaż ENCJA będzie cyklicznie odtwarzana, to
jednak asocjacja z tym samym ładunkiem (Cargo) utrzyma ciągłość
pomiędzy kolejnymi instancjami obiektu.
Nie będzie już dłużej istnieć konieczność skomplikowanego two-
rzenia oraz utrzymywania cyklicznej referencji. Fabryka ładunku (Cargo
Factory) zostanie uproszczona w ten sposób, aby nie musiała już dłużej
dołączać do nowych obiektów ładunku pustych obiektów klasy Delivery
History. Pozwoli to na znaczne zmniejszenie przestrzeni wymaganej
w bazie danych oraz rzeczywistej liczby trwałych obiektów, co w nie-
których obiektowych bazach danych jest fizycznym ograniczeniem. Jeżeli
w normalnym użytkowaniu aplikacji jej użytkownik rzadko będzie spraw-
dzał stan ładunku do czasu jego dostarczenia, wtedy również będziemy
mogli uniknąć zbędnej pracy.
Z drugiej strony, jeżeli korzystamy z obiektowej bazy danych, spraw-
dzanie asocjacji lub jawnej kolekcji jest prawdopodobnie zdecydowanie
szybsze od zapytania do REPOZYTORIUM. Jeżeli aplikacja będzie często
służyć do sprawdzania pełnej historii podróży, a nie tylko okazjonalnego
odpytywania o ostatnią pozycję ładunku, wtedy kompromis związany
z wydajnością mógłby faworyzować użycie bezpośredniej kolekcji.
699b15244ae879c728b313782703a4d7
6
Rysunek 7.6.
Implementacja
kolekcji Handling
Event z klasy
Delivery History
w postaci zapytania
ułatwia wstawianie
operacji obsługi
i pozbawia rywalizacji
o AGREGAT Cargo
699b15244ae879c728b313782703a4d7
6
MODUŁY w modelu logistyki morskiej
Dotychczas zajmowaliśmy się tak niewieloma obiektami, że ich moduło-
wość nie była problemem. A teraz przyjrzyjmy się nieco większemu frag-
mentowi modelu logistyki (choć wciąż oczywiście uproszczonemu), aby
dostrzec jego organizację w MODUŁY, która może mieć wpływ na model.
Na rysunku 7.7 przedstawiony jest model schludnie podzielony przez
hipotetycznego entuzjastycznego czytelnika książki. Ten diagram będzie
odmianą problemu związanego z podziałem na pakiety sterowane infra-
strukturą, o którym dyskutowaliśmy w rozdziale 5. W tym przypadku
obiekty zostały pogrupowane ze względu na wzorce, które są przez nie
implementowane. W rezultacie obiekty, które razem mają niewiele związ-
ków pojęciowych (mała spójność), są zgrupowane, a asocjacje z koniecz-
ności będą rozrzucone pomiędzy wszystkimi MODUŁAMI (duże zwią-
zanie). Pakiety przekazują pewną opowieść, ale nie będzie to historia
mówiąca o logistyce morskiej, a raczej historia tego, co przeczytał pro-
gramista.
Partycjonowanie z wykorzystaniem wzorca może wydawać się oczy-
wistym błędem, jednak nie jest wcale mniej praktyczne od oddzielania
obiektów trwałych od tymczasowych lub też od jakiegokolwiek meto-
dologicznego schematu, który nie jest osadzony w znaczeniu samych
obiektów.
W zamian powinniśmy szukać spójnych znaczeń i koncentrować się
na tym, co chcielibyśmy zakomunikować innym osobom w projekcie.
Podobnie do decyzji podejmowanych w stosunku do modelu w małej skali
istnieje wiele różnych sposobów wykonania tego podziału. Na rysunku 7.8
przedstawiony jest jeden z nich.
Nazwy MODUŁÓW na rysunku 7.8 wspierają język zespołu. Nasza
firma zajmuje się logistyką morską (ang. shipping) dla klientów (ang. customer),
abyśmy mogli wystawić im faktury (ang. bill). Nasze działy sprzedaży i mar-
ketingu pracują z klientami i podpisują z nimi umowy (ang. agreements).
Dział operacyjny (ang. operations) wykonuje fizycznie zadania logistyczne,
dostarczając ładunek (ang. cargo) do określonego miejsca docelowego (ang.
destination). Pracownicy biurowi zajmują się fakturowaniem klienta, wysyłając
faktury zgodnie ze stawkami zawartymi w umowie z klientem. To jedna
z historii, które mogę opowiedzieć, patrząc na taki zestaw MODUŁÓW.
Taki intuicyjny podział mógłby być oczywiście udoskonalony w kolej-
nych iteracjach lub nawet całkowicie zastąpiony innym. Jednak w tym
momencie wspiera on PROJEKT STEROWANY MODELEM i współ-
pracuje z jego JĘZYKIEM WSZECHOBECNYM.
699b15244ae879c728b313782703a4d7
6
Rysunek 7.7.
Te moduły
nie przekazują
wiedzy dziedzinowej
699b15244ae879c728b313782703a4d7
6
Rysunek 7.8.
MODUŁY oparte
Nowa funkcjonalność na szerokich
pojęciach dziedziny
— sprawdzanie przydziału
Nowa funkcjonalność — sprawdzanie przydziału
Do tej pory zajmowaliśmy się początkowymi wymaganiami oraz modelem.
Teraz zamierzmy dodać pierwszą z nowych funkcjonalności.
Dział sprzedaży wymyślonej firmy logistycznej używa innego opro-
gramowania do zarządzania relacjami z klientem, planowania sprzedaży itd.
Jedna z funkcjonalności wspiera zarządzanie wydajnością, umożliwiając
firmie określenie, jak dużo ładunku danego rodzaju będzie rezerwowane,
na podstawie rodzaju produktów, miejsca nadania i przeznaczenia oraz
dowolnego innego współczynnika, który można podać w charakterze
nazwy kategorii. Wszystko to określa cele sprzedażowe dla każdego rodzaju
produktu w taki sposób, aby bardziej zyskowne rodzaje biznesu nie zostały
przeciążone przez mniej dochodowe ładunki, a jednocześnie aby unikać
rezerwacji zbyt niedoszacowanej (a co z tym związane, niewykorzysta-
nia przestrzeni ładunkowej statków) lub przeszacowanej (powodującej
699b15244ae879c728b313782703a4d7
6
pozostawianie ładunku tak często, że w końcu zacznie mieć to negatywny
wpływ na relacje z klientem). Firma chciałaby teraz, aby ta funkcjonalność
została zintegrowana z systemem rezerwacji.
W chwili wprowadzenia rezerwacji powinno nastąpić sprawdzenie
przydziałów i podjęcie decyzji, czy rezerwacja powinna zostać przyjęta,
czy też nie.
Potrzebne informacje są przechowywane w dwóch miejscach, które
powinny zostać sprawdzone przez aplikację używaną do rezerwacji
(Booking Application), tak aby mogła ona zaakceptować lub odrzucić
żądaną rezerwację. Szkic ogólnego przepływu informacji wygląda podobnie
do tego poniżej:
Rysunek 7.9.
Nasza aplikacja
do rezerwacji
(Booking Application)
musi używać
informacji z systemu
zarządzania sprzedażą
(Sales Management
System) oraz
z naszych własnych
REPOZYTORIÓW
dziedziny
699b15244ae879c728b313782703a4d7
6
sprzedażą”. Jednak w tym momencie stracilibyśmy możliwość użycia
języka do sprowadzenia problemu do postaci bardziej dla nas przydatnej.
W zamian dla każdej funkcji do przydzielania ładunku zdefiniujmy więc
USŁUGĘ, której będziemy potrzebować z drugiego systemu. Te USŁUGI
zaimplementujemy w klasie, której nazwa będzie odzwierciedlać odpowie-
dzialność pełnioną w naszym systemie, tzn. Allocation Checker (klasa
sprawdzająca przypisania).
W przypadku, gdy wymagana będzie jakakolwiek inna integracja
(na przykład użycie bazy danych klientów z systemu zarządzania sprze-
dażą zamiast naszego własnego REPOZYTORIUM klientów), mogłaby
zostać utworzona kolejna klasa tłumacząca z USŁUGAMI wypełniającymi
tę odpowiedzialność. Oczywiście, wciąż mogłoby być przydatne posia-
danie klasy niższego poziomu, jak chociażby interfejsu do systemu zarzą-
dzania sprzedażą (Sales Management System Interface), obsługującej
kwestie techniczne komunikacji z innym programem, jednak nie byłaby
ona odpowiedzialna za dokonywanie tłumaczenia. Co więcej, byłaby ona
ukryta za naszą klasą Allocation Checker, przez co nie pojawiałaby się
w projekcie dziedziny.
699b15244ae879c728b313782703a4d7
6
wymiary wymienione dotychczas dla biznesu logistyki morskiej, jak rów-
nież wymiary czasowe (np. liczba miesięcy do chwili obecnej). Wykorzy-
stanie tego pomysłu w naszym modelu przypisania ładunku uczyni go
bardziej wymownym i uprości cały interfejs. W naszym modelu oraz pro-
jekcie pojawi się nowa klasa o nazwie Enterprise Segment, reprezentu-
jąca dodatkową WARTOŚĆ, która będzie musiała zostać określona dla
każdego ładunku (Cargo).
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
699b15244ae879c728b313782703a4d7
6
2. Nie jest wciąż oczywiste, w jaki sposób aplikacja do rezerwacji określi
segmenty przedsiębiorstwa.
Obie te odpowiedzialności wydają się przynależeć do klasy Allo-
cation Checker. Zmiana jej interfejsu może oddzielić te dwie USŁUGI
i uczynić komunikację bardziej przejrzystą oraz bezpośrednią.
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
699b15244ae879c728b313782703a4d7
6
dwóch komunikatów. Nie ma alternatywy dla drugiej wiadomości, wywo-
łującej metodę systemu zarządzania sprzedażą, mającej na celu przesłanie
odpowiedzi na proste pytanie, czy dany ładunek powinien zostać zaak-
ceptowany. Jednak pierwszy komunikat, określający segment przedsię-
biorstwa dla ładunku, jest oparty na w miarę statycznych danych i logice,
przynajmniej w porównaniu do samej logiki przypisywania ładunku. Jedną
z możliwych alternatyw projektowych byłoby zachowanie tych informacji
w pamięci podręcznej w taki sposób, aby mogły zostać umieszczone gdzieś
na serwerze lokalnym dla Allocation Checker, co o połowę zmniejszyłoby
narzut komunikacyjny. Istnieje jednak cena za tę elastyczną funkcjonal-
ność. Projekt staje się bardziej skomplikowany, a zduplikowane dane muszą
być w jakiś sposób odświeżane. W przypadku jednak, gdy w systemach
rozproszonych wydajność staje się czynnikiem krytycznym, wdrożenie sys-
temu w sposób elastyczny może okazać się ważnym celem projektowym.
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ę.
699b15244ae879c728b313782703a4d7
6
tej pierwszej, co spowodowałoby przeładowanie jej metodami niezbęd-
nymi do obliczania konkretnych segmentów przedsiębiorstwa. Z tego
powodu odpowiedzialność za utworzenie tej właściwości leży poprawnie
w obiekcie, który zna zasady segmentacji, a nie w tym, który posiada dane,
z których te reguły korzystają. Same reguły mogłyby zostać wyodrębnione
do oddzielnego obiektu Strategia, który następnie mógłby zostać przeka-
zany do obiektu klasy Cargo w celu umożliwienia mu utworzenia
segmentu przedsiębiorstwa. W chwili obecnej tego rodzaju rozwiązanie
wykraczałoby zdecydowanie poza nasze wymagania, jednak mogłoby
stanowić dobrą alternatywę dla późniejszego projektu i nie powinno sta-
nowić dla niego destrukcyjnej zmiany.
699b15244ae879c728b313782703a4d7
6
222 ROZDZIAŁ 7. UŻYCIE JĘZYKA — PRZYKŁAD ROZSZERZONY
699b15244ae879c728b313782703a4d7
6
III
Refaktoryzacja
ku głębszemu
zrozumieniu
699b15244ae879c728b313782703a4d7
6
W części II tej książki zajmowaliśmy się podstawami utrzymania powią-
zania pomiędzy modelem a jego implementacją. Wykorzystywanie spraw-
dzonego zestawu elementów składowych wraz ze spójnym językiem nadaje
wysiłkom programistycznym pewną spójność.
Oczywiście, prawdziwym wyzwaniem jest znalezienie wyrazistego
modelu — takiego, który będzie wychwytywać subtelne pojęcia definio-
wane przez ekspertów dziedzinowych oraz będzie mógł stanowić pod-
stawę praktycznego projektu. Ostatecznie będziemy też mieć nadzieję
na utworzenie modelu przekazującego głębokie zrozumienie dziedziny.
W rezultacie wszystkie te działania powinny doprowadzić do utworzenia
programu spójnego z rozumowaniem ekspertów dziedzinowych, a także
w lepszy sposób odpowiadającego potrzebom użytkowników. W tej części
książki wyjaśnimy cel modelowania, opiszemy proces, w którym może on
zostać osiągnięty, a także wyjaśnimy kilka zasad oraz wzorców projekto-
wania, których stosowanie sprawi, iż powstający projekt będzie spełniał
potrzeby aplikacji oraz samych programistów.
Sukces projektowania przydatnych modeli zależy od trzech głównych
czynników:
1. Możliwe jest utworzenie zaawansowanych modeli dziedziny i będzie
to warte wysiłku.
2. Przydatne modele są rzadko uzyskiwane w sposób inny niż poprzez
iteracyjny proces refaktoryzacji, a także ścisłą współpracę ekspertów
dziedzinowych z programistami zainteresowanymi poznaniem szcze-
gółów dziedziny.
3. Poprawne zaimplementowanie oraz efektywne zastosowanie zaawan-
sowanych modeli mogą wymagać również zaawansowanych umie-
jętności projektowych.
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ść.
699b15244ae879c728b313782703a4d7
6
Jednak prawie cała dostępna literatura na temat refaktoringu skupia
się jedynie na mechanicznych zmianach kodu, ułatwiających jego analizę
oraz zwiększających szczegółowość. Idea „refaktoryzacji do wzorców”1
umożliwia przeniesienie procesu refaktoryzacji na wyższy poziom, ponie-
waż pomaga programistom w wyszukiwaniu możliwości zastosowania
w projekcie uznanych wzorców projektowych. Wciąż jednak te wszystkie
wymienione powyżej wysiłki skupiają się na technicznym sposobie zwięk-
szenia jakości projektu.
Innym rodzajem refaktoryzacji, który ma największy wpływ na wyko-
nalność aplikacji, jest refaktoryzacja motywowana zrozumieniem dziedziny
lub zwiększająca przejrzystość odzwierciedlenia modelu w kodzie. Ten
typ refaktoryzacji w żaden sposób nie zastępuje wspomnianej „refakto-
ryzacji do wzorców” czy innych mikrozmian w kodzie, które powinny być
wykonywane w sposób ciągły. Nakłada na nią po prostu nowy poziom —
refaktoryzacji do głębszego modelu. Wykonywanie refaktoryzacji na pod-
stawie zrozumienia dziedziny wymaga często serii mikrorefaktoryzacji
samego kodu, jednak w tym przypadku motywacją nie będzie jedynie stan
tego kodu. Ta mikrorefaktoryzacja będzie raczej pomagać w definiowa-
niu zmian o wygodnym do implementacji rozmiarze, prowadzących do
powstania bardziej wnikliwego modelu. Celem tych zmian jest nie tylko
zrozumienie przez programistę zasady działania kodu, lecz także przeka-
zanie mu informacji o tym, dlaczego on w ten sposób działa lub co będzie
wynikiem tego działania, tak aby ta wiedza mogła być wykorzystana
w bieżącej komunikacji prowadzonej z ekspertami dziedzinowymi.
Katalog umieszczony w książce pt. Refactoring: Improving the Design
of Existing Code (Fowler 1999) pokrywa większość typów tego rodzaju
mikrorefaktoryzacji pojawiających się regularnie w projekcie. Każda z nich
jest motywowana zazwyczaj przez jakiś problem, który może zostać zaob-
serwowany w samym kodzie. Dla kontrastu modele dziedziny w miarę
pojawiania się głębszego jej zrozumienia są transformowane na tyle róż-
nych sposobów, że stworzenie ich wyczerpującego katalogu nie byłoby
możliwe.
Z natury rzeczy modelowanie, podobnie jak każde inne odkrywanie,
nie jest czynnością uporządkowaną. Refaktoryzacja ku głębszemu zrozu-
mieniu powinna podążać w kierunku wskazanym przez odkrywanie dzie-
dziny. Jak omówimy to w rozdziale 11., przydatne mogą również okazać
się udane modele wzorcowe, jednak nie powinny one odwracać naszej
uwagi, redukując zadanie modelowania dziedziny do zestawu narzędzi
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.).
699b15244ae879c728b313782703a4d7
6
lub przepisów. Modelowanie oraz projektowanie wymagają kreatywności.
W kolejnych sześciu rozdziałach zasugerujemy określone sposoby myślenia
o udoskonalaniu modelu dziedziny oraz samego projektu, który ten model
będzie ożywiać.
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.
699b15244ae879c728b313782703a4d7
6
Model dogłębny umożliwia przejrzyste wyrażenie podstawowych zagadnień
ekspertów dziedzinowych oraz ich najbardziej odpowiedniej wiedzy, przy jedno-
czesnym pominięciu powierzchownych aspektów tej dziedziny. W tej definicji nie
wspomniano o abstraktyzacji. Model dogłębny zazwyczaj posiada elementy
abstrakcyjne, jednak równie dobrze może zawierać elementy konkretne,
odpowiadające na żywotne problemy tej dziedziny.
Wielofunkcyjność, prostota oraz moc wyjaśniania dziedziny mogą
iść w parze jedynie z modelem ściśle związanym z tą dziedziną. Jedną
z podstawowych cech takich modeli prawie zawsze jest posługiwanie się
prostym, jednak wciąż często abstrakcyjnym językiem stosowanym przez
ekspertów biznesowych.
699b15244ae879c728b313782703a4d7
6
Proces odkrywania
Aby utworzyć projekt rzeczywiście dobrze odzwierciedlający dany problem,
musimy najpierw dysponować modelem wychwytującym centralne, istotne
pojęcia dziedziny. Opis sposobów aktywnego poszukiwania tych pojęć oraz
wpisywania ich do projektu stanowić będzie przedmiot rozdziału 9.,
„Odkrywanie pojęć niejawnych”.
Model oraz projekt są ze sobą blisko powiązane, dlatego jeżeli kod
jest trudny do refaktoryzacji, proces modelowania musi również ulec
zatrzymaniu. Rozdział 10., „Projekt elastyczny”, zajmuje się tym, w jaki
sposób tworzyć oprogramowanie dla programistów, a nie dla samego siebie,
aby było można je później prosto rozszerzać oraz zmieniać. Równocze-
śnie wykonywane będą dalsze udoskonalenia modelu, które bardzo często
wymagają bardziej zaawansowanych technik projektowania oraz większego
rygoru w definiowaniu modelu.
Odszukanie dobrych sposobów modelowania odkrytych pojęć będzie
zazwyczaj wymagać kreatywności oraz wielu prób i błędów, jednak nie-
kiedy będzie można skorzystać ze wzorców odnalezionych przez inne
osoby. W rozdziałach 11. oraz 12. omówimy zastosowanie wzorców anali-
tycznych oraz wzorców projektowych. Nie stanowią one gotowych roz-
wiązań, jednak mogą być punktem wyjścia w procesie przetwarzania wie-
dzy oraz zawężania obszaru poszukiwań.
Część III zaczniemy jednak od najbardziej fascynującego zdarzenia
w projektowaniu sterowanym dziedziną. Niekiedy w chwili, gdy stworzone
są już podstawy PROJEKTU STEROWANEGO MODELEM oraz odkryte
najbardziej oczywiste pojęcia, dochodzi do przełomu. Pojawia się moż-
liwość przekształcenia programu w coś zdecydowanie bardziej wymow-
nego i uniwersalnego, niż można byłoby tego wcześniej oczekiwać. Może
to oznaczać nowe funkcjonalności lub też jedynie zastąpienie wielkiej ilości
nieelastycznego kodu prostszym i bardziej uniwersalnym sposobem wyra-
żenia głębszego modelu. Chociaż takie momenty przełomowe nie zdarzają
się codziennie, to jednak ich pojawienie się jest tak cenne, że taka moż-
liwość powinna zostać poprawnie rozpoznana i wykorzystana.
W rozdziale 8. opowiedziana jest prawdziwa historia projektu, w któ-
rym refaktoryzacja ku głębszemu zrozumieniu doprowadziła do przełomu.
Tego rodzaju doświadczenia nie można zaplanować. Jednak niezależnie
od tego moment przełomowy stwarza dobry kontekst do myślenia o refak-
toryzacji dziedziny.
699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 8.
Moment
przełomowy
229
699b15244ae879c728b313782703a4d7
6
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).
699b15244ae879c728b313782703a4d7
6
Rysunek 8.1.
Model zakładający
stały udział
pożyczkodawców
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
699b15244ae879c728b313782703a4d7
6
Rysunek 8.2.
W celu rozwiązania
problemów model
ulegał stopniowym odkrycie miało szerokie konsekwencje. Razem z przytakującymi oraz
zmianom. Obiekt entuzjastycznie pomagającymi ekspertami stworzyliśmy na tablicy nowy
Loan Adjustment model. Eksperci dziwili się szczerze, dlaczego zajęło nam to tyle czasu.
(korekta pożyczki) Chociaż szczegóły rozwiązania wciąż jeszcze się nie pojawiły, to jednak
śledzi odchylenia znaliśmy istotną funkcjonalność nowego modelu: udziały w pożyczce
od udziału, na który oraz te w linii kredytowej mogą ulegać zmianom w sposób zupełnie nie-
pożyczkodawca zależny od siebie. Dysponując tą wiedzą, przeszliśmy jeszcze przez wiele
początkowo zgodził
scenariuszy, używając przy tym wizualizacji nowego modelu, przypomi-
się w obiekcie Facility
nających te poniżej.
(linia kredytowa)
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
699b15244ae879c728b313782703a4d7
6
Następnie na rysunku 8.4 pożyczkobiorca pobiera dodatkowych 30
mln PLN, zwiększając tym samym swoją pożyczkę do 80 mln, jednak
znajduje się wciąż poniżej 100 mln dopuszczalnego limitu. W tym mo-
mencie firma B rezygnuje z udziału w wypłacie, pozwalając firmie A na
zwiększenie swojego udziału. Udział w wypłatach odzwierciedla podjęte
decyzje inwestycyjne. W chwili, gdy kwoty wypłat zostaną dodane do
pożyczki, udział w pożyczce nie będzie już więcej proporcjonalny do
udziałów w linii kredytowej. To dość powszechne zjawisko.
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.
699b15244ae879c728b313782703a4d7
6
Rysunek 8.5.
Płatności rat kapitału
są zawsze dzielone
proporcjonalne
do udziałów
w pozostałej
do spłaty kwocie
pożyczki
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
699b15244ae879c728b313782703a4d7
6
Rysunek 8.8.
Model pożyczki
(Loan) oparty
Niemniej jednak problemy zniknęły przede wszystkim dlatego, że
na rozkładzie
nowy model usunął niewłaściwe ograniczenie. Uwolnił udziały w pożyczce udziałów (Share Pie)
od proporcji udziałów w linii kredytowej, utrzymując jednocześnie
poprawne ograniczenia w sumach, opłatach dystrybucyjnych itp. Zarówno
rozkład udziałów (Share Pie), jak i pożyczka (Loan) mogły być zmie-
niane bezpośrednio, dlatego obiekt reprezentujący operacje korygowania
pożyczki (Loan Adjustment) nie był już dłużej wymagany, co usunęło
dużą ilość dedykowanej mu logiki.
W analogiczny sposób zniknął obiekt Loan Investment i w tym
momencie zorientowaliśmy się, że „inwestycja pożyczkowa” nie była okre-
śleniem bankowym. W rzeczywistości eksperci biznesowi wielokrotnie
mówili nam, że nie rozumieją tego określenia. Oczywiście, mieli zaufanie
do naszej wiedzy dotyczącej projektowania oprogramowania, więc zakładali,
że termin ten był przydatny dla projektu technicznego. W rzeczywistości
utworzyliśmy go tylko z powodu naszego niekompletnego zrozumienia
dziedziny.
Nagle, bazując na tym nowym spojrzeniu na dziedzinę, mogliśmy
w sposób relatywnie prosty przejść przez każdy napotkany dotychczas
scenariusz. I było to zdecydowanie łatwiejsze niż wcześniej. A nasze dia-
gramy stały się zrozumiałe dla ekspertów biznesowych, tych samych, którzy wcześniej
żalili się, że były one dla nich „zbyt techniczne”. Nawet w szkicach na tablicy
byliśmy też w stanie zauważyć, że nasze problemy z zaokrągleniami zostały
całkowicie rozwiązane, co pozwoliło usunąć skomplikowany kod, który
je obsługiwał.
Nasz nowy model działał dobrze — bardzo dobrze.
I wszyscy poczuliśmy się źle!
699b15244ae879c728b313782703a4d7
6
Otrzeźwiająca decyzja
Mógłbyś słusznie zakładać, że w tym momencie byliśmy uszczęśliwieni.
Wcale tak nie było. Byliśmy pod silną presją. Projekt był już niebezpiecznie
opóźniony, a naszym dominującym odczuciem było przerażenie.
Duch refaktoryzacji mówi, aby zawsze wykonywać małe kroki, dyspo-
nując stale w pełni działającym systemem. Jednak refaktoryzacja naszego
kodu do nowego modelu wymagałaby zmiany dużej ilości wspierającego
go kodu, a pomiędzy konkretnymi akcjami byłoby bardzo mało przerw
(jeżeli w ogóle jakieś by były). Zauważaliśmy oczywiście pewne drobne
możliwe usprawnienia, jednak żadne z nich nie przybliżało nas nawet do
nowego pomysłu. Moglibyśmy wprawdzie dostać się tam, wykonując
szereg drobnych kroków, jednak wtedy po drodze wyłączone zostałyby
duże części aplikacji. A było to zdecydowanie wcześniej, zanim w tego
rodzaju projektach zaczęły być używane testy automatyczne. Ponieważ
nie mieliśmy żadnych, narażaliśmy się na nieprzewidziane problemy.
Co więcej, cała akcja wymagałaby dodatkowej pracy, a sami byliśmy
już wyczerpani miesiącami nacisków projektowych.
W tym momencie odbyliśmy z naszym kierownikiem projektu roz-
mowę, której nigdy nie zapomnę. Nasz kierownik był osobą inteligentną
oraz odważną. Zadał nam jedynie kilka pytań:
P: Jak długo zajmie wam przywrócenie obecnej funkcjonalności z nowym
projektem?
O: Około trzech tygodni.
P: Czy bez tych zmian możemy zapobiec problemom?
O: Prawdopodobnie tak, jednak nie mamy takiej gwarancji.
P: Czy jeżeli nie wykonamy tych zmian teraz, to będziemy w stanie
pójść dalej z pracami nad nową wersją programu?
O: Bez tej zmiany poruszanie się dalej będzie wolne, a co więcej, wyko-
nanie zmiany będzie dużo trudniejsze po opublikowaniu pierwszej
wersji programu.
P: Czy sądzimy, że jest to właściwa rzecz do zrobienia?
Wiedzieliśmy, że sytuacja polityczna jest niestabilna i będziemy
musieli sobie z nią poradzić. Dodatkowo byliśmy zmęczeni. Ale odpo-
wiedź była pozytywna, ponieważ w dłuższej perspektywie nowy projekt
lepiej pasował do biznesu i stwarzał mniejsze ryzyko.
Kierownik dał nam zielone światło i zapewnił, że zajmie się obsłu-
gą eskalacji. Zawsze miałem niesamowity podziw dla jego zaufania oraz
odwagi, które kryły się za tą decyzją.
699b15244ae879c728b313782703a4d7
6
Sprężyliśmy się zatem znacznie i dokończyliśmy przeróbkę w trzy
tygodnie. Było to bardzo duże zadanie, jednak, o dziwo, przeszło całkiem
gładko.
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
699b15244ae879c728b313782703a4d7
6
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.
699b15244ae879c728b313782703a4d7
6
Rysunek 8.9.
Jak to zdarza się bardzo często w przypadku prawdziwego przełomu Kolejny przełom
prowadzącego do modelu dogłębnego, prostota oraz przejrzystość nowego w modelu, który
nastąpił kilka
projektu, w połączeniu z rozszerzoną komunikacją opartą na nowym
tygodni później.
JĘZYKU WSZECHOBECNYM, doprowadziła do wystąpienia kolejnego
Ograniczenia
momentu przełomowego. w transakcjach
Nasz projekt doznał dużego przyspieszenia na etapie, na którym mogły być wyrażone
większość projektów zatrzymuje się z powodu rozmiaru oraz złożoności w prosty i precyzyjny
utworzonego dotychczas kodu. sposób
699b15244ae879c728b313782703a4d7
6
240 ROZDZIAŁ 8. MOMENT PRZEŁOMOWY
699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 9.
Odkrywanie
pojęć niejawnych
241
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
wyraz ich twarzy ustępuje, gdy użyjesz określonego wyrazu? To są
właśnie wskazówki dotyczące pomysłów, które mogą pozytyw-
nie wzbogacić model.
Nie jest to tylko stara notacja, mówiąca „rzeczowniki są obiektami”.
Wychwycenie nowego słowa początkuje proces, który można kontynuować
w dyskusji lub przetwarzaniu wiedzy, mając na celu wykształcenie czystego,
przydatnego pojęcia. W sytuacji, gdy użytkownicy lub eksperci dziedzinowi
używają słownictwa, którego nie ma w projekcie, będzie to zawsze sygnał
ostrzegawczy. A jeszcze silniejszy sygnał będzie występować wtedy, gdy
zarówno programiści, jak i eksperci dziedzinowi wspólnie będą używać
niewystępujących w projekcie pojęć.
Być może jednak lepiej jest potraktować to jako okazję. JĘZYK
WSZECHOBECNY stworzony jest ze słownictwa, które przenika
rozmowy, dokumenty, diagramy modelu, a nawet sam kod. Jeżeli w pro-
jekcie brakuje danego pojęcia, będzie to okazja do wzbogacenia modelu
oraz projektu poprzez jego dołączenie.
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?
699b15244ae879c728b313782703a4d7
6
Rysunek 9.1.
Programista: Dla każdego etapu podróży zawiera ona identyfikator
ładunku (Cargo_ID), identyfikator podróży (Voyage_ID), port
załadunku (Load_Loc) oraz rozładunku (Unload_Loc).
Ekspert: A gdzie są daty? Dział operacyjny będzie musiał zamawiać obsługę
ładunku na podstawie oczekiwanych czasów jego przybycia.
Programista: No cóż, te daty mogą być pobrane z rozkładu podróży.
Tabela jest przecież znormalizowana.
Ekspert: Oczywiście, normalną rzeczą jest oczekiwanie daty. Pracownicy
działu operacyjnego używają tego rodzaju planów podróży do pla-
nowania nadchodzących zadań obsługi.
Programista: Ach tak... w porządku. Z pewnością będą oni mieć dostęp
do dat. Aplikacja do zarządzania operacjami będzie w stanie podać całą
sekwencję załadunku oraz rozładunku z datą każdej z tych operacji,
czyli — jak byś to prawdopodobnie określił — „plan podróży”
(Itinerary).
Ekspert: W porządku. Główną rzeczą, jakiej potrzebują, jest właśnie
plan podróży. Wiesz, w rzeczywistości aplikacja do rezerwacji dyspo-
nuje opcją wydrukowania planu podróży lub jego wysłania e-mailem
do klienta. Czy mógłbyś to jakoś wykorzystać?
Programista: Sądzę, że jest to tylko raport. Nie będziemy w stanie
oprzeć na nim aplikacji do wsparcia operacji.
[Programista przygląda się uważnie, a następnie radośnie woła].
Programista: Ach, w takim razie plan podróży (Itinerary) jest rzeczy-
wistym połączeniem pomiędzy rezerwacją a czynnością operacyjną.
699b15244ae879c728b313782703a4d7
6
Ekspert: Tak. I dodatkowo również pomiędzy pewnymi relacjami
z klientem.
Programista: [Rysując na tablicy diagram] To co w takim razie powiedział-
byś na coś takiego?
Rysunek 9.2.
699b15244ae879c728b313782703a4d7
6
Rysunek 9.3.
W dalszym etapie programiści dokonali refaktoryzacji kodu w taki
sposób, aby odzwierciedlał on nowy model. W tym celu w ciągu tygo-
dnia w krótkim odstępie czasu wykonali dwie lub trzy refaktoryzacje.
Uproszczeniem raportu z planem podróży w aplikacji do rezerwacji zajęli
się na początku kolejnego tygodnia.
Programiści słuchali eksperta dziedzinowego na tyle uważnie, aby
dostrzec, jak ważne dla niego było pojęcie „planu podróży”. Oczywiście,
wszystkie dane były już wcześniej zebrane, a logika zawarta była niejaw-
nie w raporcie z planem podróży, jednak ujawnienie obiektu Itinerary
jako części modelu otwiera nowe możliwości.
Korzyści wynikające z refaktoryzacji prowadzącej do jawnego obiektu
Itinerary:
1. Bardziej wyraziste zdefiniowanie interfejsu do usługi trasowania
(Routing Service).
2. Oddzielenie usługi trasowania od tabel z bazy danych dla rezerwacji
podróży.
699b15244ae879c728b313782703a4d7
6
3. Wyjaśnienie relacji pomiędzy aplikacją do rezerwacji a aplikacją do
wsparcia operacji (współdzielenie obiektu Itinerary).
4. Zmniejszenie duplikacji, ponieważ obiekt Itinerary tworzy daty
załadunku/rozładunku zarówno dla raportu rezerwacji, jak i aplika-
cji do wsparcia operacji.
5. Usunięcie logiki dziedzinowej z raportu rezerwacji i umieszczenie
go w wydzielonej warstwie dziedziny.
6. Rozszerzenie JĘZYKA WSZECHOBECNEGO, pozwalającego na
bardziej precyzyjne dyskutowanie o modelu oraz projekcie pomię-
dzy programistami a ekspertami dziedzinowymi, a także pomiędzy
samymi programistami.
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
699b15244ae879c728b313782703a4d7
6
stopniowo, funkcjonalność po funkcjonalności. Każdej nocy jeden kom-
ponent był uruchamiany w postaci skryptu wsadowego, obliczając wszystkie
odsetki oraz opłaty dla danego dnia, a następnie zapisując je odpowiednio
w oprogramowaniu księgowym tej firmy.
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ę.
699b15244ae879c728b313782703a4d7
6
Programistka: Przypominam sobie, że kiedyś wyodrębniliśmy kalku-
lator odsetkowy (Interest Calculator) z aktywów (Asset), co nam
znacznie pomogło. Może musimy go dalej podzielić?
Ekspert: W porządku.
Programistka: Myślałam, że może znalazłbyś inny sposób opowiadania
o tym kalkulatorze odsetkowym.
Ekspert: Co masz na myśli?
Programistka: Na przykład śledzimy odsetki wymagane, lecz niezapła-
cone w danym okresie księgowym. Czy masz dla tego jakąś stosowną
nazwę?
Ekspert: Cóż, właściwie to robimy to w nieco inny sposób. Zarobione
odsetki oraz płatności są całkiem rozdzielane i księgowane osobno.
Programistka: Nie potrzebujecie więc tej liczby?
Ekspert: Niekiedy moglibyśmy na nią patrzeć, ale nie jest to nasz zwy-
kły sposób postępowania.
Programistka: W porządku. W takim razie, jeżeli płatności oraz odsetki
są rozłączne, może powinniśmy je w ten sposób zamodelować. Jak
wyglądałoby coś takiego? [szkicując na tablicy]
Rysunek 9.5.
699b15244ae879c728b313782703a4d7
6
Ekspert: Aha. Cóż, kiedy zobaczyłem, że rozdzielasz odsetki i historię
płatności (Payment History), myślałem, że rozdzielasz odsetki,
aby zorganizować je w sposób podobny do historii płatności. Czy
wiesz coś na temat księgowości opartej na zasadzie memoriałowej?
Programistka: Proszę, wyjaśnij.
Ekspert: Każdego dnia lub kiedy wskazuje na to harmonogram, przy-
rost odsetek księgowany jest do danej księgi rachunkowej. Płatności
dodawane są w inny sposób. Ten agregat, który masz w tym miej-
scu, jest nieco dziwny.
Programistka: Mówisz, że gdybyśmy utrzymywali listę „przyrostów”,
mogłyby one być w przypadku takiej konieczności agregowane lub
„księgowane”.
Ekspert: Prawdopodobnie księgowane w dniu wymagalności, lecz rzeczy-
wiście agregowane mogłyby być w dowolnej chwili. Opłaty działają
w ten sam sposób, lecz oczywiście są one księgowane do innej księgi.
Programistka: W rzeczywistości obliczanie odsetek byłoby prostsze, gdyby
było wymagane dla jednego dnia lub okresu. A następnie moglibyśmy
tylko na nich polegać. Co powiesz na to?
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,
699b15244ae879c728b313782703a4d7
6
Rysunek 9.7.
Rysunek 9.8.
Głębszy model
po refaktoryzacji
699b15244ae879c728b313782703a4d7
6
Nowy model ma wiele zalet. Wykonana zmiana:
1. Wzbogaca JĘZYK WSZECHOBECNY pojęciem „sumy przyro-
stowej”.
2. Oddziela sumy przyrostowe od płatności.
3. Przenosi wiedzę dziedzinową (jak chociażby rodzaj księgi, do któ-
rej księgować sumy) ze skryptu do warstwy dziedziny.
4. Łączy ze sobą opłaty z odsetkami w sposób, który pasuje do biznesu
i eliminuje powtórzenia w kodzie.
5. Umożliwia w prosty sposób dodawanie do harmonogramów sum
przyrostowych (Accrual Schedules) nowych rodzajów opłat oraz
odsetek.
W tym przypadku programistka musiała odszukać nowe pojęcia, któ-
rych potrzebowała. Była ona w stanie dostrzec dziwaczność obliczeń odsetek
i zaangażowała się, aby poszukać głębszej odpowiedzi.
Miała też szczęście posiadać inteligentnego i zmotywowanego partnera
w osobie eksperta bankowego. Gdyby miała bardziej pasywne źródło wie-
dzy eksperckiej, prawdopodobnie musiałaby zacząć od pomyłek i zależeć
bardziej od dyskusji z innymi programistami. Postęp byłby wciąż moż-
liwy, lecz wolniejszy.
699b15244ae879c728b313782703a4d7
6
Galileo skonstruował więc eksperyment myślowy. Gdyby jeździec
zrzucił podczas jazdy piłkę, gdzie mogłaby ona upaść? Oczywiście, piłka
poruszałaby się dalej obok jeźdźca, dopóki nie upadłaby na ziemię wprost
pod kopyta konia, zupełnie tak, jakby koń stał w miejscu. Z tego ekspe-
rymentu wywiódł on początkową postać sił bezwładności, rozwiązując
wcześniejszy paradoks i definiując bardziej przydatny model fizyki dyna-
micznej.
W porządku, nasze sprzeczności nie są zazwyczaj tak interesujące,
a ich konsekwencje nie są aż tak znaczące. Wciąż jednak ten sam wzo-
rzec myślowy może często pomóc przebić się przez powierzchowne
warstwy dziedziny problemu do głębszych spostrzeżeń.
Wyjaśnienie wszystkich sprzeczności nie jest praktyczne, a nawet
może być niepożądane (w rozdziale 14. porozmawiamy o tym, w jaki spo-
sób o tym zdecydować i jak zinterpretować wynik). Nawet jeżeli sprzecz-
ności są pozostawione na swoim miejscu, to zastanowienie się nad tym,
w jaki sposób oba stwierdzenia mogą pasować do rzeczywistości, może
być odkrywcze.
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.
699b15244ae879c728b313782703a4d7
6
W zamian udała się ona do księgarni. Po chwili poszukiwań odnalazła
książkę o księgowości, która po pobieżnym przejrzeniu bardzo się jej spodo-
bała. Odkryła w niej cały system dobrze zdefiniowanych pojęć. A jeden
z akapitów szczególnie przykuł jej uwagę:
Księgowość na zasadzie memoriałowej. Ta metoda uznaje przychód
w momencie jego zaksięgowania, nawet gdy kwota nie została jeszcze
zapłacona. Wszystkie wydatki także pojawiają się w momencie ich księ-
gowania, nawet jeżeli nie zostały opłacone lub też ich płatność została
zaplanowana w późniejszym czasie. Wszystkie należności, włączając
w to opłaty skarbowe, będą ukazywane jako wydatki.
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.
699b15244ae879c728b313782703a4d7
6
Kiedy wreszcie programistka miała okazję porozmawiać z ekspertem
dziedzinowym, był on całkiem zaskoczony. Po raz pierwszy programista
wykazał cień zainteresowania przedmiotem jego pracy. Ponieważ eks-
pert miał zupełnie inny zakres obowiązków, nigdy nie miał szansy usiąść
z programistką nad modelem, tak jak w poprzednim scenariuszu. Ponie-
waż jednak wiedza programistki umożliwiła jej zadanie lepszych pytań,
od tej chwili ekspert przysłuchiwał się jej uważnie i zadał sobie dodatkowy
trud, aby odpowiadać właściwie na stawiane przez nią pytania.
699b15244ae879c728b313782703a4d7
6
Wszystkie te zmiany kierunków nie idą na marne. Każda zmiana
dołącza do modelu głębsze spostrzeżenia. Każda refaktoryzacja zwiększa
elastyczność projektu, sprawiając, że będzie on łatwiejszy do zmiany
w przyszłości, gotowy do modyfikacji w wymagającym tego miejscu.
W rzeczywistości nie ma wyboru. Eksperymentowanie jest jedy-
nym sposobem pozwalającym się przekonać, co działa, a co nie. Unikanie
pomyłek w projekcie będzie skutkować jego niższą jakością, ponieważ
będzie on się opierał na mniejszym doświadczeniu. I w prosty sposób
może to trwać dłużej niż seria szybkich eksperymentów.
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) {
699b15244ae879c728b313782703a4d7
6
Rysunek 9.10.
699b15244ae879c728b313782703a4d7
6
skutkować powstaniem metody dłuższej od używającego jej kodu (w tym
przypadku metody pourIn()). W ten sposób kod używający tej metody
pozostaje prosty i koncentruje się na swoim zadaniu, podczas gdy złożo-
ność ograniczenia może ulec zwiększeniu (jeżeli istnieje taka konieczność).
Oddzielna metoda oddaje ograniczeniu przestrzeń, w której może ono
rosnąć, jednakże istnieje wiele przypadków, w których ograniczenie nie
może w prosty sposób pomieścić się w pojedynczej metodzie. A nawet gdy
ta metoda pozostaje prosta, to może ono polegać na informacjach, któ-
rych dany obiekt nie posiada, jako swojej podstawowej odpowiedzialności.
Reguła może najzwyczajniej nie mieć pasującego dla niej miejsca w ist-
niejącym obiekcie.
Oto kilka sygnałów ostrzegawczych mówiących o tym, że ograni-
czenie zniekształca projekt obiektu, którego dotyczy:
1. Interpretacja ograniczenia wymaga danych, które w przeciwnym razie
nie mieściłyby się w definicji obiektu.
2. Powiązane reguły pojawiają się w wielu obiektach, wymuszając dupli-
kacje lub dziedziczenie pomiędzy obiektami, które w przeciwnym
razie nie byłyby ze sobą powiązane.
3. Dużo dyskusji na temat projektu oraz wymagań toczy się wokół
ograniczeń, jednak w implementacji są one ukryte w kodzie proce-
duralnym.
W sytuacji, gdy ograniczenia psują podstawową odpowiedzialność
obiektu lub gdy dane ograniczenie jest w dziedzinie zasadnicze, lecz nie
wynika to z modelu, możesz umieścić je w postaci jawnego obiektu lub
nawet zamodelować w postaci zbioru obiektów oraz ich relacji. (Jedno
ze szczegółowych prawie formalnych opracowań tego tematu znajduje się
w książce: Warmer J. oraz A. Kleppe, The Object Constraint Language: Precise
Modeling with UML, 1999 r.).
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.
699b15244ae879c728b313782703a4d7
6
Rysunek 9.11.
Aby przeanalizować cały kod i jego uzasadnienie, spójrz do przy- Model
kładu „Wydobywanie ukrytego pojęcia” w rozdziale 1. zrefaktoryzowany
w celu
uwidocznienia
Procesy jako obiekty dziedziny polityki
699b15244ae879c728b313782703a4d7
6
Niektóre przydatne kategorie pojęć są znacznie węższe. Ten rozdział
uzupełnimy jeszcze jedną kategorią, zdecydowanie konkretniejszą, choć
wciąż bardzo powszechną. SPECYFIKACJA dostarcza spójny sposób
wyrażenia pewnego rodzaju reguł, uwalniając je od logiki warunkowej
i przedstawiając w sposób jawny w modelu.
SPECYFIKACJA została zdefiniowana przeze mnie razem z Mar-
tinem Fowlerem (Evans oraz Fowler 1997 r.). Prostota tego pojęcia stoi
w sprzeczności z subtelnością implementacji w aplikacji, dlatego w tym
podrozdziale będzie wiele informacji szczegółowych. Jeszcze więcej na
ten temat będziemy dyskutować w rozdziale 10., w którym rozszerzymy ten
wzorzec. Po zapoznaniu się z początkowym wyjaśnieniem wzorca
mógłbyś chcieć przeskoczyć podrozdział „Zastosowanie SPECYFIKACJI
w implementacji”, chyba że rzeczywiście chciałbyś ten wzorzec zasto-
sować w praktyce.
699b15244ae879c728b313782703a4d7
6
SPECYFIKACJA
699b15244ae879c728b313782703a4d7
6
wartość „prawda” lub „fałsz”, które w celu wyrażenia bardziej złożonych
reguł mogą być łączone przy użyciu operatorów w rodzaju „AND”
(logiczne „i”) oraz „OR” (logiczne „lub”). Dzięki tym predykatom mogli-
byśmy zadeklarować reguły w sposób bezpośredni i użyć ich w obiekcie
Invoice. Oczywiście, gdybyśmy tylko używali paradygmatu logicznego.
Widząc to, programiści zaczęli podejmować próby implementacji
reguł logicznych w nomenklaturze obiektowej. Niektóre z tych prób były
bardzo skomplikowane, zaś inne bardzo naiwne. Część była ambitna, inne
stanowiły mniejsze wyzwanie. Niektóre okazały się cenne, inne zostały
odrzucone jako niepomyślne eksperymenty. Kilka prób wykoleiło swoje
projekty. Jedno jest pewne — niezależnie od tego, jak zachęcająca jest sama
idea, pełna implementacja logiki w obiektach jest poważnym przedsięwzię-
ciem (ostatecznie programowanie logiczne jest całym własnym oddziel-
nym paradygmatem modelowania i projektowania).
Bardzo często reguły biznesowe nie pasują wcale do odpo-
wiedzialności żadnej z ENCJI lub WARTOŚCI, a ich różnorod-
ność oraz kombinacje mogą przeciążyć proste znaczenie obiektów
dziedziny. Jednak przeniesienie reguł poza warstwę dziedziny
może nawet pogorszyć sprawę, ponieważ kod dziedziny nie będzie
już dłużej wyrażony w modelu.
Programowanie logiczne udostępnia ideę oddzielnych, moż-
liwych do połączenia obiektów reguł zwanych „predykatami”.
Jednak ich pełna implementacja w obiektach może być kłopo-
tliwa. Dodatkowo nie będzie ona przekazywać zamiarów tak
dobrze, jak bardziej specjalizowane projekty.
Na szczęście, aby skorzystać z ich zalet, nie musimy implementować
całkowicie zasad programowania logicznego. Większość naszych reguł
zalicza się do kilku specjalnych przypadków. Możemy pożyczyć pomysł
predykatów i utworzyć specjalizowane obiekty, które będą zwracać wartość
logiczną. Te metody sprawdzające reguły, które wymkną się spod kontroli,
będą mogły być schludnie opakowane w samodzielne obiekty. Niektóre
małe porównania mogą zostać zrefaktoryzowane do postaci samodzielnych
WARTOŚCI. Tego rodzaju nowy obiekt mógłby sprawdzać wartość innego
obiektu w celu określenia dla niego wartości predykatu.
Rysunek 9.12.
699b15244ae879c728b313782703a4d7
6
Mówiąc inaczej, nowy obiekt będzie specyfikacją. SPECYFIKACJA
wyraża ograniczenie stanu innego obiektu, który może, lecz nie musi
być obecny. Ma ona wiele zastosowań, jednak najbardziej podstawowym jest
wykorzystanie SPECYFIKACJI do testowania dowolnego obiektu w celu
sprawdzenia, czy spełnia on określone kryteria.
Dlatego:
Dla specyficznych zastosowań utwórz przypominające pre-
dykat WARTOŚCI. SPECYFIKACJA jest predykatem sprawdzają-
cym, czy dany obiekt spełnia określone kryteria, czy nie.
Wiele SPECYFIKACJI to proste testy specjalnego przeznaczenia,
podobnie jak w przykładzie z zaległą fakturą. Jeżeli reguły są złożone, ta
idea może być rozszerzona, aby umożliwić łączenie prostych specyfikacji
w sposób podobny do łączenia predykatów za pomocą operatorów logicz-
nych (ta technika zostanie omówiona w kolejnym rozdziale). Podsta-
wowy wzorzec pozostaje niezmieniony i stanowi łącznik pomiędzy pro-
stymi a bardziej skomplikowanymi modelami.
Przykład z zaległą fakturą może być zamodelowany przy użyciu
SPECYFIKACJI definiującej znaczenie określenia „przeterminowana” oraz
umożliwiającej sprawdzenie pod tym kątem każdej faktury.
SPECYFIKACJA utrzymuje regułę w warstwie dziedziny. Ponieważ
reguła jest wciąż pełnoprawnym obiektem, projekt może stanowić bardziej
bezpośrednie odzwierciedlenie modelu. FABRYKA może skonfigurować
SPECYFIKACJĘ, używając w tym celu informacji z innych źródeł, jak
chociażby konta klienta lub bazy danych z polityką firmy. Bezpośrednie
udostępnienie tych źródeł z obiektu Invoice powodowałoby powiązanie
Rysunek 9.13.
Bardziej
rozbudowana reguła
wymagalności,
utworzona w postaci
SPECYFIKACJI
699b15244ae879c728b313782703a4d7
6
obiektów w sposób, który nie wykracza poza związanie wynikające z żąda-
nia płatności (podstawowa odpowiedzialność obiektu Invoice). W takim
przypadku musiała zostać utworzona specyfikacja faktur przeterminowa-
nych, która była używana do testowania pewnych faktur, a następnie usu-
wana, co sprawiło, że konkretna data sprawdzenia była poprawnie zdefi-
niowana — miłe uproszczenie. SPECYFIKACJA może w prosty sposób
uzyskać informacje niezbędne jej do funkcjonowania.
***
Podstawowa idea SPECYFIKACJI jest bardzo prosta i pomaga nam
w myśleniu o problemie modelowania dziedziny. Jednak PROJEKT STE-
ROWANY MODELEM wymaga efektywnej implementacji, która rów-
nież wyraża pojęcie. Aby tego dokonać, musimy zagłębić się nieco bardziej
w sposób zastosowania tego wzorca. Wzorzec dziedziny nie jest jedynie
zgrabnym pomysłem na potrzeby diagramu UML — jest rozwiązaniem
problemu programistycznego zachowującym PROJEKT STEROWANY
MODELEM.
W przypadku, gdy w sposób poprawny zastosujemy ten wzorzec,
możemy natrafić na wiele przemyśleń na temat sposobu podejścia do
całej klasy problemów modelowania dziedziny, a co się z tym wiąże,
możemy skorzystać z lat doświadczeń w wyszukiwaniu efektywnych imple-
mentacji. Wciąż jednak w dyskusji na temat SPECYFIKACJI występuje
wiele szczegółów — wiele alternatyw dla jej funkcjonalności lub podejść
do implementacji. Wzorzec nie jest przepisem. Pozwala on jedynie roz-
począć tworzenie rozwiązania na bazie pewnych doświadczeń, a także
udostępnia język, który może być wykorzystywany do rozmawiania o wyko-
nywanych rzeczach.
Podczas pierwszego czytania możesz pominąć niektóre istotne zagad-
nienia. Później, gdy napotkasz odpowiednią sytuację, możesz wrócić do
sprawy i skorzystać z doświadczenia wychwyconego w szczegółowej dys-
kusji. Na koniec natomiast możesz poszukać rozwiązania problemu.
699b15244ae879c728b313782703a4d7
6
2. W celu wyboru obiektu z kolekcji (jak w przypadku odszukiwania
wymagalnych faktur).
3. W celu utworzenia obiektu spełniającego pewne wymaganie.
Wszystkie powyższe przypadki użycia — walidacja, wybór oraz two-
rzenie w celu spełnienia wymagania — są na tym samym poziomie poję-
ciowym. Bez użycia wzorca w rodzaju SPECYFIKACJI ta sama reguła
może pojawiać się w różnych i prawdopodobnie sprzecznych odmianach.
Spójność pojęciowa może zostać utracona. Zastosowanie wzorca SPECY-
FIKACJI pozwala na użycie spójnego modelu, nawet jeżeli implementacja
może od niego nieco odbiegać.
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
699b15244ae879c728b313782703a4d7
6
gracePeriod);
return currentDate.after(firmDeadline);
}
}
699b15244ae879c728b313782703a4d7
6
W tej sytuacji za pomocą pojedynczej instrukcji w kodzie klient
mógłby uzyskać kolekcję przeterminowanych faktur:
Set delinquentInvoices = invoiceRepository.selectSatisfying(
new DelinquentInvoiceSpecification(currentDate));
699b15244ae879c728b313782703a4d7
6
Rysunek 9.15.
umożliwiają wyrażenie takiego zapytania w kategoriach obiektowych
Interakcja
pomiędzy w modelu, tworząc rzeczywiste zapytania SQL w warstwie infrastruktury.
obiektami To umożliwiłoby nam równoczesne posiadanie ciastka i jego zjedzenie.
REPOSITORY Gdyby jednak nie pomogła nam infrastruktura, moglibyśmy zrefak-
oraz toryzować zapytanie SQL i wydobyć je z obiektów dziedziny poprzez
SPECIFICATION dodanie specjalistycznej metody zapytania w obiekcie Invoice Repo-
sitory. Aby uniknąć konieczności osadzania reguły w REPOZYTO-
RIUM, musimy wyrazić zapytanie w bardziej ogólny sposób — taki,
który nie zawiera reguły, jednak mógłby być umieszczony w takim miejscu,
aby umożliwić jej zadziałanie (w tym przykładzie przez zastosowanie
podwójnego przekazania).
public class InvoiceRepository {
public Set selectWhereGracePeriodPast(Date aDate){
// To nie jest reguła, a raczej specjalizowane zapytanie
String sql = whereGracePeriodPast_SQL(aDate);
ResultSet queryResultSet =
SQLDatabaseInterface.instance().executeQuery(sql);
return buildInvoicesFromResultSet(queryResultSet);
}
699b15244ae879c728b313782703a4d7
6
public Set selectSatisfying(InvoiceSpecification spec) {
return spec.satisfyingElementsFrom(this);
}
}
699b15244ae879c728b313782703a4d7
6
// W tym miejscu podstawowy kod DelinquentInvoiceSpecification
699b15244ae879c728b313782703a4d7
6
dolarów itp. Niemniej jednak niezależnie od tego, jak precyzyjna jest ta
specyfikacja, nie stanowi ona projektu samolotu, a w jeszcze mniejszym
stopniu jest docelowym samolotem. Firma budująca samoloty weźmie
taką specyfikację i stworzy na jej podstawie jeden lub więcej projektów.
Jej konkurenci mogą utworzyć inne projekty, z których większość prawdo-
podobnie będzie spełniać warunki oryginalnej specyfikacji.
Wiele programów komputerowych tworzy różne rzeczy, a te muszą
być również wyspecyfikowane. Kiedy wstawiasz do edytora tekstu rysu-
nek, tekst prawdopodobnie go otoczy. Musisz jedynie określić położenie
rysunku oraz styl otaczania rysunku przez tekst. Następnie sposób docelo-
wego umieszczenia słów na stronie jest definiowany przez program tak,
aby spełniał on zdefiniowaną specyfikację.
Chociaż może nie być to widoczne na pierwszy rzut oka, jest to
dokładnie ta sama idea SPECYFIKACJI, która była już użyta do walidacji
oraz wyboru. Określamy w niej kryteria dla nieistniejącego jeszcze obiektu.
Jednakże implementacja będzie zupełnie inna. Ta SPECYFIKACJA nie
stanowi filtra dla istniejących wcześniej obiektów, jak w zapytaniu. Nie
jest również testem dla istniejącego obiektu, jak w walidacji. W tym
momencie musi zostać utworzony lub zmodyfikowany całkiem nowy
obiekt lub ich zestaw w taki sposób, aby spełniały one SPECYFIKACJĘ.
Bez jej użycia mógłby zostać również stworzony generator, zawierający
procedury lub zestaw instrukcji tworzących potrzebne obiekty. Taki kod
w sposób pośredni definiuje zachowanie generatora.
W zamian interfejs generatora, utworzony w zgodzie z opisującą go
SPECYFIKACJĄ, w sposób jawny ogranicza jego produkty.
Takie podejście ma kilka zalet:
Implementacja generatorów jest oddzielona od ich interfejsu. SPE-
CYFIKACJA deklaruje wymagania dla wyjścia, lecz nie definiuje,
w jaki sposób ten rezultat ma być osiągnięty.
Interfejs komunikuje swoje reguły w sposób bezpośredni, dlatego
programiści wiedzą, czego mogą się spodziewać od generatora, bez
konieczności zrozumienia wszystkich szczegółów jego działania. Jedy-
nym sposobem przewidzenia zachowania generatora zdefiniowanego
w sposób proceduralny jest jego uruchomienie w celu zrozumienia
każdego wiersza kodu.
Interfejs jest bardziej elastyczny lub może być rozszerzony w spo-
sób to umożliwiający, ponieważ definicja zapytania do generatora leży
w rękach klienta, podczas gdy generator jest zobligowany jedynie do
wypełnienia zapisów SPECYFIKACJI.
699b15244ae879c728b313782703a4d7
6
Ostatecznie tego rodzaju interfejs łatwiej jest testować, ponieważ
model zawiera jasny sposób wyrażenia wejścia do generatora, który
jest równocześnie sposobem walidacji wyniku jego działania. To znaczy ta
sama SPECYFIKACJA, która jest przekazywana do interfejsu gene-
ratora w celu nałożenia ograniczeń na jego proces tworzenia, może
być również użyta w roli walidacyjnej (jeżeli implementacja taką
wspiera) w celu potwierdzenia, że utworzony obiekt jest poprawny
(jest to rodzaj ASERCJI omówiony w rozdziale 10.).
Tworzenie na zamówienie może oznaczać utworzenie obiektu od pod-
staw. Jednak może oznaczać też konfigurację istniejących wstępnie obiek-
tów w celu spełnienia SPECYFIKACJI.
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
699b15244ae879c728b313782703a4d7
6
od problemu walidacyjnego. To zmusi nas do jawnego zdefiniowania reguł
oraz umożliwi przetestowanie finalnego rozwiązania.
Każda substancja chemiczna będzie posiadać SPECYFIKACJĘ
kontenera.
Substancja
chemiczna Specyfikacja kontenera
Trotyl Kontener opancerzony
Piach
Próbki biologiczne Nie może współdzielić kontenera z materiałami
wybuchowymi
Amoniak Kontener wentylowany
699b15244ae879c728b313782703a4d7
6
Metoda obiektu Container o nazwie isSafelyPacked() sprawdzi, czy
kontener ma wszystkie cechy określone przez umieszczone w nim obiekty
Chemicals.
boolean isSafelyPacked(){
Iterator it = contents.iterator();
while (it.hasNext()) {
Drum drum = (Drum) it.next();
if (!drum.containerSpecification().isSatisfiedBy(this))
return false;
}
return true;
}
699b15244ae879c728b313782703a4d7
6
Przykład
Działający prototyp pakowacza w magazynie
Stworzenie logiki optymalizacji umożliwiającej działanie programu do
pakowania jest dużym zadaniem. Mały zespół programistów oraz eks-
pertów biznesowych podzielił się zadaniami i zaczął nad nimi pracować,
jednak wciąż jeszcze nawet nie rozpoczął kodowania. Jednocześnie inny
mały zespół tworzy aplikację, która pozwoli użytkownikom wyciągnąć
z bazy danych zapasy magazynowe, przekazać je do obiektu Packer,
a następnie zinterpretować wyniki. Wszyscy usiłują zaprojektować ocze-
kiwany program, jednak w tym czasie mogą pracować jedynie nad stwo-
rzeniem makiety interfejsu użytkownika, a także kodu integracji z bazą
danych. Nie mogą jednak pokazać użytkownikom interfejsu z istotną
logiką, aby uzyskać od nich dobry komentarz. Z tego samego powodu
zespół pracujący nad obiektem Packer także działa w próżni.
Mając do dyspozycji obiekty dziedziny oraz interfejs USŁUGI utwo-
rzony w przykładzie dla programu pakowacza w magazynie, zespół apli-
kacyjny zdał sobie sprawę, że może utworzyć bardzo prostą implemen-
tację obiektu Packer, która pomogłaby kontynuować proces tworzenia
programu, umożliwiając równoległe wykonywanie pewnych czynności oraz
zbieranie komentarzy od użytkowników, które mają znaczenie jedynie
w przypadku w pełni działającego systemu.
public class Container {
private double capacity;
private Set contents; // beczki
699b15244ae879c728b313782703a4d7
6
public void pack(Collection containers, Collection drums)
throws NoAnswerFoundException {
/* Ta metoda, jak napisano, spełnia ASERCJĘ. Jednakże
w sytuacji zwrócenia wyjątku zawartość obiektu
klasy Container może się zmienić
W takim razie odtwarzanie musi być zapewnione
na wyższym poziomie */
Iterator it = drums.iterator();
while (it.hasNext()) {
Drum drum = (Drum) it.next();
Container container =
findContainerFor(containers, drum);
container.add(drum);
}
}
699b15244ae879c728b313782703a4d7
6
napisany w zgodzie z dobrze scharakteryzowanym interfejsem — tym Usuwanie zatorów
samym interfejsem i ASERCJAMI, dla których napisana była pierwotna projektowych przy
użyciu prototypów
aplikacja współpracująca z prototypem. Jeden z zespołów,
Poprawne zaimplementowanie programu zajęło specjalistom od aby posunąć się
optymalizacji algorytmów ponad miesiąc. Oczywiście, korzystali oni do przodu, musi
dodatkowo z uwag od użytkowników prototypu. Podczas programowania czekać na działający
kod od innego zespołu.
wszystkie inne części programu miały z czym współpracować. Aby jednak w pełni
Mamy tu przykład podejścia wykorzystującego „najprostszą rzecz, zintegrować swój kod
która mogłaby prawdopodobnie działać”. Była ona możliwa do utwo- lub uzyskać uwagi
rzenia dzięki zaawansowanemu modelowi. Dzięki niemu możemy mieć od użytkowników,
oba zespoły muszą
funkcjonujący prototyp bardzo zaawansowanego komponentu, używający czekać na siebie
niewielu wierszy łatwego do zrozumienia kodu. Inne podejście, w mniej- nawzajem. Ten rodzaj
szym stopniu STEROWANE MODELEM, byłoby trudniejsze do zro- zależności może być
zumienia, trudniejsze do uaktualnienia (ponieważ obiekt Packer byłby często rozwiązany
przy użyciu prototypu
w większym stopniu związany z resztą projektu), a w tym przypadku STEROWANEGO
stworzenie jego prototypu wymagałoby również większej ilości czasu. MODELEM
utworzonego
dla kluczowego
komponentu,
nawet w sytuacji,
gdy nie spełnia on
wszystkich wymagań.
Jeżeli implementacja
oddzielona
jest od interfejsu,
wtedy dysponowanie
jakąkolwiek działającą
implementacją
pozwala projektowi
na równoległe
wykonywanie
niektórych czynności.
Kiedy nadejdzie
odpowiednia pora,
prototyp może zostać
zastąpiony bardziej
efektywną
implementacją
końcową. Dzięki temu
wszystkie części
systemu dysponują
czymś, czego można
będzie używać
w trakcie
projektowania.
699b15244ae879c728b313782703a4d7
6
278 ROZDZIAŁ 9. ODKRYWANIE POJĘĆ NIEJAWNYCH
699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 10.
Projekt elastyczny
279
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
Programiści spełniają dwie role, z których każda musi być wspierana
przez projekt. Ta sama osoba może równie dobrze grać obie role naraz
(lub nawet je zmieniać), jednak pomimo to jej relacja do kodu będzie
w każdej z nich inna. Jedna z tych funkcji to programista kodu klienc-
kiego, który umieszcza obiekty dziedziny w kodzie docelowej aplikacji lub
innym kodzie warstwy dziedziny, wykorzystując możliwości projektu.
W takim przypadku projekt elastyczny udostępnia głęboki model, przej-
rzyście przedstawiający kryjący się w nim potencjał. Dzięki temu w celu
zaprezentowania szeregu scenariuszy z zakresu dziedziny programista może
w sposób elastyczny wykorzystać minimalny zestaw luźno powiązanych
pojęć. Elementy projektu pasują do siebie w sposób naturalny, dzięki
czemu efekt pracy jest przewidywalny, jasno określony i ma bardzo duże
możliwości.
Równie istotne jest, aby projekt wspierał programistów, którzy zamie-
rzają go zmieniać. Aby być gotowym na zmianę, projekt musi być łatwy
do zrozumienia oraz odkrywać ten sam model leżący u jego podstaw, co
udostępniony wcześniej programiście warstwy klienta. Musi on przede
wszystkim mieścić się w zarysach nakreślonych przez dogłębny model
dziedziny, tak aby wszystkie zmiany modyfikowały go w jego najbardziej
elastycznych miejscach. Także efekty działania kodu muszą być widoczne
w sposób przezroczysty i oczywisty, aby łatwo było przewidzieć konse-
kwencje wprowadzanych zmian.
Wczesne wersje projektu są zazwyczaj mało elastyczne. Wiele z nich
nigdy nie uelastyczni się w czasie jego trwania z powodu ograniczeń czaso-
wych lub budżetowych. W trakcie swojej kariery nigdy nie widziałem
dużego programu, który działałby inaczej. Jednak w sytuacji, gdy złożo-
ność powstrzymuje postęp projektu, dopracowanie najbardziej istotnych
i skomplikowanych jego części, aby wspierały projekt elastyczny, może
zadecydować o tym, czy projekt utknie w nieskończonym procesie uaktu-
alniania starego kodu oraz czy odbije się od granicy wyznaczającej jego
maksymalną złożoność.
Oczywiście, nie ma przepisu na utworzenie tego rodzaju programu,
jednak w tym miejscu zgromadziłem zestaw wzorców, które w moim
przekonaniu w przypadku ich poprawnego dopasowania wspierają tworze-
nie projektu elastycznego. Wszystkie te wzorce oraz przykłady powinny
pomagać w zrozumieniu, czym jest projekt elastyczny i w jaki sposób
zmienia on sposób rozumowania.
699b15244ae879c728b313782703a4d7
6
Rysunek 10.1.
Niektóre
ze wzorców
wspierających
projekt elastyczny
699b15244ae879c728b313782703a4d7
6
INTERFEJSY UJAWNIAJĄCE ZAMIAR
W projektowaniu sterowanym dziedziną chcielibyśmy zajmować się istotną
logiką tej dziedziny. Kod zakładający istnienie jakiejś reguły bez jasnego
jej wyrażenia zmuszać nas będzie do jego analizy krok po kroku. To samo
dotyczy obliczeń, które są wynikiem działania jakiegoś kodu, jednak nie
bardzo wiadomo, jakie były ich założenia. Bez jasnego połączenia z mo-
delem trudno zrozumieć wynik działania kodu i przewidzieć rezultat
ewentualnej zmiany. W poprzednim rozdziale zagłębiliśmy się w zasady
modelowania reguł oraz obliczeń. Implementacja obiektów uwzględniają-
cych te reguły lub wykonujących obliczenia wymaga dobrego zrozumie-
nia ich logiki. Piękno obiektów polega na tym, że mogą w sobie herme-
tyzować wszystkie te rzeczy, umożliwiając utworzenie czystego kodu
klienta, który następnie będzie mógł być interpretowany na podstawie
wysokopoziomowych pojęć.
Jeżeli jednak interfejs nie przekaże programiście kodu klienta wszyst-
kich informacji, których będzie on potrzebować do efektywnego wyko-
rzystania danego obiektu, taki programista i tak będzie musiał zagłębić się
w wewnętrzny kod tego obiektu, aby zrozumieć jego szczegóły. Dokładnie
tę samą czynność będzie musiała wykonać później osoba analizująca kod
klienta, co spowoduje utracenie najważniejszej wartości wynikającej z her-
metyzacji. Zawsze staramy się walczyć z przeciążeniem pojęciowym. Jeżeli
umysł programisty klienta będzie przeciążony szczegółami dotyczącymi
pracy danego komponentu, nie będzie mógł zajmować się niuansami pro-
jektowania samego kodu klienta. Jest to również prawdziwe w przypadku,
gdy ta sama osoba odgrywa obie role, tj. tworzy kod oraz go używa. Nie
musi wprawdzie wtedy poznawać kodu od nowa, jednak istnieje granica
określająca liczbę kwestii, którymi można się jednocześnie zajmować.
Jeżeli w celu użycia komponentu programista musi zaprzątać
sobie głowę jego implementacją, tracimy całą wartość hermety-
zacji. Jeżeli natomiast ktoś inny niż twórca kodu będzie próbo-
wał wywnioskować przeznaczenie obiektu lub operacji na pod-
stawie ich implementacji, wtedy może nieświadomie skorzystać
z funkcjonalności, którą wykonują one jedynie przypadkowo. Jeżeli
nie był to główny zamysł twórcy, kod może działać przez moment,
jednak zarys pojęciowy projektu ulegnie uszkodzeniu, a obaj pro-
gramiści będą przeszkadzać sobie nawzajem.
Aby skorzystać z dobrodziejstw, jakie daje przejrzyste modelowanie
pojęcia w postaci klasy lub metody, musimy tym elementom programu
nadać nazwy, które będą te pojęcia odzwierciedlać. Nazwy klas oraz
metod stanowią wspaniałą możliwość usprawnienia komunikacji pomiędzy
programistami, a także udoskonalenia abstrakcji systemu.
699b15244ae879c728b313782703a4d7
6
Kent Beck napisał kiedyś książkę o komunikowaniu przeznaczenia
metod za pomocą ich nazw składających się na SELEKTOR UJAWNIA-
JĄCY ZAMIAR (Beck 1997). Wszystkie elementy publiczne projektu two-
rzą razem jego interfejs, a nazwa każdego elementu umożliwia ujawnienie
zamiaru tego projektu. Nazwy typów, metod oraz argumentów składają
się łącznie na INTERFEJS UJAWNIAJĄCY ZAMIAR.
Dlatego:
Nazywaj klasy oraz operacje w taki sposób, aby opisywały swój
cel lub działanie bez odwoływania się do środków, których do tego
używają. Uwolni to osobę programującą kod klienta od koniecz-
ności zrozumienia szczegółów wewnętrznych kodu. Wszystkie
te nazwy powinny składać się na JĘZYK WSZECHOBECNY, aby
wszyscy członkowie zespołu mogli szybko domyślić się ich zna-
czenia. Przed utworzeniem kodu napisz najpierw dla niego test,
aby wymusić na sobie sposób myślenia późniejszego użytkownika.
Cały skomplikowany mechanizm powinien być schowany za abstrak-
cyjnymi interfejsami, operującymi opisem celu, a nie prowadzącego do
niego środka.
W publicznych interfejsach dziedziny określ relacje oraz reguły, jed-
nak nie wspominaj o tym, jak są one wymuszane. Opisuj zdarzenia oraz
akcje, jednak nie wspominaj nic o ich implementacji. Sformułuj równa-
nie, jednak pomiń rozwiązującą je metodę numeryczną. Zadaj pytanie,
lecz nie podawaj środków, które przyniosą odpowiedź.
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.
699b15244ae879c728b313782703a4d7
6
public void paint(Paint paint) {
v = v + paint.getV(); // Po zmieszaniu objętość farby jest sumowana
// Pominiętych wiele wierszy skomplikowanej logiki mieszania farb,
// kończącej się przypisaniem nowych wartości do zmiennych r, b oraz y
}
699b15244ae879c728b313782703a4d7
6
Aby napisać test odzwierciedlający sposób, w jaki chcielibyśmy komu-
nikować się z tymi obiektami, musimy poświęcić na to chwilę. Następnie
zrefaktoryzujemy klasę Paint w taki sposób, aby ten test został spełniony.
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.
699b15244ae879c728b313782703a4d7
6
FUNKCJE BEZ EFEKTÓW
UBOCZNYCH
Operacje można ogólnie podzielić na dwie kategorie — polecenia oraz
zapytania. Te ostatnie uzyskują z systemu informacje, najzwyczajniej odwo-
łując się do danych w zmiennych lub wykonując na tych danych obli-
czenia. Polecenia natomiast (często znane lepiej jako modyfikatory) są
operacjami, które wprowadzają w systemie pewne zmiany (na przykład
zmieniając wartość zmiennej). W standardowym języku angielskim okre-
ślenie efekty uboczne oznacza niezaplanowany wynik działania. Jednak
w przypadku informatyki nazywa się tak każdą zmianę stanu systemu.
Na nasze potrzeby zawęzimy to znaczenie do jakiejkolwiek zmiany
w stanie systemu, która będzie mieć wpływ na przyszłe operacje.
Dlaczego w takim razie termin efekty uboczne został przyjęty do nazwa-
nia całkiem celowych zmian wprowadzanych przez operacje? Sądzę, że
wynikało to z doświadczeń ze złożonymi systemami. Większość operacji
wywoływała w nich inne operacje, które z kolei wywoływały dalsze itp.
Gdy tylko dozwolone jest takie głębokie zagnieżdżanie operacji, bardzo
trudno jest przewidzieć wszystkie konsekwencje wywołania danej operacji.
Programista tworzący kod klienta może nie zakładać świadomie efektów
działania operacji drugiego lub trzeciego poziomu — dlatego w pełnym
tego słowa znaczeniu stają się one efektami ubocznymi. Elementy złożo-
nego projektu również oddziałują na siebie w sposób, który może skutko-
wać takim samym stopniem nieprzewidywalności. Zastosowanie określenia
efekty uboczne wskazuje na nieprzewidywalność tego rodzaju interakcji.
Wzajemne oddziaływanie na siebie wielu reguł lub kompo-
zycji obliczeń staje się bardzo trudne do przewidzenia. Aby prze-
widzieć rezultat operacji, wywołujący ją programista musi zrozu-
mieć implementację jej samej, a także wszystkich jej delegacji.
Przydatność dowolnej abstrakcji interfejsów jest ograniczona, jeżeli
zmuszają one programistę do przenikania przez zasłonę tajem-
nicy. Bez łatwo przewidywalnych abstrakcji programiści muszą
ograniczać możliwości ich zastosowania, zmniejszając stopień
potencjalnego skomplikowania możliwej do utworzenia funk-
cjonalności.
Operacje zwracające wyniki bez tworzenia efektów ubocznych są
nazywane funkcjami. Mogą być one wywoływane wielokrotnie i za każ-
dym razem zwracać tę samą wartość. Funkcja może również wywoływać
inną funkcję bez martwienia się o głębokość zagnieżdżenia. Co więcej,
699b15244ae879c728b313782703a4d7
6
funkcję jest zdecydowanie łatwiej testować niż operacje wywołujące efekty
uboczne. Z tych przyczyn funkcje pociągają za sobą zdecydowanie mniejsze
ryzyko.
Oczywiście, w większości systemów programistycznych nie da się
całkowicie uniknąć używania poleceń, jednak związanym z tym proble-
mom można zapobiegać na dwa sposoby. Po pierwsze, możemy trzy-
mać polecenia oraz zapytania oddzielone od siebie w różnych operacjach.
Warto upewnić się, czy metody powodujące efekty uboczne nie zwracają
danych dziedzinowych i czy są napisane w najprostszy możliwy sposób.
Wszystkie zapytania oraz obliczenia powinny być umieszczone w meto-
dach bez obserwowalnych efektów ubocznych (Meyer 1988).
Po drugie, często istnieją alternatywne modele lub projekty, które
nie wymagają wcale zmiany istniejących obiektów. W zamian tworzona
i zwracana jest nowa WARTOŚĆ, reprezentująca rezultat obliczenia. To
powszechna technika, która zostanie zaprezentowana w kolejnym przy-
kładzie. W odpowiedzi na zapytanie WARTOŚĆ może być utworzona,
przekazana oraz zapomniana, w przeciwieństwie do ENCJI, której cykl
życia jest ściśle regulowany.
WARTOŚCI są niezmienne, co skutkuje tym, że poza inicjalizatorami
wywoływanymi podczas ich tworzenia wszystkie ich pozostałe operacje są
funkcjami. Podobnie jak funkcje, WARTOŚCI są bezpieczniejsze w użyciu
oraz prostsze do testowania od zwykłych obiektów. Operacja, która łączy
logikę lub obliczenia ze zmianą stanu, powinna zostać zrefaktoryzowana
do dwóch oddzielnych operacji (Fowler 1999, str. 279). Zgodnie z defi-
nicją, rozdzielenie efektów ubocznych na proste metody poleceń stosuje
się jedynie do ENCJI. Po ukończeniu refaktoryzacji zmierzającej do oddzie-
lenia modyfikacji od zapytań rozważ drugą refaktoryzację — tym razem
w celu przeniesienia odpowiedzialności za złożone obliczenia do WAR-
TOŚCI. Niekiedy można również całkowicie pozbyć się efektów ubocz-
nych poprzez zastąpienie zmiany istniejącego stanu utworzeniem WAR-
TOŚCI lub też przeniesieniem do niej całej odpowiedzialności.
Dlatego:
Umieść jak najwięcej logiki programu w funkcjach, operacjach,
które zwracają wartości bez zauważalnych efektów ubocznych.
Ściśle przestrzegaj wydzielania poleceń (metod, które skutkują
zauważalnymi modyfikacjami stanu) do bardzo prostych operacji,
niezwracających informacji dziedzinowych. Kontroluj dalej efekty
uboczne przez przeniesienie złożonej logiki do WARTOŚCI zaw-
sze wtedy, gdy ujawni się pojęcie pasujące do odpowiedzialności.
FUNKCJE BEZ EFEKTÓW UBOCZNYCH, szczególnie te w nie-
zmiennych WARTOŚCIACH, umożliwiają bezpieczne łączenie operacji.
699b15244ae879c728b313782703a4d7
6
W sytuacji, gdy FUNKCJA prezentowana jest poprzez INTERFEJS
UJAWNIAJĄCY ZAMYSŁ, programista może jej użyć bez zrozumienia
szczegółów jej implementacji.
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()
699b15244ae879c728b313782703a4d7
6
W metodzie mixIn() umieszczono wprawdzie wiele czynności, jednak
jej projekt zachowuje zasadę oddzielania modyfikacji od zapytań. Jednym
z problemów, którym zajmiemy się później, jest pozostawienie w pustce
objętości farby nr 2, stanowiącej argument metody mixIn(). Atrybut
volume drugiej farby nie jest przez operacje zmieniany, co nie wydaje się
logiczne w kontekście modelu pojęciowego. Dla pierwszego programisty
nie stanowiło to problemu, ponieważ — jak możemy sądzić — druga farba
nie interesowała go już po operacji, jednak fakt istnienia konsekwencji
efektów ubocznych lub ich braku jest trudny do przewidzenia. Do tego
pytania powrócimy wkrótce podczas omawiania ASERCJI. Teraz jednak
przyjrzyjmy się kolorowi.
Kolor jest ważnym pojęciem w tej dziedzinie. Spróbujmy poekspe-
rymentować i uczynić go obiektem jawnym. Jak powinien się nazywać?
Pierwsze, co przychodzi na myśl, to „kolor”, jednak wcześniejsze doświad-
czenie z przetwarzania wiedzy dowiodło, iż mieszanie kolorów jest inne
w przypadku farb, a inne w przypadku mieszania światła w kolorze RGB1.
Nazwa obiektu powinna to odzwierciedlać.
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.
699b15244ae879c728b313782703a4d7
6
PigmentColor reprezentujący konkretny odcień koloru żółtego jest zawsze
taki sam. Dlatego też mieszanie kolorów będzie raczej skutkować utworze-
niem nowego obiektu PigmentColor, reprezentującego nowy kolor.
Rysunek 10.7.
Rysunek 10.8.
699b15244ae879c728b313782703a4d7
6
wykorzystać samodzielnie lub w połączeniu z innymi operacjami. Ponie-
waż ten kod jest tak bezpieczny, złożona logika mieszania kolorów jest
w pełni zahermetyzowana. Programiści używający tej klasy nie będą musieli
rozumieć jej implementacji.
***
699b15244ae879c728b313782703a4d7
6
ASERCJE
Wydzielenie złożonych obliczeń do FUNKCJI BEZ EFEKTÓW
UBOCZNYCH ogranicza rozmiar problemu, jednak wciąż pozostaje
kwestia poleceń działających na ENCJACH, które także mogą powodo-
wać wystąpienie efektów ubocznych. Dlatego każdy, kto ich używa, musi
zrozumieć tego konsekwencje. ASERCJE umożliwiają ujawnienie i obsłu-
żenie efektów ubocznych.
***
Zgoda. Analiza kodu polecenia niezawierającego złożonych obliczeń może
być w miarę prosta. Jednak w projekcie, w którym większe elementy są
utworzone z mniejszych, polecenie może wywołać inne polecenia. Progra-
mista używający polecenia wysokiego poziomu musi rozumieć konsekwen-
cje każdego z poleceń na niższym poziomie. Tyle na temat hermetyzacji.
A ponieważ interfejsy obiektów nie ograniczają efektów ubocznych, dwie
podklasy implementujące ten sam interfejs mogą mieć różne efekty
uboczne. Programista używający tych klas chciałby wiedzieć, która jest
którą, aby przewidzieć tego konsekwencje. Tyle na temat abstrakcji oraz
polimorfizmu.
W sytuacji, gdy efekty uboczne operacji są niejawnie zde-
finiowane jedynie przez ich implementacje, projekty, w których
występuje wiele delegacji, staną się gmatwaniną przyczyn i skut-
ków. Jedynym sposobem pozwalającym zrozumieć program
będzie śledzenie jego działania, wraz z rozgałęziającymi się ścież-
kami wykonania. W ten sposób utracimy wartość hermetyzacji.
Konieczność śledzenia konkretnego wykonania programu poko-
nuje abstrakcję.
Musimy dysponować sposobem pozwalającym na zrozumienie zna-
czenia elementu projektu oraz konsekwencji wykonania operacji bez
konieczności zagłębiania się w ich kod wewnętrzny. INTERFEJSY UJAW-
NIAJĄCE ZAMYSŁ ułatwiają nam częściowo to zadanie, jednak niefor-
malne sugestie dotyczące intencji programisty nie zawsze wystarczają.
Szkoła hołdująca „projektowaniu kontraktowemu” idzie o krok dalej,
tworząc dla klas i metod „asercje”, dla których programista gwarantuje,
że będą mieć prawdziwą wartość. Ten styl programowania omówiony
został szczegółowo w książce Meyera z roku 1988. Mówiąc w skrócie,
efekty uboczne operacji opisują „warunki końcowe”, gwarantujące wynik
wywołania metody. „Warunki początkowe” są jakby opisem kontraktu,
czyli warunkami, które muszą być spełnione, aby warunki końcowe były
gwarantowane. Niezmienniki klasy tworzą asercję w stosunku do stanu
ASERCJE 293
699b15244ae879c728b313782703a4d7
6
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.
699b15244ae879c728b313782703a4d7
6
Przykład
Powrót do mieszania farb
Przypomnij sobie, jak w poprzednim przykładzie byłem bardzo zainte-
resowany niejednoznacznością tego, co stanie się z argumentem operacji
mixIn(Paint) w klasie Paint.
Rysunek 10.9.
Po p1.mixIn(p2):
p1.volume jest zwiększany o wartość p2.volume.
p2.volume pozostaje bez zmian.
ASERCJE 295
699b15244ae879c728b313782703a4d7
6
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.
699b15244ae879c728b313782703a4d7
6
Teraz istnieje tylko jedno polecenie mixIn(). Powoduje ono jedy-
nie dodanie obiektu do kolekcji, co jest efektem zgodnym z intuicyjnym
zrozumieniem modelu. Wszystkie inne operacje są FUNKCJAMI BEZ
EFEKTÓW UBOCZNYCH.
Metoda testująca potwierdzająca jedną z ASERCJI przedstawionych
na rysunku 10.10 mogłaby wyglądać tak jak poniżej (przy użyciu szkie-
letu JUnit):
public void testMixingVolume {
PigmentColor yellow = new PigmentColor(0, 50, 0);
PigmentColor blue = new PigmentColor(0, 0, 50);
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
699b15244ae879c728b313782703a4d7
6
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ąć
699b15244ae879c728b313782703a4d7
6
jedynie mechanicznego patrzenia na te dwa elementy, powściągnij swój
techniczny sposób podejścia do problemów, odwołując się często do
intuicji w danej dziedzinie. W przypadku każdej decyzji zadaj sobie pyta-
nie: „Czy jest ona korzystna w odniesieniu do zestawu relacji w danym
modelu i kodzie lub czy odzwierciedla pewien zarys w modelowanej
dziedzinie?”.
Postaraj się odszukać pojęciowo istotny zestaw funkcjonalności,
a wynikowy projekt będzie zarówno elastyczny, jak i zrozumiały. Jeżeli
na przykład „dodawanie” dwóch obiektów ma istotne spójne znaczenie
w danej dziedzinie, zaimplementuj je w metodzie na takim właśnie
poziomie. Nie dziel wtedy metody add() na dwie części. Nie przechodź
do sztucznego kolejnego kroku w tej samej operacji. Patrząc na to z nieco
dalszej perspektywy, każdy obiekt powinien być pojedynczym i komplet-
nym pojęciem, czyli WARTOŚCIĄ CAŁKOWITĄ2.
Idąc tym samym tokiem rozumowania, należy stwierdzić, że szcze-
góły niektórych obszarów dziedziny nie są istotne dla ludzi, którym pro-
gram będzie służyć. Użytkownicy naszej hipotetycznej aplikacji do mie-
szania farb nie dodają przecież czerwonego pigmentu do niebieskiego.
Oni mieszają gotowe farby, z których każda składa się z pigmentów.
Grupowanie elementów, które nie muszą być wyizolowane ani przearan-
żowane, zapobiega bałaganowi i ułatwia dostrzeżenie tych, które naprawdę
muszą zostać ze sobą połączone. Gdyby urządzenie wykorzystywane przez
użytkownika naprawdę umożliwiało łączenie poszczególnych pigmentów,
dziedzina musiałaby zostać zmodyfikowana w taki sposób, aby możliwe
było operowanie na ich poziomie. Również chemik zajmujący się farbami
musiałby dysponować bardziej precyzyjną kontrolą, która prawdopodob-
nie wymagałaby zupełnie odmiennej analizy. Z pewnością skutkowałaby
ona utworzeniem zdecydowanie bardziej szczegółowego modelu tworze-
nia farby niż nasze abstrakcyjne kolory pigmentów, które sprawdzają się
w mieszaniu farb. Niemniej jednak dla wszystkich osób zajmujących się
prostą aplikacją do mieszania farb wszystkie te rozważania będą nieistotne.
Dlatego:
Wydziel elementy projektowe (operacje, interfejsy, klasy oraz
AGREGATY) do spójnych grup, biorąc pod uwagę własną intuicję
związaną z ważnymi częściami dziedziny. W trakcie refaktory-
zacji obserwuj kierunki zmian oraz stabilność modelu, poszu-
kując bazowych ZARYSÓW KONCEPCYJNYCH wyjaśniających
2
Wzorzec WARTOŚCI CAŁKOWITEJ stworzony przez Warda Cun-
ninghama.
699b15244ae879c728b313782703a4d7
6
zastosowane wzorce. W pierwszej kolejności dostosuj model do
spójnych aspektów dziedziny, składających się na możliwy do
zastosowania obszar wiedzy.
Celem jest uzyskanie prostego zestawu interfejsów, możliwych do
połączenia w sposób logiczny w celu utworzenia sensownych stwierdzeń
w JĘZYKU WSZECHOBECNYM, bez wprowadzania chaosu oraz bała-
ganu utrzymaniowego związanego z nieistotnymi pojęciami. Jest to zazwy-
czaj wynik refaktoryzacji. Rzadko udaje się go uzyskać od razu na początku.
Jednak może również nigdy nie pojawić się w przypadku refaktoryzacji
skupiającej się na aspektach technicznych. Jego pojawienie się umożliwia
jedynie skupienie się na refaktoryzacji ku głębszemu zrozumieniu.
Nawet w sytuacji, gdy projekt wpasowuje się w ZARYSY KON-
CEPCYJNE, może istnieć potrzeba jego modyfikacji i refaktoryzacji. Jeśli
jednak wydaje się ona jedynie lokalna i nie dotyka całego szerokiego zakresu
pojęciowego modelu, będzie to sygnał jego dobrego dopasowania. Napo-
tkanie wymagania zmuszającego nas do szerokich zmian w podziale
obiektów oraz metod jest istotnym sygnałem mówiącym o tym, że nasze
zrozumienie dziedziny wymaga ulepszenia. W takim przypadku będzie
to szansa na pogłębienie modelu oraz utworzenie bardziej elastycznego
projektu.
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
699b15244ae879c728b313782703a4d7
6
Rysunek 10.11.
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
699b15244ae879c728b313782703a4d7
6
Rysunek 10.12.
Ten model
obejmuje nowe
rodzaje
harmonogramów
dla sum
przyrostowych
reguły miały zastosowanie do płatności odsetek i opłat. Oznaczało to, że
elementy modelu w sposób naturalny znajdą swoje połączenie w poje-
dynczej klasie Payment.
Rysunek 10.13.
***
699b15244ae879c728b313782703a4d7
6
INTERFEJSY UJAWNIAJĄCE ZAMIAR umożliwiają klientom przed-
stawienie obiektów w postaci mechanizmu pojęciowego, a nie jedynie
stricte technicznego. FUNKCJE BEZ EFEKTÓW UBOCZNYCH oraz
ASERCJE umożliwiają natomiast bezpieczne wykorzystanie tych mecha-
nizmów i łączenie ich w złożone kombinacje. Pojawienie się ZARYSÓW
KONCEPCYJNYCH stabilizuje elementy modelu, a także umożliwia ich
bardziej intuicyjne wykorzystanie i łączenie.
Wciąż jednak możemy być podatni na przeciążenie pojęciowe,
jeżeli wzajemne zależności zmuszą nas do analizowania zbyt wielu rzeczy
jednocześnie.
699b15244ae879c728b313782703a4d7
6
KLASY SAMODZIELNE
Wzajemne zależności utrudniają zrozumienie modeli oraz projektów.
Co więcej, utrudniają również ich testowanie i tak jak wszystkie zależ-
ności, mają tendencję do szybkiego powiększania się.
Każda asocjacja jest, oczywiście, zależnością i zrozumienie klasy
wymaga również analizy tego, do czego jest dołączona. A wszystkie kwestie
z nią powiązane mogą być dołączone do wielu innych aspektów, które
również muszą zostać zrozumiane. Typ każdego argumentu dowolnej
z metod jest również zależnością, podobnie jak zwracana wartość.
W przypadku pojedynczej zależności musisz jednocześnie analizo-
wać dwie klasy, a także zastanawiać się nad rodzajem ich wzajemnego
powiązania. Jeżeli masz dwie zależności, w grę wchodzą już trzy klasy,
relacje pomiędzy parami klas osobno, a także relacja, w której mogą być
wszystkie wspólnie. W przypadku, gdy każda z tych klas ma jeszcze
swoje zależności, musisz pamiętać również o nich. Trzy zależności tworzą
już efekt kuli śniegowej.
Do ograniczenia sieci wzajemnych zależności służą MODUŁY
oraz AGREGATY. Gdy pewną wysoce spójną poddziedzinę umieścimy
w MODULE, zestaw jej obiektów jest odłączany od reszty systemu, co
skutkować będzie ograniczoną liczbą powiązanych pojęć. Jednak nawet
w przypadku MODUŁU wciąż jeszcze będzie wiele tematów do analizy,
a także wiele wewnętrznych relacji, zarządzanie którymi może sprawiać
duże trudności.
Zwiększona trudność interpretacji projektu powiększającego
się stale wraz z dodawaniem kolejnych zależności dotyczyć będzie
także MODUŁU. To wszystko prowadzi do przeciążenia umy-
słowego, ograniczając maksymalną złożoność projektu, którą może
obsłużyć programista. Na to przeciążenie zdecydowanie większy
wpływ od widocznych odwołań mają pojęcia niejawne.
Dobrze dopracowane modele są destylowane do momentu, aż każde
pozostałe połączenie pomiędzy pojęciami będzie reprezentować coś fun-
damentalnego dla ich znaczenia. W przypadku bardzo ważnego zestawu
pojęć liczba wzajemnych relacji może być zredukowana do zera, powo-
dując utworzenie klasy, która wraz z niewieloma typami podstawowymi
oraz bibliotekami może być w pełni zrozumiana samodzielnie.
W każdym środowisku programistycznym kilka podstawowych ele-
mentów jest zazwyczaj tak dominujących, że zawsze pozostają w pamięci.
Na przykład w Javie są to najważniejsze typy oraz kilka standardowych
bibliotek obsługujących takie podstawowe rzeczy jak liczby, łańcuchy
znakowe oraz kolekcje. Właściwie w takim przypadku „liczby całkowite”
699b15244ae879c728b313782703a4d7
6
nie będą nas przeciążać umysłowo, w przeciwieństwie do każdego dodat-
kowego pojęcia, o którym będziemy musieli myśleć, aby zrozumieć dany
obiekt.
Pojęcia niejawne, te rozpoznane i nierozpoznane, mają taki sam wkład
do naszego wysiłku interpretacyjnego jak jawne zależności. Chociaż zazwy-
czaj możemy pominąć zależności lub typy podstawowe, w rodzaju liczb
całkowitych lub łańcuchów znakowych, to jednak nie możemy zigno-
rować tego, co one sobą reprezentują. Na przykład w pierwszym projekcie
aplikacji do mieszania farb obiekt Paint zawierał trzy publiczne wartości
całkowite, reprezentujące wartości kolorów czerwonego, żółtego oraz nie-
bieskiego. Utworzenie obiektu Pigment Color nie zwiększyło liczby
obsługiwanych pojęć ani zależności. Spowodowało jedynie, że te, które
były już obecne, stały się jawne i łatwiej było je zrozumieć. Z drugiej
strony, operacja size() w klasie Collection zwraca wartość typu int,
reprezentującą liczbę elementów, a ta mieści się w podstawowym znacze-
niu liczby całkowitej, dlatego nie wprowadzamy żadnego nowego pojęcia.
Podejrzana będzie każda zależność, dopóki nie udowodnimy jej pod-
stawowego znaczenia dla obiektu. Analiza powinna rozpocząć się od
momentu tworzenia podstawowych pojęć modelu. Następnie trzeba będzie
zachować ostrożność przy każdej indywidualnej asocjacji lub operacji.
Przydatność modelu oraz projektu może zmniejszyć się wraz z każdą
zależnością — często do zera.
Słabe związanie jest jednym z podstawowych pojęć w pro-
jektowaniu obiektowym. Jeżeli tylko możesz, tego się trzymaj.
Eliminuj z obrazu wszystkie inne pojęcia. Wtedy klasa będzie
całkowicie samodzielna i będzie można analizować ją niezależ-
nie. Każda taka samodzielna klasa zdecydowanie zmniejsza trud
związany z interpretacją MODUŁU.
Zależności z innymi klasami występującymi w tym samym module
są mniej szkodliwe od tych umieszczonych na zewnątrz. Podobnie, jeżeli
dwa obiekty są ze sobą powiązane w sposób naturalny, wtedy wiele
operacji używających tej samej pary obiektów może w rzeczywistości
wyjaśnić naturę ich relacji. Celem nie jest eliminacja wszystkich zależ-
ności, lecz tylko tych nieistotnych. W sytuacji, gdy nie mogą zostać wyeli-
minowane wszystkie zależności, i tak każda usunięta uwalnia programistę,
który może wtedy skoncentrować się na pozostałych.
Postaraj się umieścić najbardziej skomplikowane obliczenia w KLA-
SACH SAMODZIELNYCH, być może modelując odpowiednie WAR-
TOŚCI umieszczone w bardziej powiązanych klasach.
Pojęcie farby jest fundamentalnie związane z pojęciem koloru. Jednak
ten ostatni, nawet jeżeli jest pigmentem, i tak może być interpretowany
699b15244ae879c728b313782703a4d7
6
w oddzieleniu od farby. Ujawniając oba te pojęcia i rozdzielając je od siebie,
sprawiamy, że pozostała jednokierunkowa asocjacja mówi nam coś waż-
nego. Klasa Pigment Color, w której zawarta jest większość logiki obli-
czeniowej, może zaś być analizowana i testowana niezależnie.
***
Słabe związanie jest podstawowym sposobem zmniejszania przeciążenia
pojęciowego. KLASA SAMODZIELNA jest tego ekstremalnym przy-
kładem.
Eliminacja zależności nie powinna oznaczać redukcji logiki modelu
przez arbitralne przekształcanie wszystkiego na typy proste. Ostatni wzo-
rzec zaprezentowany w tym rozdziale — ZAMKNIĘCIE OPERACJI —
jest przykładem techniki umożliwiającej zmniejszenie zależności przy
utrzymaniu bogatego interfejsu.
699b15244ae879c728b313782703a4d7
6
ZAMKNIĘCIE OPERACJI
Gdybyśmy wzięli dwie liczby rzeczywiste i pomnożyli je przez siebie,
uzyskalibyśmy kolejną liczbę rzeczywistą (liczby rzeczywiste obejmują
liczby wymierne oraz niewymierne). Ponieważ to zdanie będzie zawsze
prawdziwe, możemy stwierdzić, że liczby rzeczywiste są „zamknięte
w operacji mnożenia”, ponieważ nie ma dla nich żadnej drogi ucieczki
z danego zbioru. Jeżeli połączysz dwa dowolne elementy zbioru,
rezultat również jest zawsze w nim zawarty.
— Forum matematyczne na Uniwersytecie Drexel (mathforum.org)
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ęć.
699b15244ae879c728b313782703a4d7
6
Ten wzorzec jest najczęściej stosowany do operacji na WARTO-
ŚCIACH. Ponieważ cykl życia ENCJI ma znaczenie w dziedzinie, nie
możesz po prostu wyczarować nowej w celu udzielenia odpowiedzi na
zapytanie. Istnieją operacje, które są zamknięte, w rodzaju ENCJI. Na
przykład mógłbyś zapytać obiekt klasy Employee (ang. pracownik) o jego
kierownika i dostać z powrotem kolejny obiekt tej samej klasy. Jednak
ogólnie rzecz biorąc, ENCJE nie są tym rodzajem pojęć, które stanowią
zazwyczaj wynik obliczeń. Dlatego w większości sytuacji jest to zachętą
do poszukania WARTOŚCI.
Operacja może być również zamknięta w typie abstrakcyjnym, kiedy
to określone argumenty mogą być obiektami różnych skonkretyzowa-
nych klas. W gruncie rzeczy przecież dodawanie jest operacją zamkniętą
w liczbach rzeczywistych, które mogą być zarówno wymierne, jak i nie-
wymierne.
W trakcie eksperymentowania poszukaj sposobów na ograniczenie
wzajemnych zależności oraz zwiększenie spójności, które niekiedy dopro-
wadzą Cię w pół drogi do tego wzorca. A co w przypadku, gdy argument
zgadza się z klasą implementującą, jednak zwracany jest rezultat innego
typu? Albo gdy typ zwracany zgadza się z odbiorcą, zaś argument się
różni? Tego rodzaju operacje nie są zamknięte, jednak i tak korzystają
z zalet ZAMKNIĘCIA. Jeżeli ten dodatkowy typ jest typem podstawo-
wym lub podstawową klasą biblioteczną, uwalnia on umysł od dodat-
kowych obowiązków prawie tak samo skutecznie jak ZAMKNIĘCIE.
We wcześniejszym przykładzie operacja mixedWith() z klasy Pig-
mentColor była zamknięta w tej klasie. Istnieje też kilka innych przy-
kładów takiego zamknięcia rozrzuconych w tej książce. Oto przykład tego,
jak przydatna może być ta idea, nawet jeżeli nie osiągniemy pełnego
ZAMKNIĘCIA.
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()) {
699b15244ae879c728b313782703a4d7
6
Employee anEmployee = it.next();
if (anEmployee.salary() < 40000)
lowPaidEmployees.add(anEmployee);
}
***
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,
699b15244ae879c728b313782703a4d7
6
a także skorzystać z zawartego w nich sposobu rozumowania, aby stwo-
rzyć programy, które później będzie można dalej rozwijać, aż do bardzo
dużej złożoności.
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ęść.
699b15244ae879c728b313782703a4d7
6
Techniki generowania kodu, które znacznie ograniczają możliwość
iteracji, ponieważ łączą kod utworzony automatycznie z tym napi-
sanym ręcznie w sposób utrudniający jego ponowne automatyczne
wygenerowanie.
Niezamierzoną konsekwencją wielu prób projektowania deklaratyw-
nego jest zmniejszenie roli modelu oraz aplikacji, ponieważ programiści
ograniczeni przez szkielet bardzo często odchodzą od projektu, aby dostar-
czyć cokolwiek materialnego.
Kolejnym obiecującym podejściem do projektowania deklaratywnego
jest programowanie na podstawie reguł. Niestety, jego intencje mogą osła-
bić subtelne problemy.
Mimo iż program oparty na regułach jest teoretycznie deklaratywny,
większość systemów definiuje „predykaty kontrolne”, które zostały dodane
w celu umożliwienia zwiększania wydajności programu. Kod kontrolny
zwiększa efekty uboczne, co sprawia, że logika nie jest już dłużej całkowicie
dyktowana przez zadeklarowane reguły. Dodawanie, usuwanie i porząd-
kowanie reguł może powodować niespodziewane niepoprawne wyniki.
Dlatego aby stworzyć przejrzysty kod, programista używający reguł
logicznych musi zachować ostrożność. Dotyczy to również programisty
obiektowego.
Wiele podejść do projektowania deklaratywnego może zostać wypa-
czonych w sytuacji, gdy programiści będą je celowo lub niecelowo obcho-
dzić. Jest to bardzo prawdopodobne w sytuacji, gdy system jest trudny
w użyciu lub narzuca zbyt wiele ograniczeń. Aby uzyskać korzyści z pro-
gramowania deklaratywnego, każdy musi podporządkować się regułom
narzuconym przez szkielet programowania.
Największą korzyścią ze stosowania podejścia deklaratywnego, jaką
udało mi się zaobserwować, było wykorzystanie wysoce specjalizowanego
szkieletu do automatyzacji szczególnie żmudnych i narażonych na błędy
aspektów projektu, w rodzaju zachowania trwałości danych lub mapowania
obiektowo-relacyjnego. Najlepsze rozwiązania nie obciążają dodatkowo
programistów, zostawiając im całkowitą wolność projektowania.
699b15244ae879c728b313782703a4d7
6
do ich łączenia. Program jest następnie kompilowany, bardzo często przy
użyciu tradycyjnego języka obiektowego, w którym biblioteka klas wspiera
implementacje w kategoriach danego języka.
W tego rodzaju języku programy mogą być niesamowicie opisowe
i mają najsilniejsze związanie z JĘZYKIEM WSZECHOBECNYM.
Stanowi to razem niesamowitą ideę, jednak języki właściwe dziedzinie
mają również pewne ograniczenia w podejściu do programowania, odzie-
dziczone z technologii obiektowej.
W celu ulepszenia modelu programista musi być w stanie modyfi-
kować język. Może to polegać na modyfikacji deklaracji gramatycznych
lub funkcjonalności związanych z interpretacją innych języków, jak
również modyfikacją bibliotek klas. Jestem dużym entuzjastą poznawa-
nia zaawansowanych technologii oraz pomysłów projektowych, jednak
musimy trzeźwo ocenić możliwości konkretnego zespołu projektowego,
jak również prawdopodobne umiejętności przyszłego zespołu utrzyma-
niowego. Szczególną wartość stanowi również spójność aplikacji oraz
modelu zaimplementowanych w tym samym języku. Innym ogranicze-
niem jest trudność refaktoryzacji kodu klienta w taki sposób, aby był on
zgodny z odświeżonym modelem oraz skojarzonym językiem właści-
wym dziedzinie. Oczywiście, ktoś mógłby wynaleźć techniczną popraw-
kę problemów z refaktoryzacją.
Ta technika może być najbardziej przydatna w przypadku bardzo
dojrzałych modeli, być może wtedy, gdy kod klienta jest tworzony przez
inny zespół. Ogólnie mówiąc, takie konfiguracje prowadzą do zabójczego
rozdzielenia bardzo dobrze wyszkolonych twórców technicznego szkieletu
i słabo wyszkolonych twórców aplikacji, jednak nie musi być to reguła.
Bardzo podobna idea jest częścią standardowego stylu programowania
w języku programowania o nazwie Scheme3, pozwalającym na wyko-
rzystanie opisowości języka właściwego dziedzinie bez tworzenia takiego
podziału ról w systemie.
3
Jest to odmiana języka programowania Lisp — przyp. tłum.
699b15244ae879c728b313782703a4d7
6
możliwe do łączenia ze sobą elementy, które również dobrze komunikują Z dołu do góry
swoje znaczenie oraz mają dobrze scharakteryzowane lub oczywiste efekty Inny paradygmat
albo nie posiadają ich wcale. może obsługiwać
języki właściwe
Projekt elastyczny umożliwia wykorzystywanie deklaratywnego stylu
dziedzinie w sposób
projektowania w kodzie klienta. Aby to zademonstrować, w kolej-
lepszy od obiektów.
nym podrozdziale użyjemy razem niektórych wzorców przedstawio- W języku
nych wcześniej w celu utworzenia bardziej elastycznej i deklaratywnej programowania
SPECYFIKACJI. o nazwie Scheme,
będącym
przedstawicielem
Rozszerzenie SPECYFIKACJI rodziny języków
„programowania
w stylu deklaratywnym funkcjonalnego”,
W rozdziale 9. opisaliśmy podstawowe pojęcie SPECYFIKACJI, rolę, jaką podobna idea
może ona odgrywać w programie, oraz w pewien sposób również to, co jest częścią
standardowego stylu
jest wymagane w jej implementacji. Teraz natomiast przyjrzyjmy się dodat-
programowania,
kowym wodotryskom, które mogą być bardzo przydatne w niektórych
co umożliwia
sytuacjach, określonych przez skomplikowane reguły. zachowanie
SPECYFIKACJA stanowi adaptację ustalonego formalizmu w postaci ekspresywności
predykatu. Predykaty mają inne przydatne właściwości, z których możemy języka właściwego
wybiórczo skorzystać. dziedzinie
bez rozdwajania
systemu.
Łączenie SPECYFIKACJI
przy użyciu operatorów logicznych
W trakcie używania SPECYFIKACJI bardzo szybko napotkasz sytuacje,
w których chciałbyś je połączyć. Jak stwierdziliśmy przed chwilą, SPE-
CYFIKACJA jest przykładem predykatu, a te z kolei mogą być ze sobą
łączone, a także modyfikowane przy użyciu operatorów „AND” (logiczna
koniunkcja), „OR” (logiczna alternatywa) oraz „NOT” (logiczna negacja).
Tego rodzaju operacje logiczne są zamknięte w predykatach, dlatego SPE-
CYFIKACJA wykazuje cechy ZAMKNIĘCIA OPERACJI.
Ponieważ w SPECYFIKACJĘ wbudowane są znaczące możliwości
uogólniania idei, bardzo przydatne staje się utworzenie klasy abstrakcyjnej
lub interfejsu, który może być użyty do wszelkiego rodzaju SPECYFI-
KACJI. Oznaczać to będzie określenie typu argumentu na bardzo wyso-
kim poziomie klasy abstrakcyjnej:
public interface Specification {
boolean isSatisfiedBy(Object candidate);
}
699b15244ae879c728b313782703a4d7
6
Ta abstrakcja wymaga konstrukcji zabezpieczającej na początku
metody, jednak w żaden sposób nie dotyka to funkcjonalności. Na przy-
kład specyfikacja kontenera (Container Specification) z przykładu
w rozdziale 9. mogłaby zostać zmodyfikowana w następujący sposób:
public class ContainerSpecification implements Specification {
private ContainerFeature requiredFeature;
699b15244ae879c728b313782703a4d7
6
new ContainerSpecification(VENTILATED_TYPE_2);
Specification either = ventilatedType1.or(ventilatedType2);
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);
}
699b15244ae879c728b313782703a4d7
6
public Specification not() {
return new NotSpecification(this);
}
}
public NotSpecification(Specification x) {
wrapped = x;
}
public boolean isSatisfiedBy(Object candidate) {
return !wrapped.isSatisfiedBy(candidate);
}
}
699b15244ae879c728b313782703a4d7
6
niż inne, a co więcej, jego implementacja jest mniej złożona. Jeżeli jest
to jedyny wymagany operator, nie zawahaj się ograniczyć implementacji
tylko do niego.
Wracając do przykładowego dialogu umieszczonego w rozdziale 2.,
wydaje się, że programiści w swojej SPECYFIKACJI nie zaimplemento-
wali logiki dla relacji „spełniony przez”. Aż do tej pory SPECYFIKACJA
była wykorzystywana jedynie do tworzenia na zamówienie. Nawet wtedy
abstrakcja była nietknięta, a dodawanie nowej funkcjonalności było rela-
tywnie proste. Stosowanie wzorca nie oznacza tworzenia niepotrzebnych
funkcjonalności. Mogą one zostać dodane później, pod warunkiem że
użyte pojęcia nie zostaną zaciemnione.
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))
699b15244ae879c728b313782703a4d7
6
Zawartość SPECYFIKACJI dla „taniego” kontenera
Góra Operator AND (WZORZEC PYŁKU — FLYWEIGHT)
Operator NOT (WZORZEC PYŁKU — FLYWEIGHT)
Wzmocniony (ang. Armored)
Operator NOT
Wentylowany (ang. 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
699b15244ae879c728b313782703a4d7
6
do bardziej rygorystycznego kontenera. W obu tych przypadkach właśnie
opisany raport nie wyświetliłby tych substancji.
Wprowadźmy zatem nową operację, porównującą bezpośrednio dwie
SPECYFIKACJE.
boolean subsumes(Specification other);
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.
699b15244ae879c728b313782703a4d7
6
public class MinimumAgeSpecification {
int threshold;
699b15244ae879c728b313782703a4d7
6
}
return true;
}
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
699b15244ae879c728b313782703a4d7
6
specjalizowane obliczenia. Spróbuj wtedy ją wydzielić. Jeżeli aplikacja
narzuca złożone reguły ograniczające zmiany stanu, spróbuj wyciągnąć je
do oddzielnego modelu lub prostego szkieletu, który pozwoli Ci te reguły
zadeklarować. Każdy taki krok oczyszcza nowy moduł, a część, której
nie dotykał, staje się mniejsza i czystsza. Pozostawiona część jest napisana
w stylu deklaratywnym, tworząc deklaracje w terminologii używanej przez
szkielet walidacyjny lub z wykorzystaniem matematyki albo w dowolnej
innej wykorzystywanej przez tę poddziedzinę.
Zdecydowanie lepiej jest wywrzeć duży wpływ na jeden obszar,
silnie go uelastyczniając, niż rozproszyć swoje działania na cały model.
W rozdziale 15. zajmiemy się bardziej szczegółowo tym, w jaki sposób
wybrać poddziedziny oraz nimi zarządzać.
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
699b15244ae879c728b313782703a4d7
6
się na jednej z funkcjonalności modelu, porównywalnej do tej w rzeczy-
wistym projekcie. Jedno z wymagań aplikacji mówiło, że w przypadku,
gdy pożyczkobiorca dokonuje płatności, środki są domyślnie dzielone
według udziałów pożyczkodawców w danej pożyczce.
Rysunek 10.16.
double newLoanShareAmount =
initialLoanShareAmount - paymentShareAmount;
Share newLoanShare =
new Share(owner, newLoanShareAmount);
loanShares.put(owner, newLoanShare);
}
return paymentShares;
}
699b15244ae879c728b313782703a4d7
6
return total;
}
}
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);
}
}
699b15244ae879c728b313782703a4d7
6
Kod klienta wygląda teraz następująco:
Map distribution =
aLoan.calculatePrincipalPaymentShares(paymentAmount);
aLoan.applyPrincipalPaymentShares(distribution);
Rysunek 10.18.
699b15244ae879c728b313782703a4d7
6
Rysunek 10.19.
699b15244ae879c728b313782703a4d7
6
Skierujmy się teraz ku ZAMKNIĘCIU OPERACJI. Zamiast „zwięk-
szać” wartość obiektu SharePie lub dodawać do niego inne udziały, spró-
bujmy dodać do siebie razem dwa obiekty SharePie. Wynikiem tej operacji
powinien być nowy, większy obiekt SharePie.
Moglibyśmy częściowo zamknąć operacje prorate() w obiekcie Sha-
rePie, zmieniając jedynie zwracany przez nią typ. Również zmiana jej
nazwy na prorated() uwypukla brak efektów ubocznych. „Matematyka
udziałów” zaczyna już nabierać kształtu, na razie tylko z czterema
operacjami.
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();
699b15244ae879c728b313782703a4d7
6
Object owner = it.next();
double resultShareAmount = getShareAmount(owner) –
otherShares.getShareAmount(owner);
result.add(owner, resultShareAmount);
}
return result;
}
699b15244ae879c728b313782703a4d7
6
nam na użycie w kodzie klasy Loan stylu deklaratywnego, który zaczyna
przypominać pojęciową definicję transakcji biznesowej, a nie sztuczne
obliczenia.
W ten sposób możliwe jest teraz proste zadeklarowanie innych
rodzajów transakcji (zbyt skomplikowanych do wcześniejszego wprowa-
dzenia). Na przykład wypłaty pożyczki są dzielone pomiędzy udziałow-
ców proporcjonalnie do ich udziałów w linii kredytowej. Nowa wypłata
jest dodawana do wymaganej pożyczki. W naszym nowym języku dzie-
dziny będzie to wyglądać następująco:
public class Facility {
private SharePie shares;
. . .
public SharePie calculateDrawdownDefaultDistribution(
double drawdownAmount) {
return shares.prorated(drawdownAmount);
}
}
699b15244ae879c728b313782703a4d7
6
ich nazwy, i nic wiÚcej. Pozwala nam to równieĝ na podstawie tych
samych metod utworzyÊ funkcjonalnoĂci analityczne (wczeĂniej
mogïy byÊ one wywoïywane jedynie w przypadku wykonania rze-
czywistego podziaïu Ărodków, poniewaĝ po kaĝdej modyfikacji da-
ne ulegïyby zmianie)
699b15244ae879c728b313782703a4d7
6
Projekt dziedziny wywiera bardzo duży wpływ na zdolność programu
do radzenia sobie ze zmianą oraz złożonością. Jak pokazują przykłady
umieszczone w tym rozdziale, bardzo często zależy on od całkiem szczegó-
łowych decyzji modelowania oraz projektowania. Wpływ projektu ela-
stycznego może rozciągać się zdecydowanie poza dany modelowany oraz
projektowany obszar. W rozdziale 15. omówimy strategiczną wartość
projektu elastycznego jako jednego z wielu narzędzi do destylacji modelu
dziedziny w celu ułatwienia śledzenia dużych i złożonych projektów.
699b15244ae879c728b313782703a4d7
6
332 ROZDZIAŁ 10. PROJEKT ELASTYCZNY
699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 11.
Stosowanie wzorców
analitycznych
333
699b15244ae879c728b313782703a4d7
6
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]
699b15244ae879c728b313782703a4d7
6
Zasady wzorców analitycznych oraz ich stosowanie można lepiej
wyjaśnić poprzez przykład niż abstrakcyjne rozważania. W tym rozdziale
przedstawię dwa przykłady programistów używających małych, reprezen-
tatywnych modeli z rozdziału pt. „Księgowość i gospodarka magazynowa”
umieszczonego w książce Fowlera z roku 1997. Podsumujemy również
wzorce analityczne, których użycie jest konieczne w celu wsparcia tych
przykładów. Oczywiście, nie będziemy podejmować się prób utworzenia
katalogu wzorców tego rodzaju ani nawet w pełni wyjaśniać tych przy-
kładowych. Naszym celem jest zobrazowanie podejścia do ich integracji
z procesem projektowania sterowanego dziedziną.
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
699b15244ae879c728b313782703a4d7
6
Programistka zdecydowała się przeczytać rozdział 6. w książce o wzor-
cach analitycznych, zatytułowany „Księgowość i gospodarka magazynowa”.
Oto podsumowanie fragmentu, który uznała ona za najbardziej właściwy.
Rysunek 11.2.
Prosty model
księgowy
Rysunek 11.3.
Model transakcyjny
699b15244ae879c728b313782703a4d7
6
Jest to uznana zasada podwójnego księgowania. Każde uznanie
(ang. credit) ma odpowiadające mu obciążenie (ang. debit). Oczywiście,
podobnie do innych zasad zachowawczych, dotyczy to jedynie syste-
mu zamkniętego, czyli takiego, który zawiera wszystkie źródła i ujścia
danych. Wiele prostych aplikacji nie wymaga tego rodzaju rygory-
stycznego podejścia.
W swojej książce Fowler zawarł również bardziej rozbudowane
odmiany tych modeli, a także znaczną ilość dyskusji na temat zwią-
zanych z nimi kompromisów.
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.
699b15244ae879c728b313782703a4d7
6
Programista 2: Nie jestem pewny, czy już użyliśmy „Transakcji” (Tran-
saction). Definicja mówi o przenoszeniu pieniędzy z jednego konta
(Account) na inne, a nie dwóch wpisach bilansujących się wzajemnie
na tym samym koncie.
Programista 1: To słuszna uwaga. Zastanawiało mnie również, że
w książce zwracano wiele uwagi na temat jednoczesnego tworzenia
transakcji. Przecież płatności odsetek mogą być opóźnione o kilka dni.
Ekspert: Właściwie nie muszą być koniecznie opóźnione. Dopuszczalna
jest duża elastyczność w terminach płatności.
Programista 1: To może zatem być ślepa uliczka. Sądziłam, że mogliśmy
odkryć pewne ukryte pojęcie. Umożliwienie kalkulatorowi odsetko-
wemu (Interest Calculator) utworzenia obiektu wpisu (Entry)
wydaje się komunikować proces w lepszy sposób. A transakcja (Tran-
saction) wydaje się porządnie spajać razem obliczone odsetki z doko-
naną płatnością.
Ekspert: Ale dlaczego musimy wiązać razem sumę przyrostową (ang. accrual)
z płatnością? To zupełnie oddzielne wpisy w systemie księgowym.
Najważniejszy jest bilans rachunku. Jeżeli dodamy do tego poszcze-
gólne wpisy, będziemy już mieć wszystko, czego potrzebujemy.
Programista 2: Czy dobrze rozumiem, że nie śledzicie tego, czy klienci
dokonali wpłaty odsetek?
Ekspert: Oczywiście, że śledzimy. Jednak nie jest to takie proste, jak
przyjęty przez Was schemat, zgodny z zasadą jeden przyrost/jedna
płatność.
Programista 2: Właściwie, gdybyśmy przestali skupiać się na tym połą-
czeniu, uprościłoby to wiele rzeczy.
Programista 1: W porządku. Co powiedzielibyście na coś takiego? [Bierze
kopię starego diagramu klas i zaczyna szkicować modyfikacje]. A tak na
marginesie, użyłeś kilkukrotnie określenia suma przyrostowa. Czy
mógłbyś wyjaśnić, co ono oznacza?
Ekspert: Oczywiście. Suma przyrostowa występuje wtedy, gdy księgujesz
wydatek lub przychód w czasie jego wystąpienia, bez względu na
to, czy rzeczywiste pieniądze zmienią właściciela, czy nie. Dlatego
odsetki księgowane są codziennie, lecz są one na przykład płacone
dopiero łącznie na koniec miesiąca.
Programista 1: Rzeczywiście potrzebowaliśmy takiego określenia.
W porządku. To jak to teraz wygląda?
699b15244ae879c728b313782703a4d7
6
Rysunek 11.5.
Programista 1: Teraz możemy pozbyć się wszystkich tych zawiłości Oryginalny diagram
związanych z kojarzeniem płatności, które występowały w kalku- klas z sumami
latorze. W zamian wprowadziliśmy pojęcie sumy przyrostowej, które przyrostowymi
w lepszy sposób odkrywa zamiar. oddzielonymi
od płatności
Ekspert: Czy w takim razie nie będziemy dysponować obiektem Account?
Oczekiwałem, że będę mógł zobaczyć tu wszystko razem, to znaczy
sumy przyrostowe, płatności i bilans rachunku.
Programista 1: Naprawdę? W takim razie może zadziałałoby takie roz-
wiązanie. [Bierze inny diagram i szkicuje].
699b15244ae879c728b313782703a4d7
6
Dwoje programistów wyszło ze spotkania i rozpoczęło refaktoryza-
cję na podstawie nowego modelu. Gdy tylko zagłębili się w kod, dopra-
cowali projekt, a także dokonali kilku spostrzeżeń ulepszających model.
Klasa wpisów (Entry) została zamodelowana jako klasa bazowa dla
płatności (Payment) oraz sum przyrostowych (Accrual), ponieważ bliższa
analiza odkryła dla nich nieco inne odpowiedzialności w aplikacji, a oba
te pojęcia były bardzo ważne w dziedzinie. Z drugiej strony, nie istniało
żadne rozróżnienie pojęciowe ani pod względem zachowania dla wpisów,
niezależnie od tego, czy pochodziły z płatności opłat (ang. fee), czy odse-
tek (ang. interest). Pojawiały się one zwyczajnie na odpowiednim koncie
(Account).
Niestety, jednak po chwili programiści odkryli, że muszą odrzucić
tę ostatnią abstrakcję z powodu braku możliwości jej implementacji. Dane
przechowywane były bowiem w tabelach relacyjnych, a standard projekto-
wy wymagał, aby można było je interpretować nawet bez działającego
programu. Oznacza to konieczność trzymania wpisów dla opłat oraz
odsetek w oddzielnych tabelach. Jedynym sposobem na osiągnięcie tego
przy użyciu wykorzystywanego przez nich szkieletu mapowania relacyj-
nego było utworzenie konkretnych podklas dla różnych rodzajów płat-
ności (FeePayment, InterestPayment itp.). Gdyby programiści dyspono-
wali inną infrastrukturą, mogliby uniknąć tego niezgrabnego rozszerzenia.
Zdecydowałem się przedstawić tę w dużej mierze fikcyjną historię,
ponieważ chciałem zaprezentować sytuacje, które napotykamy wielo-
krotnie. Musimy decydować się na ostrożnie skalkulowane kompromisy,
a następnie podążać naprzód, broniąc się przed zboczeniem ze ścieżki
PROJEKTU STEROWANEGO MODELEM.
Analiza oraz testowanie nowego projektu były zdecydowanie prostsze,
ponieważ jego najbardziej złożona funkcjonalność umieszczona została
w FUNKCJACH BEZ EFEKTÓW UBOCZNYCH. Kod pozostałych
poleceń jest prosty (ponieważ wywołuje różne FUNKCJE) oraz dobrze
scharakteryzowany przez ASERCJE.
699b15244ae879c728b313782703a4d7
6
Rysunek 11.7.
Diagram klas
Przykład (kontynuacja)
po implementacji
Spostrzeżenia na temat nocnego przetwarzania
Po kilku tygodniach model oparty na ulepszonej idei konta zaczął się zado-
mawiać. Jak to się często zdarza, przejrzystość nowego modelu spowo-
dowała ujawnienie innych problemów. Programista odpowiedzialny za
nocny skrypt przetwarzania wsadowego (Programista 2) musiał zacząć
używać nowego modelu i dostrzegł połączenia pomiędzy zachowaniem
skryptu a pewnymi pojęciami we wzorcach analitycznych. A oto podsumo-
wanie kilku pojęć, które uznał on za istotne.
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.
699b15244ae879c728b313782703a4d7
6
Rysunek 11.8.
Reguła księgowania jest wywoływana przez nowy wpis (Entry)
Diagram klas prostej
reguły księgowania do jej konta „wejściowego”. Następnie, zgodnie z jej wewnętrzną
metodą obliczeniową, tworzony jest nowy wpis, wstawiany do konta
„wyjściowego”. W systemie płacowym wpis do konta płacowego może
uruchomić regułę księgowania obliczającą 30-procentowy szacun-
kowy podatek od dochodu, a następnie wstawiającą ten wpis na koncie
z potrąconymi podatkami.
699b15244ae879c728b313782703a4d7
6
jest tak samo istotne dla pomyślnego zastosowania wzorca, jak same
definicje obiektów modelu. W ten sposób eliminowana jest wielo-
znaczność, a osoby podejmujące decyzję prowadzone są bezpośrednio
do jasno zdefiniowanego zestawu możliwych wyborów. Wszystkie
te tryby identyfikują łatwe do przegapienia wyzwanie, a jednocześnie
udostępniają zestaw słownictwa ułatwiający dyskusję.
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
699b15244ae879c728b313782703a4d7
6
sposób otrzymaïem równieĝ INTERFEJS UJAWNIAJkCY ZAMIAR
do ksiÚgowania w starym systemie
Programista 1: InteresujÈce. Jakiego wiÚc stylu wywoïania zamierzasz
uĝyÊ dla tych reguï ksiÚgowania?
Programista 2: Nie dotarïem jeszcze aĝ tak daleko.
Programista 1: Dla sum przyrostowych (Accrual) dobrze dziaïaïoby
wywoïanie ochocze, poniewaĝ skrypt wïaĂciwie nakazuje aktywom
(Asset) ich wstawienie, jednak nie dziaïaïoby dla pïatnoĂci, które
wstawiane sÈ w ciÈgu dnia.
Programista 2: Nie sÈdzÚ, abyĂmy chcieli tak ĂciĂle zwiÈzaÊ metodÚ
obliczeniowÈ ze skryptem wsadowym. GdybyĂmy kiedykolwiek chcieli
wywoïaÊ obliczenia odsetek o innej porze, wprowadziïoby to duĝe
zamieszanie. Równieĝ koncepcyjnie nie wydaje siÚ to wïaĂciwe.
Programista 1: Brzmi to jak wywoïanie na podstawie reguïy ksiÚgowania.
To skrypt nakazuje kaĝdej z reguï wykonanie, a reguïa odszukuje
wïaĂciwe nowe wpisy i wykonuje zadanie. To prawie tak, jak nary-
sowaïeĂ.
Programista 2: W ten sposób unikniemy tworzenia w projekcie skryptu
wielu zaleĝnoĂci, a skrypt bÚdzie wszystko kontrolowaÊ. Brzmi
logicznie.
Programista 1: WciÈĝ nie do koñca rozumiem interakcjÚ tych obiektów
z obiektami Account oraz Entry.
Programista 2: Ja teĝ. Przykïady w ksiÈĝce utworzyïy bezpoĂrednie
poïÈczenie pomiÚdzy kontami oraz reguïami ksiÚgowymi. To caïkiem
logiczne, chociaĝ nie sÈdzÚ, aby sprawdziïo siÚ w naszym przypadku.
Przecieĝ za kaĝdym razem musimy tworzyÊ te obiekty z nowych
danych, wiÚc musielibyĂmy jednoczeĂnie odszukiwaÊ pasujÈcÈ reguïÚ,
aby jÈ skojarzyÊ. W tym czasie to obiekt Asset ma informacje o zawar-
toĂci kaĝdego z kont i tylko on wie, która reguïa powinna byÊ zastoso-
wana. Ale to niewaĝne... Co w takim razie z resztÈ modelu?
Programista 1: Nie lubiÚ wytykaÊ, jednak nie sÈdzÚ, abyĂmy poprawnie
uĝywali „metody”. SÈdzÚ, ĝe pierwotnym zaïoĝeniem byïo, aby metoda
obliczaïa kwotÚ do zaksiÚgowania — dajmy na to, 20-procentowy
podatek od dochodu. Jednak w naszym przypadku jest to prosta
czynnoĂÊ, poniewaĝ zawsze ksiÚgujemy caïÈ kwotÚ. SÈdzÚ, ĝe to
reguïa ksiÚgowania powinna sama wiedzieÊ, do którego konta
powinny byÊ zaksiÚgowane Ărodki, co odpowiada naszej „ksiÚdze
rachunkowej”.
699b15244ae879c728b313782703a4d7
6
Programista 2: No tak. Jeżeli więc to reguła księgowania jest odpowie-
dzialna za wybór odpowiedniej księgi rachunkowej, to w takim razie
prawdopodobnie nie potrzebujemy wcale tej metody. W rzeczywi-
stości cała logika wyboru właściwej nazwy księgi rachunkowej staje
się coraz bardziej skomplikowana. Już teraz jest ona kombinacją
rodzaju przychodu (opłata lub odsetki) i „klasy aktywów” (kategorii
przypisywanej przez biznes do każdego z aktywów). Mam nadzieję,
że to jedno z miejsc, w którym nowy model nam pomoże.
Programista 1: W porządku. W takim razie skoncentrujmy się właśnie
na tym. Reguła księgowania odpowiedzialna jest za wybór księgi
rachunkowej na podstawie atrybutów konta. Na tę chwilę możemy
wykorzystać ją do prostego sposobu obsługi klasy aktywów, a także
rozróżnienia pomiędzy odsetkami i opłatami. W przyszłości będziemy
mieć MODEL OBIEKTOWY, który będziemy mogli dodatkowo
rozszerzyć, aby obsługiwał bardziej złożone przypadki.
Programista 2: Muszę jeszcze nad tym chwilę pomyśleć. Pozwól mi to
jeszcze raz rozważyć i doczytać wzorzec, a następnie przyjść po
ponowne uwagi. Czy mógłbym porozmawiać o tym z Tobą jeszcze
raz jutro po południu?
W ciągu kolejnych dwóch dni dwoje programistów rozpracowywało
model i refakturowało kod w taki sposób, aby skrypt przeszukiwał aktywa,
do każdego z nich wysyłał kilka oczywistych komunikatów, a następnie
zatwierdzał transakcje w bazie danych. Cała złożoność została przenie-
siona do warstwy dziedziny, w której dzięki modelowi obiektowemu stała
się zarówno bardziej przejrzysta, jak i bardziej abstrakcyjna.
Rysunek 11.10.
Diagram klas
z regułami
księgowymi
699b15244ae879c728b313782703a4d7
6
Rysunek 11.11.
Diagram sekwencji Programiści odeszli znacznie od szczegółów przedstawionych w książce
ukazujący o wzorcach analitycznych, jednak oboje czuli, że wciąż zachowali istotę
wywołanie reguły pomysłu. Odrobinę nie podobało im się jednak dodanie do aktywów
(Asset) odpowiedzialności za wybór reguły księgowania. Zdecydowali się
na takie podejście, ponieważ to klasa Asset miała wiedzę na temat natury
każdego z kont (dla opłat lub odsetek) i była naturalnym punktem dostępu
dla skryptu. Skojarzenie obiektu reguły bezpośrednio z kontem wyma-
gałoby współdziałania obiektu Asset dla każdej z instancji obiektu (każdo-
razowo podczas uruchomienia skryptu). W zamian pozwolono obiektowi
Asset odszukać dwie odpowiednie reguły przy użyciu ich SINGLETONA
i przekazać je do odpowiedniego konta. Wydawało się to czynić kod zdecy-
dowanie bardziej bezpośrednim i to właśnie zdecydowało o takiej prag-
matycznej decyzji.
Obydwoje jednak czuli, iż lepiej byłoby logicznie skojarzyć reguły
księgowania z kontem, a klasie Asset pozostawić zadanie tworzenia sum
przyrostowych. Mieli również nadzieję, iż kolejne refaktoryzacje oraz lepsze
zrozumienie dziedziny przybliżą ich ponownie do takiej postaci i pokażą im
sposób na wprowadzenie jasnego podziału bez utraty oczywistości kodu.
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.
699b15244ae879c728b313782703a4d7
6
Powinien również ułatwić Ci zrozumienie konsekwencji wyborów imple-
mentacyjnych, co oszczędzi Ci bólu w trakcie projektu.
Wszystko to będzie stanowić wkład w proces przetwarzania wiedzy
oraz refaktoryzacji ku głębszemu zrozumieniu, a tym samym stymulować
proces rozwoju oprogramowania. Rezultat bardzo często przybiera postać
udokumentowaną we wzorcu analitycznym, lecz dostosowaną do oko-
liczności. Niekiedy rezultat nie odwołuje się do samego wzorca anali-
tycznego, chociaż jest inspirowany spostrzeżeniami wyniesionymi z jego
analizy.
Istnieje jednak rodzaj zmiany, którego powinieneś unikać. Jeżeli uży-
wasz pojęcia z dobrze znanego wzorca analitycznego, staraj się utrzymywać
w niezmienionej postaci jego podstawową intencję. Możesz jednak zmie-
niać je powierzchownie. Istnieją dwie przyczyny takiego zachowania. Po
pierwsze, we wzorcu może być zawarta wiedza, która pozwoli Ci uniknąć
problemów. Po drugie, i co ważniejsze, używany przez Ciebie JĘZYK
WSZECHOBECNY zostanie znacznie rozszerzony, jeżeli będzie zawierać
tylko pojęcia szeroko rozumiane lub jeżeli przynajmniej zostaną one
wyjaśnione w przejrzysty sposób. Jeśli jednak w drodze naturalnej ewolucji
ulegną zmianie definicje modelu, zmień również ich nazwy.
O modelach obiektowych napisano już wiele. Niektóre z nich są
dedykowane pewnym rodzajom aplikacji lub dziedzinom przemysłu, a inne
są całkiem ogólne. Większość z nich zawiera podstawową ideę, jednak tylko
nieliczne wychwytują rozumowanie kryjące się za wyborami, a także kon-
sekwencje ich dokonania, co stanowi najważniejszą przewagę wzorców
analitycznych. Większość z tych rozbudowanych wzorców analitycznych
mogłaby się nam przydać do uratowania nas przed ponownym wynajdo-
waniem koła. Byłbym zdziwiony, widząc obszerny katalog takich wzorców,
jednak mogą istnieć mniejsze, właściwe dla danego przemysłu. Wzorce dla
niektórych dziedzin, które dotykają różnych rodzajów aplikacji, mogłyby
być szeroko rozpowszechniane.
Ten rodzaj ponownego wykorzystania już raz sformalizowanej wiedzy
różni się znacznie od prób wykonywanych przy użyciu szkieletów apli-
kacyjnych lub komponentów, za wyjątkiem tego, że w obu przypadkach
dostarczałyby one nam zalążek pomysłu, który niekoniecznie mógłby być
dla nas na początku oczywisty. Model, nawet w postaci ogólnego szkie-
letu, stanowi kompletną i działającą całość, natomiast analiza jest zesta-
wem fragmentów modelu. Wzorce analityczne skupiają się na najbardziej
krytycznych oraz trudnych decyzjach i wskazują na istniejące możliwe
wybory alternatywne. Przewidują również konsekwencje ich użycia, chro-
niąc nas przed koniecznością sprawdzenia ich samemu, co mogłoby być
dla nas bardzo kosztowne.
699b15244ae879c728b313782703a4d7
6
348 ROZDZIAŁ 11. STOSOWANIE WZORCÓW ANALITYCZNYCH
699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 12.
Powiązanie wzorców
projektowych
z modelem
349
699b15244ae879c728b313782703a4d7
6
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.
699b15244ae879c728b313782703a4d7
6
STRATEGIA
(zwana również POLITYKĄ)
699b15244ae879c728b313782703a4d7
6
technicznych. W tym miejscu użyjemy go w roli pojęcia w modelu i odzwier-
ciedlimy w ten sposób w kodzie implementującym ten model. Mamy
bowiem tę samą potrzebę oddzielenia części procesu bardziej zmiennej
od tej stabilniejszej.
Dlatego:
Zamodeluj zmienną część procesu w oddzielnym obiekcie
„strategia”. Wyjmij z niego regułę oraz zachowanie, którym
zarządza. Zaimplementuj regułę w postaci wymiennego procesu
uwzględniającego wzorzec projektowy STRATEGIA. Wiele odmian
obiektu strategii będzie reprezentować różne sposoby wykonania
danego procesu.
Tradycyjny obraz STRATEGII jako wzorca projektowego koncen-
truje się głównie na umożliwieniu zastępowania różnych wersji algoryt-
mów, natomiast jego użycie w charakterze wzorca dziedziny dotyka moż-
liwości wyrażenia pewnego pojęcia, będącego zazwyczaj procesem lub
regułą polityki.
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
699b15244ae879c728b313782703a4d7
6
opcją najszybszą a najtańszą. Znacznie więcej problemów sprawi nam
dodanie nowych kryteriów, pozwalających na bardziej subtelny wybór
pomiędzy trasami.
Jedną z możliwości będzie oddzielenie tych parametrów konfigura-
cyjnych do STRATEGII. Wtedy mogłyby zostać wyrażone bezpośrednio,
co umożliwi przekazywanie ich do usługi trasowania (Routing Service)
w charakterze parametru.
W tym momencie usługa trasowania (Routing Service) obsługuje
wszystkie zapytania w ten sam bezwarunkowy sposób, poszukując ciągu
etapów podróży z najmniejszą wagą, obliczoną przez politykę wagi odcinka
(Leg Magnitude Policy).
Ten projekt ma te same zalety, które leżały u podstaw wzorca
STRATEGIA w książce pt. Wzorce projektowe. Działanie usługi trasowa-
nia (Routing Service) może być teraz konfigurowane oraz rozszerzane
poprzez instalację odpowiedniej polityki wagi odcinka (Leg Magnitude
Policy), co zwiększa jej uniwersalność. STRATEGIE przedstawione na
rysunku 12.2 (najszybsza i najtańsza) są najbardziej oczywistymi. Bardzo
prawdopodobne jest istnienie jeszcze dodatkowych, równoważących szyb-
kość oraz koszt. Istnieje jeszcze wiele innych czynników, na przykład
nastawienie na rezerwację przewozu ładunków przy użyciu własnego
transportu firmy zamiast wynajmowania w tym celu usług transportowych
od innych firm przewozowych. Oczywiście, takie modyfikacje mogłyby
zostać również wykonane bez odwoływania się do STRATEGII, jednak
ich logika musiałaby zostać umieszczona w wewnętrznym kodzie usługi
trasowania (Routing Service), co znacznie skomplikowałoby jej interfejs.
Oddzielenie tej logiki znacznie upraszcza kod i czyni go łatwiejszym do
testowania.
Została również jawnie wyróżniona podstawowa i ważna reguła
w dziedzinie, jaką jest logika wyboru etapu podróży podczas tworzenia jej
planu. Model przekazuje teraz wiedzę, że konkretny atrybut reprezentujący
dany etap (prawdopodobnie wyliczony na podstawie innych argumentów),
przedstawiony w postaci pojedynczej liczby, stanowi podstawę trasowa-
nia. Umożliwia to utworzenie prostego twierdzenia w języku dziedziny,
określającego zachowanie usługi trasowania: Usługa trasowania wybiera
plan podróży o minimalnej wartości wagi jej odcinków na podstawie
wybranej STRATEGII.
Uwaga: W tej dyskusji założyliśmy, że usługa trasowania rzeczy-
wiście analizuje etapy w trakcie wyszukiwania najlepszej trasy podróży.
Takie podejście jest proste koncepcyjnie i prawdopodobnie mogłoby zostać
w rozsądny sposób zaimplementowane w prototypie, jednak jego wydajność
699b15244ae879c728b313782703a4d7
6
Rysunek 12.2.
Alternatywne
STRATEGIE
(POLITYKI)
przekazywane
w postaci argumentu
***
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
699b15244ae879c728b313782703a4d7
6
podejścia do implementacji zawarta w książce pt. Wzorce projektowe ma
zastosowanie w przypadku używania ich do modelowania dziedziny.
Dzieje się tak, ponieważ wciąż używamy STRATEGII. Nasza motywa-
cja jest częściowo inna i może mieć wpływ na pewne decyzje, jednak
możemy skorzystać z całego doświadczenia zawartego w danym wzorcu
projektowym.
699b15244ae879c728b313782703a4d7
6
KOMPOZYT
699b15244ae879c728b313782703a4d7
6
skojarzonych obiektów łatwo jest przeglądać je rekurencyjnie, lecz czy jest
to rzeczywiście prawdziwa hierarchia zgodna ze wzorcem część-całość
(ang. part-whole)? Czy już odnalazłeś abstrakcję, w której wszystkie części
są tego samego typu pojęciowego? Jeżeli odpowiesz twierdząco, wtedy
wzorzec KOMPOZYT sprawi, że ten aspekt modelu stanie się zdecy-
dowanie bardziej przejrzysty i pozwoli Ci skorzystać ze starannie prze-
myślanych decyzji projektowych oraz implementacyjnych tego wzorca
projektowego.
Dlatego:
Zdefiniuj typ abstrakcyjny obejmujący wszystkie składowe
KOMPOZYTU. Metody zwracające jakiekolwiek informacje są
zaimplementowane w kontenerach, aby przekazywać zagrego-
wane dane o ich zawartości. Węzły „liści” implementują te metody
na podstawie swoich własnych wartości. Kod klienta używa typu
abstrakcyjnego i nie ma potrzeby rozróżniania liści od kontenerów.
Na poziomie strukturalnym jest to relatywnie oczywisty wzorzec,
jednak projektanci nie zmuszają się często do tego, aby podeprzeć ten
wzorzec na poziomie operacyjnym. KOMPOZYT oferuje to samo zacho-
wanie na każdym poziomie strukturalnym, a istotne zapytania mogą być
kierowane zarówno do małych, jak i dużych części KOMPOZYTU w spo-
sób przezroczysty, odzwierciedlających główny charakter wzorca. Rygory-
styczna symetria jest kluczem do jego potęgi.
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
699b15244ae879c728b313782703a4d7
6
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.
699b15244ae879c728b313782703a4d7
6
Rysunek 12.6.
Model obiektowy obrazujący wszystkie te różnice staje się skom- Koncepcja trasy
plikowany. według ekspertów
biznesowych
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
Rysunek 12.9.
Instancje
reprezentujące
całą trasę
***
699b15244ae879c728b313782703a4d7
6
WARTOŚCI, a nie dla ENCJI. Porównaj go z KOMPOZYTEM, w któ-
rym obiekty pojęciowe są złożone z innych obiektów tego samego rodzaju.
W tym przypadku wzorzec ma zastosowanie zarówno do modelu, jak
i implementacji, co jest zasadniczą cechą wzorca dziedzinowego.
699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 13.
Refaktoryzacja
ku głębszemu
zrozumieniu
363
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
może półtorej godziny. Następnie szkicują diagramy UML, starając się
przejść przez różne scenariusze przy użyciu obiektów. Upewniają się
również, czy ekspert dziedzinowy rozumie ich model i czy uznaje go za
przydatny. Kiedy już odnajdą coś, z czego będą zadowoleni, wracają i zaczy-
nają to kodować. Mogą również zdecydować się na rozważanie tego pro-
blemu przez kilka dni i wracają do biurek, aby pracować nad czymś innym.
Po kilku dniach grupa zbiera się ponownie i jeszcze raz przechodzi przez
to samo ćwiczenie. Są już wtedy bardziej pewni swoich racji, przespawszy
się odrobinę z problemem, i ostatecznie dochodzą do pewnych wniosków.
Na koniec wracają do swoich komputerów i kodują nowy projekt.
Dla zapewnienia produktywności tego procesu kluczowe znaczenie
ma kilka kwestii:
Własna determinacja. Mały zespół może być zebrany w locie w celu
rozwiązania problemu dziedzinowego. Ten sam zespół może praco-
wać przez kilka dni, a następnie się rozproszyć. Nie ma potrzeby,
aby tworzyć długotrwałe, rozbudowane struktury organizacyjne.
Zakres pracy oraz sen. Dwa lub trzy krótkie spotkania zorganizowane
w ciągu kilku dni powinny w efekcie przynieść projekt wart wypró-
bowania. Przeciąganie procesu wcale nie pomaga. Jeżeli utkniesz
w miejscu, być może oznacza to, że wziąłeś na siebie zbyt wiele
rzeczy naraz. Wybierz mniejsze aspekty dziedziny i skoncentruj się
na nich.
Używanie JĘZYKA WSZECHOBECNEGO. Dołączenie do dyskusji
członków innych zespołów (w szczególności eksperta dziedzinowego)
tworzy niepowtarzalną możliwość przećwiczenia oraz udoskonale-
nia JĘZYKA WSZECHOBECNEGO. Końcowym rezultatem tego
wysiłku jest takie ulepszenie tego JĘZYKA, które programiści zabiorą
z powrotem i sformalizują w kodzie.
We wcześniejszych rozdziałach tej książki zaprezentowaliśmy już
kilka dialogów, w których programiści wraz z ekspertami dziedzinowymi
poszukiwali lepszych modeli. Zaawansowana burza mózgów jest dyna-
miczna, nieustrukturyzowana i niesamowicie produktywna.
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ą
699b15244ae879c728b313782703a4d7
6
i jej dalszego przetwarzania w celu znalezienia rozwiązania odpowiedniego
w danej sytuacji.
Pomysły na temat samej dziedziny możesz czerpać z książek lub
innych źródeł wiedzy. Chociaż praktycy w danej dziedzinie mogli jeszcze
nie utworzyć pasującego do programu modelu, to jednak mogli w war-
tościowy sposób zorganizować już pojęcia dziedziny lub też odnaleźć
przydatne abstrakcje. Zasilanie w ten sposób procesu przetwarzania wie-
dzy prowadzi do uzyskania bogatszych i szybszych wyników, wyglądają-
cych również bardziej znajomo dla ekspertów dziedzinowych.
Niekiedy możesz wykorzystać doświadczenia innych osób, zacho-
wane w postaci wzorców analitycznych. Ten rodzaj danych wejściowych
do procesu przypomina efekt uzyskany dzięki lekturze książek o dziedzinie,
jednak w tym przypadku jest nastawiony szczególnie na proces tworzenia
oprogramowania, więc powinien być oparty bezpośrednio na doświad-
czeniu w implementowaniu aplikacji w danej dziedzinie. Wzorce ana-
lityczne mogą dostarczyć bardziej subtelne pojęcia modelowe, a także
pomóc w uniknięciu wielu błędów. Niemniej jednak nie podadzą one
prostych przepisów, a jedynie dostarczą dane wejściowe dla procesu prze-
twarzania wiedzy.
Kiedy już wszystkie elementy układanki zostaną dopasowane, praca
nad problemami modelowymi oraz projektowymi musi być kontynu-
owana równolegle. Nie oznacza to ponownie wynajdowania wszystkiego
od początku. Bardzo często w sytuacji, gdy wzorce projektowe pasują
zarówno do potrzeb implementacyjnych, jak i pojęć modelowych, mogą
one zostać zastosowane w warstwie dziedziny.
W analogiczny sposób, jeżeli do pewnej części dziedziny pasuje
powszechnie wykorzystywany formalizm w rodzaju zasad arytmetyki lub
logiki predykatów, również on może być do tej części zastosowany.
W efekcie prowadzi to do utworzenia zwięzłych oraz łatwych do zrozu-
mienia modeli.
699b15244ae879c728b313782703a4d7
6
łatwiej jest również przewidzieć konsekwencje jego zmiany. Taki projekt
pomaga także pozbyć się przeciążenia umysłowego, głównie dlatego, że
redukuje zależności oraz efekty uboczne. Jest on oparty na dogłębnym
modelu dziedziny, rozdrobnionym jedynie w tych miejscach, które są
istotne dla użytkowników. Prowadzi to do uelastycznienia miejsc podle-
gających największym zmianom oraz prostego modelu w miejscach
pozostałych.
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
699b15244ae879c728b313782703a4d7
6
dogłębnego”, do którego użycia nie będziesz potrafił przekonać eksper-
tów dziedzinowych. Nigdy nie bądź pewny swoich racji, wychodząc poza
swoją strefę komfortu w kierunku faworyzowania refaktoryzacji.
699b15244ae879c728b313782703a4d7
6
IV
Projekt
strategiczny
699b15244ae879c728b313782703a4d7
6
Kiedy system stanie się już nazbyt skomplikowany, aby znać go dokładnie
na poziomie poszczególnych obiektów, potrzebować będziemy technik
używanych do modyfikacji oraz zrozumienia dużych modeli. W tej części
książki poznamy zasady pozwalające zastosować procesy modelowania
do bardzo złożonych dziedzin. Większość tego rodzaju decyzji musi być
podejmowana na poziomie zespołu lub nawet negocjowana pomiędzy
różnymi zespołami. Na tym poziomie projekt bardzo często łączy się
bowiem z polityką.
Najbardziej ambitne systemy klasy Enterprise mają na celu ścisłą
integrację systemu rozpościerającego się na cały obszar biznesowy. Dla
prawie każdej organizacji model taki będzie zbyt duży i złożony do zarzą-
dzania lub nawet do zrozumienia w całości. Dlatego musi on zostać podzie-
lony na mniejsze części, zarówno pod względem pojęciowym, jak i imple-
mentacyjnym. W takim przypadku wyzwaniem będzie utworzenie tego
rodzaju modułowości bez utraty zalet wynikających z integracji, w sposób
pozwalający różnym częściom systemu współpracować ze sobą w celu
wspierania różnych operacji biznesowych. Monolityczny model obejmu-
jący całą dziedzinę będzie bardzo nieporęczny, a także przeładowany róż-
nymi subtelnymi duplikacjami i sprzecznościami. Natomiast zestawowi
małych rozłącznych podsystemów, połączonych ze sobą za pomocą tym-
czasowych interfejsów, brakować będzie możliwości rozwiązywania pro-
blemów na poziomie całego przedsiębiorstwa. W każdym z miejsc inte-
gracji będą również narastać problemy ze spójnością modelu. Pułapek
obu tych skrajnych rozwiązań można uniknąć, decydując się na uporząd-
kowaną, stale rozwijaną strategię projektową.
Nawet na takim poziomie projekt sterowany dziedziną nie tworzy
modeli niezwiązanych z implementacją. Każda decyzja musi mieć bezpo-
średni wpływ na rozwój systemu, gdyż w przeciwnym wypadku będzie
nieistotna. Pryncypia projektu strategicznego muszą kierować decyzjami
projektowymi, aby zredukować wzajemną zależność części modelu i wzmoc-
nić ich przejrzystość bez utraty krytycznej zgodności operacyjnej oraz
synergii. Model natomiast musi zostać skoncentrowany na zadaniu wychwy-
cenia pojęciowej istoty systemu, będącej jego „wizją”. Wszystko to nie może
również spowalniać projektu. Aby pomóc w osiągnięciu tego celu, w części IV
omówimy trzy szerokie zagadnienia: kontekst, destylację oraz struktury
dużej skali.
Kontekst, będący najmniej oczywistym z pryncypiów, jest w rzeczy-
wistości najbardziej fundamentalny. Udany model — duży czy mały —
musi być wewnątrz spójny logicznie, bez żadnych sprzecznych lub nakła-
dających się definicji. Systemy klasy Enterprise integrują niekiedy inne
podsystemy o różnym pochodzeniu lub dysponują tak różnymi od siebie
370 CZĘŚĆ IV
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
ZWIĄZANE umożliwiają wykonywanie prac projektowych w różnych
częściach systemu bez uszkadzania modelu lub jego niezamierzonej frag-
mentacji. Dodanie tych pojęć do JĘZYKA WSZECHOBECNEGO zespołu
pozwala programistom na pracę nad ich własnymi rozwiązaniami.
372 CZĘŚĆ IV
699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 14.
Utrzymywanie
integralności modelu
373
699b15244ae879c728b313782703a4d7
6
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-
699b15244ae879c728b313782703a4d7
6
nie określonych procesów. Całkowita unifikacja modelu dziedziny
dla dużego systemu nie będzie ani możliwa, ani też rozsądna
kosztowo.
Niekiedy ludzie walczą z tym faktem. Większość osób postrzega
koszt wymagany przez wiele modeli jako ograniczanie integracji oraz
zaburzanie komunikacji. Poza tym posiadanie więcej niż jednego modelu
wydaje się nieeleganckie. Ten opór przed wieloma modelami prowadzi
niekiedy do bardzo ambitnych prób unifikacji całego oprogramowania
w dużym projekcie w jednym modelu. Wiem, że przyczyniłem się do
takiego podejścia. Rozważmy jednak następujące ryzyka:
1. W ten sposób unifikacja może dotknąć zbyt wielu starych miejsc
w kodzie.
2. Duże projekty mogą zostać sparaliżowane, ponieważ narzut zwią-
zany z koordynacją może przekraczać ich możliwości.
3. Aplikacje ze specjalizowanymi wymaganiami mogą używać modeli,
które nie będą w pełni zaspokajać ich potrzeb, co zmusi je do umiesz-
czania tej logiki gdzieś indziej.
4. Próba zaspokojenia wszystkich potrzeb pojedynczym modelem
może prowadzić do złożonych wyborów, sprawiających, że model
będzie trudny do zastosowania.
Co więcej, rozbieżności w modelu mogą wynikać z fragmentacji
z przyczyn politycznych lub różnych priorytetów związanych z zarządza-
niem modelem, a nie z przyczyn technicznych. Pojawienie się różnych
modeli może być także wynikiem organizacji zespołu oraz procesu roz-
woju oprogramowania. Z tej przyczyny nawet w sytuacji, gdy nie ma
żadnych przeciwwskazań technicznych do pełnej integracji, projekt może
wciąż używać wielu modeli.
Uznając fakt, iż nie jest możliwe utrzymanie zunifikowanego modelu
dla całego przedsiębiorstwa, nie musimy zdawać się na łaskę różnych
okoliczności. Dzięki połączeniu proaktywnych decyzji dotyczących tego,
co powinno zostać zunifikowane, z pragmatycznym podejściem do tego,
czego nie warto uwspólniać, możemy stworzyć jasny i spójny obraz sytu-
acji. Mając go, możemy również upewnić się, że części, które chcemy zuni-
fikować, zostaną ujednolicone, natomiast inne nie będą wprowadzać
zamieszania ani powodować błędów w modelu.
Musimy również dysponować sposobem na oznaczenie granic oraz
relacji pomiędzy różnymi modelami. Dlatego trzeba ostrożnie wybrać
strategię, a następnie jej przestrzegać.
699b15244ae879c728b313782703a4d7
6
W tym rozdziale zdefiniujemy techniki umożliwiające rozpoznanie,
zakomunikowanie oraz wybór granic modelu oraz jego relacji z innymi.
Wszystko rozpoczyna się od zbadania bieżącego obszaru projektu. KON-
TEKST ZWIĄZANY (ang. BOUNDED CONTEXT) definiuje zakres
zastosowania każdego z modeli, natomiast MAPA KONTEKSTÓW
(ang. CONTEXT MAP) przekazuje globalny widok na konteksty pro-
jektu oraz ich związki. Ograniczenie wieloznaczności może wpłynąć na
sposób działania projektu, jednak nie musi to wystarczać. Gdy tylko
będziemy już dysponować KONTEKSTEM ZWIĄZANYM, w utrzyma-
niu zunifikowanego modelu pomoże nam proces CIĄGŁEJ INTEGRA-
CJI (ang. CONTINUOUS INTEGRATION).
Następnie, mając już ustabilizowaną sytuację, możemy rozpocząć
migrację do bardziej efektywnych strategii dla KONTEKSTÓW ZWIĄ-
ZANYCH, a także tworzenie ich relacji, począwszy od ściśle związanych
kontekstów zaprzyjaźnionych z JĄDRAMI WSPÓŁDZIELONYMI (ang.
SHARED KERNEL) do luźno powiązanych modeli idących ODDZIEL-
NYMI DROGAMI (ang. SEPARATE WAYS).
Rysunek 14.1.
Mapa nawigacji
dla wzorców
integralności
modelu
699b15244ae879c728b313782703a4d7
6
KONTEKST ZWIĄZANY
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ę.
699b15244ae879c728b313782703a4d7
6
Istotniejsza jest róĝnica w modelach obu systemów. W sytuacji, gdy nie-
spójnoĂÊ wystÚpuje nie w systemie zewnÚtrznym, lecz w tym samym
obszarze kodu, jeszcze trudniej bÚdzie jÈ rozpoznaÊ. To wciÈĝ zdarza siÚwe
wszystkich duĝych zespoïach projektowych
W duĝych projektach w uĝyciu jest wiele modeli. W momen-
cie poïÈczenia kodu bazujÈcego na róĝnych modelach aplikacja
staje siÚ peïna bïÚdów, zawodna i trudna do zrozumienia. Zabu-
rzona jest równieĝ komunikacja pomiÚdzy czïonkami zespoïu.
Bardzo czÚsto nie jest jasne, w którym kontekĂcie dany model nie
powinien zostaÊ zastosowany.
Poraĝka w utrzymywaniu prostego modelu da o sobie ostatecznie znaÊ
w trudnoĂciach z uruchomieniem kodu. Jednak sam problem zaczyna
siÚ od sposobu organizacji zespoïów oraz ich wzajemnej komunikacji. Dla-
tego aby wyjaĂniÊ kontekst modelu, musimy odwoïywaÊ siÚ do obu pro-
jektów oraz ich produktów koñcowych (kodu, schematu bazy danych itp.).
Model ma zastosowanie w kontekĂcie. Ten natomiast moĝe byÊ
okreĂlonÈ czÚĂciÈ kodu lub zadaniem okreĂlonego zespoïu. W przypadku
modelu wymyĂlonego w dyskusji zespoïowej kontekst moĝe byÊ ograni-
czony do tej okreĂlonej rozmowy. Kontekstem modelu uĝywanego w tej
ksiÈĝce bÚdzie podrozdziaï, w którym jest on zawarty, oraz wszelka dalsza
dyskusja o nim. Kontekst modelu okreĂla zestaw warunków, które muszÈ
zaistnieÊ, aby moĝna byïo stwierdziÊ, ĝe definicje modelu majÈ okreĂlone
znaczenie.
Zanim zaczniemy rozwiÈzywaÊ problemy wielu modeli, musimy
jawnie zdefiniowaÊ zakres konkretnego modelu w postaci ograniczonej
czÚĂci systemu programistycznego, w którym dany model ma zastoso-
wanie i bÚdzie tak zunifikowany, jak to tylko moĝliwe. Powyĝsza defini-
cja musi byÊ ujednolicona z organizacjÈ zespoïu.
Dlatego:
Zdefiniuj jasno kontekst, w którym model ma zastosowanie.
Ustanów granice ze wzglÚdu na organizacjÚ zespoïu, wykorzy-
stanie okreĂlonych czÚĂci aplikacji lub teĝ fizycznej obecnoĂci
na przykïad w kodzie lub schematach bazy danych. W tych gra-
nicach utrzymuj model w sposób spójny, niezmÈcony przez pro-
blemy istniejÈce na zewnÈtrz.
KONTEKST ZWIkZANY (ang. BOUNDED CONTEXT) ogra-
nicza zastosowanie okreĂlonego modelu w taki sposób, aby czïonkowie
zespoïu mieli jasne i wspólne zrozumienie tego, co musi byÊ spójne, a co
odwoïuje siÚ do innych KONTEKSTÓW. WewnÈtrz KONTEKSTU staraj
siÚ utrzymywaÊ model w postaci logicznie zunifikowanej i nie przejmuj siÚ
jego zastosowaniem na zewnÈtrz granic. W innych KONTEKSTACH
699b15244ae879c728b313782703a4d7
6
mogą mieć zastosowanie inne modele, z inną terminologią dotyczącą ich KONTEKSTY
ZWIĄZANE nie są
pojęć oraz reguł, a także z innymi dialektami JĘZYKA WSZECHOBEC-
MODUŁAMI.
NEGO. Te dwa pojęcia
Dzięki wyznaczeniu bezpośredniej granicy będziesz w stanie utrzy- są niekiedy ze sobą
mać model w przejrzystej postaci i z tego powodu będzie on sugestywny mylone. Stanowią one
wszędzie tam, gdzie ma zastosowanie. W tym samym czasie unikniesz jednak dwa różne
wzorce, powodowane
też zamieszania w trakcie przechodzenia pomiędzy różnymi KONTEK-
różnymi motywami.
STAMI. Integracja ponad granicami z pewnością będzie wymagać przetłu- Rzeczywiście gdy dwa
maczenia, którego będzie można dokonać w sposób bezpośredni i jawny. zestawy obiektów
tworzą inne modele,
wtedy prawie zawsze
Przykład
umieszczane są
Kontekst rezerwacji w oddzielnych
MODUŁACH.
Firma zajmująca się transportem morskim rozpoczęła wewnętrzny pro- W ten sposób definiuje
jekt mający na celu utworzenie nowej aplikacji do rezerwacji ładunków. się różne przestrzenie
Aplikacja ma być sterowana przez model obiektowy. Jaki będzie KON- nazw (niezbędne
dla różnych
TEKST ZWIĄZANY, do którego odnosić będzie się model? Aby odpowie- KONTEKSTÓW),
dzieć na to pytanie, musimy przyjrzeć się temu, co dzieje się w projekcie. a także pewną linię
Pamiętaj jednak, że będzie to spojrzenie na rzeczywisty projekt, a nie taki, podziału.
który powinien być idealny. Jednak MODUŁY
organizują również
Jeden zespół projektowy pracuje nad samą aplikacją do rezerwacji. elementy wewnątrz
Nie zamierza on wcale modyfikować obiektów modelu, jednak tworzona jednego modelu i nie
aplikacja musi wyświetlać i przetwarzać zawartość tych obiektów. Zespół zawsze mogą świadczyć
jest konsumentem modelu. Model ten będzie poprawny wewnątrz apli- o intencji wydzielenia
KONTEKSTÓW.
kacji (jej podstawowym konsumencie) i dlatego jego granicami będzie
Wydzielenie osobnych
aplikacja do rezerwacji. przestrzeni nazw przez
Wykonane rezerwacje muszą zostać przekazane do starego systemu MODUŁY
do śledzenia ładunku. Wcześniej podjęta została decyzja, że nowy model w KONTEKSTACH
ZWIĄZANYCH
będzie różnił się od starego, dlatego stary system będzie znajdować się
w rzeczywistości
poza jego granicami. Niezbędne tłumaczenie pomiędzy modelem nowym utrudnia odnalezienie
a starym będzie obowiązkiem zespołu utrzymującego stary system. Mecha- miejsc przypadkowej
nizm tłumaczenia nie jest sterowany modelem, czyli nie jest to jego KON- fragmentacji modelu.
TEKST ZWIĄZANY (jest jednak częścią samej granicy, co dalej omó-
wimy w trakcie przedstawiania MAPY KONTEKSTU). Tłumaczenie
jest poza KONTEKSTEM (nie jest oparte na modelu). Nierealistyczne
byłoby oczekiwanie od zespołu zajmującego się starą aplikacją wykorzysta-
nia modelu, ponieważ ich podstawowa aplikacja jest poza KONTEKSTEM.
Zespół odpowiedzialny za model zajmuje się całym cyklem życia
każdego z obiektów, a także ich zapisywaniem. Ponieważ zespół kontro-
luje schemat bazy danych, w sposób zamierzony utrzymywał on mapo-
wanie obiektowo-relacyjne w prostej postaci. Mówiąc inaczej, schemat
jest sterowany modelem i z tego powodu znajduje się w jego granicach.
699b15244ae879c728b313782703a4d7
6
Inny zespół pracuje nad modelem oraz aplikacją do tworzenia har-
monogramu podróży statków handlowych. Oba zespoły zostały sobie
przedstawione i miały za zadnie utworzenie pojedynczego zunifikowa-
nego systemu. Z tego powodu zazwyczaj koordynowały swoje zadania
i od czasu do czasu wymieniały obiekty, jednak nie robiły tego systema-
tycznie. Oba zespoły nie działają w tym samym KONTEKŚCIE ZWIĄ-
ZANYM. Stanowi to pewne ryzyko, ponieważ nie uważają również, aby
pracowały nad oddzielnymi modelami. Integracja ich pracy będzie sta-
nowić problem, chyba że utworzą procesy nadzorujące całą sytuację
(dobrym wyborem mogłoby być JĄDRO WSPÓŁDZIELONE, które
zostanie omówione w dalszej części tego rozdziału). Pierwszą czynnością
będzie jednak zrozumienie bieżącej sytuacji. Oba zespoły nie są w tym
samym KONTEKŚCIE, dlatego do czasu wykonania pewnych zmian
powinny zaprzestać usiłowań współdzielenia kodu.
KONTEKST ZWIĄZANY utworzony jest ze wszystkich aspektów
systemu sterowanych danym modelem, to znaczy obiektów modelu,
schematu bazy danych przechowującej obiekty modelu oraz aplikacji do
rezerwacji. W tym KONTEKŚCIE działają głównie dwa zespoły: mode-
lujący oraz tworzący aplikację. Informacja musi być też wymieniana z zespo-
łem utrzymującym starą aplikację do śledzenia, a ten zespół z kolei odpo-
wiada za tłumaczenie wykonywane na tej granicy i jest w tym zadaniu
wspomagany przez zespół modelujący. Nie istnieje jasno zdefiniowana
relacja pomiędzy modelami aplikacji do rezerwacji oraz tworzenia har-
monogramu podróży. Zdefiniowanie takiej relacji powinno być jednym
z pierwszych zadań obu zespołów. Do tego czasu powinny one być bardzo
ostrożne przy wymianie kodu oraz danych.
Co więc zyskaliśmy wskutek zdefiniowania tego KONTEKSTU
ZWIĄZANEGO? Zespół pracujący w KONTEKŚCIE zyskał jasność
sytuacji. Dwa zespoły wiedzą, że muszą pozostać w spójności z jednym
modelem, dzięki czemu będą uważać na podziały w trakcie podejmowa-
nia decyzji projektowych. Zespoły znajdujące się na zewnątrz granic
zyskały wolność. Nie muszą wstępować do szarej strefy ani używać tego
samego modelu, nawet jeżeli czują, że tak powinny. Jednak w tym kon-
kretnym przypadku największym zyskiem jest prawdopodobnie zdanie
sobie sprawy z ryzyka wynikającego z nieformalnego dzielenia się infor-
macjami pomiędzy zespołem tworzącym model aplikacji do rezerwacji
a zespołem pracującym nad aplikacją do tworzenia harmonogramu podróży.
Aby uniknąć problemów, muszą one zadecydować o kompromisach
w kosztach i zyskach wynikających z dzielenia się modelami i utworzyć
procesy, które tę sytuację umożliwią. Nie stanie się to bez zrozumienia
przez wszystkich, w którym miejscu leżą granice kontekstów modeli.
699b15244ae879c728b313782703a4d7
6
***
Oczywiście, granice są specjalnymi miejscami. Relacje pomiędzy KON-
TEKSTEM ZWIĄZANYM a jego sąsiadami wymagają ostrożności oraz
uwagi. MAPA KONTEKSTÓW (ang. CONTEXT MAP) wyznacza tery-
torium, przekazując szeroki obraz KONTEKSTÓW i ich połączeń, nato-
miast kilka innych wzorców określa naturę różnych relacji pomiędzy tymi
KONTEKSTAMI. Proces CIĄGŁEJ INTEGRACJI (ang. CONTINU-
OUS INTEGRATION) powoduje utrzymanie jedności modelu w KON-
TEKŚCIE ZWIĄZANYM.
Zanim jednak do tego wszystkiego dojdziemy, odpowiedzmy na
pytanie, co stanie się w przypadku zniszczenia jednolitości modelu. W jaki
sposób rozpoznać odpryski pojęciowe?
699b15244ae879c728b313782703a4d7
6
sytuacja, w której dwie czynności biznesowe nazywane były Charge
(ang. obciążenie). Jednak konflikty mogą być nawet bardziej subtelne, gdy
dwie definicje są w rzeczywistości związane z tym samym aspektem dzie-
dziny, jednak zostały zamodelowane w nieco inny sposób. Fałszywe pokre-
wieństwa prowadzą do sytuacji, w których zespoły programistów wcho-
dzą sobie w drogę, bazy danych zawierają dziwne sprzeczności, zespoły
nie mogą się ze sobą porozumieć. Termin „fałszywe pokrewieństwo” jest
zazwyczaj stosowany do języków naturalnych. Na przykład Polacy uczący
się języka czeskiego bardzo często błędnie stosują wyraz „čerstvy”. Słowo
to nie oznacza wcale, że coś jest suche (pol. czerstwy), tylko świeże. O rety!
Po rozpoznaniu problemu zespół będzie musiał podjąć decyzję.
Możesz chcieć ponownie uwspólnić model i zdefiniować proces zapo-
biegający fragmentacji. Taka fragmentacja może też być wynikiem działa-
nia grupy osób pragnących z dobrych powodów skierować model w innym
kierunku, a Ty możesz zechcieć pozwolić im pracować niezależnie. Radze-
nie sobie z tego rodzaju problemami będzie przedmiotem naszych roz-
ważań w pozostałych wzorcach w tym rozdziale.
699b15244ae879c728b313782703a4d7
6
CIĄGŁA INTEGRACJA
699b15244ae879c728b313782703a4d7
6
Jest to głównie dziedzina programowania ekstremalnego (XP).
Wiele praktyk XP skupia się na rozwiązaniu problemu utrzymywania spój-
ności projektu zmienianego stale przez wiele osób. Najczystsza odmiana
XP w najlepszy sposób utrzymuje integralność modelu w pojedynczym
KONTEKŚCIE ZWIĄZANYM. Jednakże niezależnie od tego, czy tech-
niki XP będą wykorzystywane, czy nie, istotne jest, aby posiadać proces
CIĄGŁEJ INTEGRACJI (ang. CONTINUOUS INTEGRATION).
Termin ten oznacza, że cała praca w danym kontekście jest zbierana
i ujednolicana na tyle często, aby jakiekolwiek odpryski pojęciowe zostały
szybko wyłapane i poprawione. CIĄGŁA INTEGRACJA, podobnie jak
wszystkie inne pojęcia w projektowaniu sterowanym dziedziną, funkcjo-
nuje na dwóch poziomach, tj. integracji pojęć modelu (1) oraz integracji
implementacji (2).
Pojęcia integrowane są poprzez ciągłą komunikację pomiędzy człon-
kami zespołu. Zespół musi dbać o wspólne zrozumienie stale zmieniają-
cego się modelu. Może w tym pomóc wiele praktyk, z których najbardziej
podstawową jest stałe ulepszanie JĘZYKA WSZECHOBECNEGO.
Jednocześnie zachodzi również integracja zaimplementowanych artefak-
tów poprzez proces ich systematycznego łączenia/budowania/testowania,
umożliwiający wczesne zauważenie odprysków pojęciowych. Do integra-
cji może być używanych wiele procesów, jednak te najbardziej efektywne
mają następujące cechy:
Odtwarzalną krok po kroku technikę łączenia/budowania.
Automatyczne testy jednostkowe.
Reguły narzucające rozsądny limit na czas życia niezintegrowanych
zmian.
Inną (chociaż bardzo rzadko uwzględnianą) kwestią w przypadku
procesu efektywnego będzie integracja pojęciowa.
Stałe ćwiczenie JĘZYKA WSZECHOBECNEGO w dyskusjach
na temat modelu oraz aplikacji.
Większość zwinnych projektów dysponuje przynajmniej codzien-
nym łączeniem zmian kodu wprowadzonych przez każdego programistę.
Oczywiście, częstotliwość tego procesu może być dostosowywana do
liczby zmian, pod warunkiem że niezintegrowana zmiana zostanie złą-
czona z kodem, zanim inni członkowie zespołu będą w stanie wytworzyć
istotną liczbę zmian ze sobą niezgodnych.
699b15244ae879c728b313782703a4d7
6
W PROJEKTOWANIU STEROWANYM MODELEM integracja
pojęć toruje drogę integracji implementacji, natomiast ta druga udowadnia
spójność oraz poprawność modelu oraz ujawnia wszystkie jego odstępstwa.
Dlatego:
Wprowadź proces częstego łączenia całego kodu oraz innych
artefaktów implementacyjnych z testami automatycznymi, umoż-
liwiającymi szybkie wychwycenie fragmentacji modelu. W trak-
cie, gdy pojęcia będą rozwijać się w głowach różnych osób, nie-
ustannie ćwicz JĘZYK WSZECHOBECNY w celu utworzenia
wspólnego widoku modelu.
Ostatecznie nie zwiększaj zadania ponad niezbędne minimum. CIĄ-
GŁA INTEGRACJA jest niezbędna jedynie w KONTEKŚCIE ZWIĄ-
ZANYM. Innymi problemami dotyczącymi sąsiednich KONTEKSTÓW,
włączając w to tłumaczenie pomiędzy nimi, nie trzeba zajmować się z aż
taką dużą częstotliwością.
***
CIĄGŁA INTEGRACJA mogłaby zostać zastosowana w dowolnym KON-
TEKŚCIE ZWIĄZANYM większym od zadania dla dwóch osób. Służy ona
do utrzymywania integralności pojedynczego modelu. W przypadku wspól-
nego istnienia wielu KONTEKSTÓW ZWIĄZANYCH musisz zdecy-
dować o ich relacjach oraz zaprojektować niezbędne interfejsy...
699b15244ae879c728b313782703a4d7
6
MAPA KONTEKSTÓW
699b15244ae879c728b313782703a4d7
6
chyba że podejmiemy dodatkowe wysiłki integracyjne. Większość kierow-
ników projektów intuicyjnie rozpoznaje te czynniki i w szeroki sposób
organizuje zespoły wokół podsystemów. Jednak wzajemne związki pomię-
dzy organizacją zespołu a modelem oprogramowania, a także jego pro-
jektem nie są wystarczająco widoczne. Zarówno menedżerowie, jak i człon-
kowie zespołu potrzebują jasnego spojrzenia na ciągły podział konceptualny
modelu aplikacji wraz z projektem.
Dlatego:
Zidentyfikuj każdy model używany w projekcie i zdefiniuj
jego KONTEKST ZWIĄZANY. Należą do nich również niejawne
modele podsystemów nieobiektowych. Nazwij każdy z KONTEK-
STÓW ZWIĄZANYCH i dodaj ich nazwy do JĘZYKA WSZECH-
OBECNEGO.
Zdefiniuj punkty kontaktów pomiędzy modelami oraz nakreśl
jawne tłumaczenie dla każdej komunikacji, podkreślając każde
współdzielenie obiektów.
Odwzoruj istniejące terytorium. Transformacjami zajmij się
później.
W każdym KONTEKŚCIE ZWIĄZANYM będziesz dysponować
spójnym dialektem JĘZYKA WSZECHOBECNEGO. Nazwy KON-
TEKSTÓW ZWIĄZANYCH będą same należeć do tego JĘZYKA, co
sprawi, że będziesz mógł jednoznacznie mówić o modelu każdej części
projektu, czyniąc KONTEKST zrozumiałym.
MAPA nie musi być udokumentowana w żadnej określonej formie.
Osobiście sądzę, że diagramy przypominające przedstawione w tym roz-
dziale są przydatne w wizualizacji oraz komunikacji mapy. Inni mogą
preferować bardziej tekstowe opisy lub inne reprezentacje graficzne. W nie-
których sytuacjach wystarczająca może okazać się dyskusja między człon-
kami zespołu. Poziom szczegółowości może różnić się w zależności od
potrzeb. Niezależnie od przyjętej formy MAPA musi być współdzielona
oraz rozumiana przez każdą osobę pracującą nad projektem. Musi udo-
stępniać jasną nazwę dla każdego KONTEKSTU ZWIĄZANEGO, a także
powodować, aby punkty kontaktu oraz ich natura były oczywiste.
***
Relacje pomiędzy KONTEKSTAMI ZWIĄZANYMI przyjmują wiele
postaci w zależności od problemów projektowych oraz organizacyjnych
w projekcie. W dalszej części tego rozdziału zdefiniujemy różne wzorce
relacji pomiędzy KONTEKSTAMI. Mogą one być efektywne w różnych
sytuacjach oraz udostępniać warunki do opisu relacji, które odnajdziesz
w swoich MAPACH. Należy pamiętać o tym, że MAPA KONTEKSTÓW
699b15244ae879c728b313782703a4d7
6
zawsze reprezentuje zastaną sytuację, w związku z czym odnalezione relacje
mogą początkowo nie pasować do tych wzorców. Jeżeli będą podobne,
możesz chcieć użyć nazwy wzorca, jednak nie rób tego na siłę. Najzwyczaj-
niej opisz odnalezione relacje. Później możesz zacząć migrować w kierunku
bardziej ustandaryzowanych relacji.
Co w takim razie zrobić, gdy napotkasz na odprysk — model cał-
kowicie splątany, zawierający niespójności? Wstrzymaj się na moment
i dokończ opisywanie wszystkiego. Na koniec, dysponując właściwym
widokiem globalnym, zajmij się miejscami powodującymi zamieszanie.
Mały odprysk pojęciowy można naprawić, a w celu jego podparcia usta-
nowić konkretny proces. Jeżeli relacja jest niejasna, możesz wybrać naj-
bliższy wzorzec i zmierzać ku niemu. Twoim pierwszym celem będzie
ustanowienie jasnej MAPY KONTEKSTÓW, co może oznaczać naprawę
rzeczywistych napotkanych problemów. Nie możesz jednak pozwolić,
aby ta konieczna naprawa prowadziła do całkowitej reorganizacji. Dopóki
masz jednoznaczną MAPĘ KONTEKSTÓW, która umieszcza efekty
Twojej pracy w KONTEKŚCIE ZWIĄZANYM z jawnymi relacjami
pomiędzy wszystkimi połączonymi modelami, zmieniaj tylko te zdecy-
dowane sprzeczności.
Gdy uzyskasz spójną MAPĘ KONTEKSTÓW, od razu dostrzeżesz
rzeczy, które będziesz chciał zmienić. Możesz wtedy zrealizować pożądane
zmiany w organizacji zespołów lub wprowadzić je do projektu. Pamiętaj,
aby nie zmieniać mapy, dopóki nie zaistnieje rzeczywista zmiana.
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.
699b15244ae879c728b313782703a4d7
6
Rysunek 14.2.
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).
699b15244ae879c728b313782703a4d7
6
Rysunek 14.3.
Dwa KONTEKSTY
ZWIĄZANE,
utworzone w celu
umożliwienia Oznacza to brak konieczności mapowania wszystkich rzeczy pomię-
wydajnego dzy tymi dwoma modelami, a jedynie umożliwienie wykonania dwóch
stosowania
określonych tłumaczeń:
algorytmów
do trasowania Route Specification → Lista kodów lokalizacji
699b15244ae879c728b313782703a4d7
6
Rysunek 14.4.
Tłumaczenie
zapytania do usługi
przeszukiwania sieci
(Network Traversal
Service)
Rysunek 14.5.
Tłumaczenie trasy
w usłudze
przeszukiwania
sieci połączeń
699b15244ae879c728b313782703a4d7
6
To jedynie konceptualna mapa tłumaczenia pomiędzy obydwoma
modelami. Teraz będziemy jednak musieli zaimplementować coś, co
wykona dla nas to mapowanie. W takim prostym przypadku jak ten zazwy-
czaj tworzę w tym celu pewien obiekt, a następnie znajduję lub tworzę
kolejny obiekt, funkcjonujący w charakterze usługi dla reszty podsystemu.
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();
699b15244ae879c728b313782703a4d7
6
List constraintLocations =
translator.convertConstraints(spec);
// Pobierz dostęp do NetworkTraversalService
List pathNodes =
traversalService.findPath(constraintLocations);
Itinerary result = translator.convert(pathNodes);
return result;
}
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.
699b15244ae879c728b313782703a4d7
6
zazwyczaj na granicach. Mogą one tworzyć system wczesnego ostrzegania,
szczególnie gdy polegasz na szczegółach modelu, którego sam nie kon-
trolujesz.
699b15244ae879c728b313782703a4d7
6
Relacje pomiędzy KONTEKSTAMI
ZWIĄZANYMI
Kolejne wzorce obejmują szeroki zakres strategii łączenia dwóch modeli,
które mogą obejmować całe przedsiębiorstwo. Wzorce te mają dwojakie
znaczenie. Wskazują cele dla pomyślnie zorganizowanej pracy programi-
stycznej, a poza tym zwiększają zasób językowy wykorzystywany do opisu
istniejącej organizacji.
Może się okazać, że przez przypadek lub w zamierzony sposób ist-
niejąca relacja niewiele odbiega od jednego z tych wzorców. W takim
przypadku możesz opisać ją przy użyciu jego terminologii, zwracając
szczególną uwagę na rozbieżności. Następnie, wraz z każdą małą zmianą
projektową, relacje mogą być doprowadzane bliżej do wybranego wzorca.
Z drugiej strony, możesz stwierdzić, że istniejąca relacja jest zagma-
twana i zbyt skomplikowana, dlatego konieczne może być przeorganizo-
wanie rzeczy tylko w celu umożliwienia stworzenia jednoznacznej MAPY
KONTEKSTÓW. W takim przypadku lub też w dowolnej innej sytu-
acji, gdy rozważasz reorganizację, zaprezentowane wzorce będą stanowić
szeroki wybór dostępnych możliwości, z których każdy będzie bardziej
dopasowany do różnych okoliczności. Do znaczących czynników będą
należeć poziom kontroli nad modelem, poziom oraz rodzaj współpracy
pomiędzy zespołami oraz stopień integracji funkcjonalności i danych.
Poniższy zestaw wzorców odnosi się do najbardziej powszechnych
oraz istotnych przypadków, co powinno dać Ci dobre zrozumienie, jak
poradzić sobie również z innymi sytuacjami. Specjalistyczny zespół pra-
cujący wspólnie nad ściśle zintegrowanym produktem może utworzyć
duży, zunifikowany model. Konieczność zaspokojenia potrzeb różnych
grup użytkowników oraz ograniczona możliwość koordynacji zdolności
zespołu może prowadzić do relacji typu JĄDRO WSPÓŁDZIELONE
(ang. SHARED KERNEL) lub KLIENT/DOSTAWCA (ang. CUSTO-
MER/SUPPLIER). Niekiedy dobre przyjrzenie się wymaganiom odkrywa,
że integracja systemów nie jest konieczna i najlepiej byłoby, aby oba sys-
temy poszły swoimi ODDZIELNYMI DROGAMI (ang. SEPARATE
WAYS). Oczywiście, większość projektów musi również integrować się
w pewnym stopniu ze starymi lub zewnętrznymi systemami, co może
prowadzić do wykorzystania OTWARTYCH USŁUG GOSPODARZA
(ang. OPEN HOST SERVICES) lub WARSTWY ZAPOBIEGAJĄCEJ
USZKODZENIU (ang. ANTICORRUPTION LAYER).
699b15244ae879c728b313782703a4d7
6
JĄDRO WSPÓŁDZIELONE
699b15244ae879c728b313782703a4d7
6
Dlatego:
Wyznacz podzbiór modelu dziedziny, który oba zespoły
zgodzą się współdzielić. Oczywiście, poza samym modelem doty-
czyć to będzie również związanego z nim podzbioru kodu źró-
dłowego i projektu bazodanowego. Ten obszar współdzielony
będzie mieć specjalny status i nie należy go zmieniać bez kon-
sultacji z drugim zespołem.
System funkcjonalny integruj systematycznie, jednak nieco
rzadziej niż w przypadku CIĄGŁEJ INTEGRACJI stosowanej
przez zespoły. Po wszystkich integracjach wykonuj w obu zespo-
łach testy.
To bardzo delikatna równowaga. JĄDRO WSPÓŁDZIELONE nie
może być zmieniane tak dowolnie jak inne części projektu. Decyzje będą
wymagać konsultacji z innymi zespołami. Testy automatyczne muszą być
zintegrowane, ponieważ po wykonaniu zmiany wszystkie testy obu zespo-
łów muszą skończyć się pomyślnie. Zazwyczaj zespoły dokonują zmian
na swoich własnych oddzielnych kopiach JĄDRA, cyklicznie integrując je
z drugim zespołem (na przykład w zespole, który CIĄGLE INTEGRUJE
kod codziennie lub kilka razy na dzień, JĄDRO mogłoby być integrowane
co tydzień). Niezależnie od zaplanowania integracji kodu, im wcześniej
zespoły porozmawiają o zmianach, tym lepiej.
***
JĄDRO WSPÓŁDZIELONE jest najczęściej DZIEDZINĄ GŁÓWNĄ
(ang. CORE DOMAIN), zestawem PODDZIEDZIN OGÓLNYCH
(ang. GENERIC SUBDOMAINS) lub obiema naraz (patrz rozdział 15.).
Może być również dowolną częścią modelu wymaganą przez oba zespoły.
Celem jest zmniejszenie duplikacji (jednak nie jej całkowite wyelimino-
wanie, jak w przypadku, gdyby był tylko jeden KONTEKST ZWIĄZANY)
oraz uproszczenie integracji pomiędzy tymi dwoma podsystemami.
699b15244ae879c728b313782703a4d7
6
ZESPOŁY PROGRAMISTYCZNE
KLIENTA – DOSTAWCY
699b15244ae879c728b313782703a4d7
6
Przewidzenie tego, co może mieć wpływ na drugi zespół, jest bardzo
trudne. A ponieważ natura ludzka jest taka, jaka jest, podobnie jak presja
czasowa, więc no cóż... Sformalizowanie relacji pomiędzy zespołami zde-
cydowanie ułatwiłoby wszystkim życie. W celu zrównoważenia potrzeb
obu grup użytkowników lub opracowania harmonogramu pracy nad funk-
cjonalnościami wymaganymi przez komponent podrzędny może zostać
opracowany specjalny proces.
W przypadku projektu prowadzonego zgodnie z zasadami progra-
mowania ekstremalnego (ang. Extreme Programming) występuje już odpo-
wiedni mechanizm: proces planowania kolejnych iteracji. Wymagać on
będzie jedynie zdefiniowania relacji pomiędzy oboma zespołami w kon-
tekście procesu planowania. Przedstawiciele zespołu podrzędnego mogą
funkcjonować podobnie do przedstawicieli użytkowników, dołączając do
nich w trakcie sesji planistycznych, jak również dyskutując bezpośrednio
ze swoimi znajomymi „klientami” o kompromisach wymaganych przez
oczekiwane przez nich zadania. Rezultatem jest plan iteracji dla zespołu
dostawcy zawierający zadania, których zespół podrzędny wymaga najpil-
niej, lub też odkładający te, których dostarczenie nie jest oczekiwane.
W przypadku używania innej metodologii niż XP, jakakolwiek inna
metoda służąca zrównoważeniu obaw różnych użytkowników może być
rozszerzona w taki sposób, aby obejmować potrzeby aplikacji podrzędnych.
Dlatego:
Pomiędzy dwoma zespołami ustanów przejrzystą relację typu
klient – dostawca. Podczas sesji planistycznych umożliw zespo-
łowi podrzędnemu odgrywanie roli klienta dla zespołu nadrzęd-
nego. Negocjuj oraz budżetuj zadania dla wymagań podrzędnych
w taki sposób, aby każdy rozumiał swoje zobowiązania oraz har-
monogram.
Wspólnie opracujcie zautomatyzowane testy akceptacyjne,
które będą walidować oczekiwany interfejs. Dodaj te testy do
zestawu testów zespołu nadrzędnego w celu ich używania w trak-
cie ciągłej integracji. Uwolni to zespół nadrzędny od obaw o efekty
uboczne dokonywanych zmian i ich wpływ na zespół podrzędny.
W trakcie iteracji członkowie zespołu podrzędnego muszą być
dostępni dla programistów komponentu nadrzędnego dokładnie w ten
sam sposób co klienci, aby odpowiadać na ewentualne pytania oraz poma-
gać rozwiązywać problemy.
Automatyzacja testów akceptacyjnych jest istotną częścią relacji
z klientami. Nawet w przypadku najbardziej współpracujących projek-
tów, w których klienci mogliby identyfikować oraz komunikować zależ-
ności, a dostawca mógłby komunikować zmiany, bez testów mogą zdarzać
699b15244ae879c728b313782703a4d7
6
się niespodzianki. Mogą one zaburzyć pracę zespołu podrzędnego, a także
zmusić zespół nadrzędny do podjęcia niezaplanowanych zmian ratun-
kowych. W zamian spraw, aby zespół klienta stworzył razem z zespołem
dostawcy automatyczne testy akceptacyjne, które sprawdzą oczekiwany
interfejs. Zespół nadrzędny uruchomi te testy jako część swojego standar-
dowego zestawu testów. Każda zmiana do nich będzie wymagać komu-
nikacji pomiędzy zespołami, ponieważ zmiana testów implikuje zmianę
interfejsów.
Relacja klient/dostawca powstaje również często pomiędzy projek-
tami w oddzielnych firmach w sytuacji, gdy pojedynczy klient jest bar-
dzo istotny dla biznesu dostawcy. Stare przysłowie mówi, że ogon może
zacząć machać psem. Tak samo wpływowy klient może tworzyć żądania,
które będą istotne dla sukcesu projektu nadrzędnego, jednak mogą być
one jednocześnie niszczące dla procesu tworzenia projektu podrzędnego.
Oba zespoły będą więc zyskiwać na formalizacji procesu odpowiedzi na
wymagania, ponieważ w zewnętrznych relacjach jeszcze trudniej zauwa-
żyć kompromisy dotyczące kosztów/korzyści, niż miałoby to miejsce
w przypadku wewnętrznego projektu IT.
Istnieją dwa istotne elementy tego wzorca:
1. Relacja musi być typu klient – dostawca z zastrzeżeniem, że potrzeby
klienta mogą być olbrzymie. Ponieważ zespół podrzędny nie jest
jedynym klientem, oczekiwania innych klientów muszą być zrówno-
ważone w negocjacjach. Wciąż jednak pozostają priorytety. Taka
sytuacja pozostaje w kontraście do powstającej często relacji biednego
kuzyna, w której zespół podrzędny musi w sposób błagalny przed-
stawiać swoje potrzeby zespołowi nadrzędnemu.
2. Musi istnieć zestaw testów automatycznych umożliwiających zespo-
łowi nadrzędnemu zmianę kodu bez konieczności obawiania się
o zepsucie komponentu podrzędnego, a zespołowi podrzędnemu
skoncentrowanie się na swoich własnych zadaniach bez konieczności
ciągłego monitorowania poczynań zespołu nadrzędnego.
W przypadku sztafety biegacz czekający z przodu może w celu
sprawdzenia wciąż odwracać się do tyłu. Może jednak również zaufać oso-
bie niosącej pałeczkę, że przekaże ją w sposób precyzyjny. Inaczej wydajność
całego zespołu zostanie dramatycznie zmniejszona.
699b15244ae879c728b313782703a4d7
6
Przykład
Analiza zysków a rezerwacje
Powróćmy do naszego przykładu z firmą spedycyjną. Został utworzony
bardzo specjalizowany zespół, mający za zadanie analizowanie wszystkich
rezerwacji przechodzących przez firmę w celu sprawdzenia, czy istnieje
sposób na maksymalizację dochodów. Członkowie tego zespołu mogliby
odkryć, że statki dysponują pustą przestrzenią ładunkową, lub zarekomen-
dować wprowadzenie polityki nadkompletu. Mogliby również zauważyć,
że statki zbyt szybko wypełniają się ładunkami masowymi, zmuszając firmę
do odrzucenia zamówień dotyczących transportu bardziej lukratywnych
towarów. W takim przypadku mogliby zarekomendować rezerwację prze-
strzeni ładunkowej dla tego drugiego rodzaju ładunków lub też podnie-
sienie cen za przewóz ładunków masowych.
Aby wykonać tę całą analizę, członkowie zespołu używać będą
swoich własnych zaawansowanych modeli. W fazie implementacji będą
używać hurtowni danych wraz z narzędziami do tworzenia modeli anali-
tycznych. Będą również wymagać wielu informacji z aplikacji do rezerwacji.
Od początku wiadome jest, że są to dwa KONTEKSTY ZWIĄ-
ZANE, ponieważ używają innego zestawu narzędzi implementacyjnych,
a co więcej, innych modeli dziedzinowych. Jaka więc powinna być relacja
między nimi?
Logiczny może wydawać się wybór JĄDRA WSPÓŁDZIELO-
NEGO, ponieważ analiza zysków jest zainteresowana podzbiorem modelu
rezerwacji, a z kolei własny model analizy będzie zawierać nakładające
się pojęcia ładunku, cen itp. Jednak w przypadku zastosowania różnych
technologii implementacyjnych użycie JĄDRA WSPÓŁDZIELONEGO
jest trudne. Oprócz tego potrzeby w kontekście modelowania zespołu
analizującego zysk są całkiem specyficzne, a sam zespół ciągle modyfikuje
swoje modele i wypróbowuje nowe. Być może lepszym wyjściem byłoby
tłumaczenie przez zespół rzeczy, których potrzebuje z KONTEKSTU
rezerwacji do swojego własnego. (Z drugiej strony, używanie JĄDRA
WSPÓŁDZIELONEGO znacznie zmniejszy narzut związany z tłuma-
czeniem. Wciąż wprawdzie będzie trzeba zmieniać implementację modelu
oraz tłumaczyć dane do nowej implementacji, jednak gdy modele są iden-
tyczne, tłumaczenie powinno być prostsze).
Aplikacja do rezerwacji nie ma zależności z analizą zysków, ponie-
waż nie ma na celu automatycznego dostosowywania polityk. Decyzję
podejmą odpowiedni ludzie i przekażą ją do zainteresowanych osób oraz
systemów. Mamy więc relację nadrzędny – podrzędny. Oto jakie są ocze-
kiwania komponentu podrzędnego:
699b15244ae879c728b313782703a4d7
6
1. Dane niewymagane przez żadną operację rezerwacji.
2. Stabilny obraz schematu bazy danych (lub przynajmniej wiarygodny
sposób powiadamiania o zmianie) lub narzędzie do wykonywania
eksportów.
Na szczęście, menedżer projektu zarządzający zespołem pracującym
nad aplikacją do księgowania jest zmotywowany, aby pomóc zespołowi ana-
lityków zysku. To mógłby być potencjalny problem, ponieważ depar-
tament operacyjny, przygotowujący codzienne raporty rezerwacji, podlega
innemu prezesowi niż osoby wykonujące analizę. Jednak wyższe kierow-
nictwo bardzo troszczy się o zarządzanie zyskiem i widząc w przeszłości
problemy we współpracy pomiędzy tymi oboma działami, skonstruowało
strukturę zespołu programistycznego w ten sposób, aby obydwaj kierownicy
projektu raportowali do tej samej osoby.
Dlatego dysponujemy już wszystkimi wymaganiami niezbędnymi do
utworzenia ZESPOŁÓW PROGRAMISTYCZNYCH KLIENTA – DOS-
TAWCY (ang. CUSTOMER/SUPPLIER DEVELOPMENT TEAMS).
Spostrzegłem, że tego rodzaju scenariusz powstawał w wielu róż-
nych miejscach, w których programiści tworzący oprogramowanie ana-
lityczne byli w relacji klient – dostawca z programistami odpowiedzial-
nymi za oprogramowanie operacyjne. Jeżeli tylko członkowie zespołu
nadrzędnego traktowali swoją rolę jak służenie klientowi, wtedy wszystko
szło poprawnie. Relacja była zorganizowana niemal nieformalnie i dzia-
łała w każdym przypadku, podobnie jak poprawnie rozwijały się osobiste
relacje pomiędzy oboma menedżerami tych dwóch projektów.
W jednym z projektów XP widziałem, że ta relacja została sformali-
zowana w ten sposób, że w każdej iteracji reprezentanci zespołu podrzęd-
nego uczestniczyli podczas „gry planistycznej” w charakterze klienta wraz
z bardziej tradycyjnymi przedstawicielami klienta (odpowiadającymi za
funkcjonalności aplikacji), negocjując zadania wykonywane w danym
planie iteracji. Projekt ten prowadzony był w niewielkiej firmie, więc
niedaleko w hierarchii występowała osoba będąca szefem obu zespołów.
Cały proces działał bardzo dobrze.
***
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...
699b15244ae879c728b313782703a4d7
6
KONFORMISTA
KONFORMISTA 403
699b15244ae879c728b313782703a4d7
6
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.
699b15244ae879c728b313782703a4d7
6
Jeżeli tego rodzaju kompromisy nie są akceptowalne, a zależność od W rezultacie możesz
komponentu nadrzędnego jest niezbędna, wtedy pozostaje jeszcze druga być wciągnięty przez
lepszy projekt.
możliwość — odizolowanie się jak najbardziej poprzez zastosowanie
WARSTWY ZAPOBIEGAJĄCEJ USZKODZENIU, będącej agresywnym Jeżeli interfejs
z komponentem
podejściem do implementacji mapy tłumaczenia, która zostanie omówiona
jest mały, współdzielenie
później. zunifikowanego
modelu jest mniej
*** istotne, a tłumaczenie
KONFORMISTA przypomina JĄDRO WSPÓŁDZIELONE, ponie- jest dostępną opcją.
Jeżeli jednak interfejs
waż oba te wzorce zawierają obszar nakładający się na siebie, w którym
jest duży, a integracja
model jest identyczny, obszary, w których Twój model został rozszerzony istotniejsza, zazwyczaj
przez dodanie nowych rzeczy, oraz obszary, w których model nie ma na rozsądniejsze wydaje
Ciebie wpływu. Różnica pomiędzy tymi dwoma wzorcami leży w podej- się naśladowanie lidera.
mowaniu decyzji oraz procesach programowania. Podczas gdy JĄDRO
WSPÓŁDZIELONE jest współpracą pomiędzy ściśle współdziałającymi
ze sobą zespołami, KONFORMISTA zajmuje się integracją z zespołem,
który nie jest zainteresowany współpracą.
Zajmowaliśmy się całym spektrum różnych sposobów współdzia-
łania w integracji pomiędzy KONTEKSTAMI WSPÓŁDZIELONYMI,
począwszy od bardzo wydajnych JĄDER WSPÓŁDZIELONYCH lub
ZESPOŁÓW PROGRAMISTYCZNYCH KLIENTA – DOSTAWCY,
aż do wzorca KONFORMISTY. Teraz podejmiemy ostateczną próbę
spojrzenia na najbardziej pesymistyczny widok relacji, zakładając, że po
drugiej stronie nie ma ani chęci współpracy, ani przydatnego projektu.
KONFORMISTA 405
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
opartych na różnych modelach konieczność dostosowania się przez nowy
system do semantyki innego może prowadzić do uszkodzenia własnego
modelu systemu. Nawet w przypadku, gdy inny system jest poprawnie
zaprojektowany, nie jest on oparty na tym samym modelu, co klient. A bar-
dzo często również drugi system nie jest dobrze zaprojektowany.
Istnieje wiele przeszkód w tworzeniu interfejsów do systemu zew-
nętrznego. Na przykład warstwa infrastruktury musi udostępniać środki
do komunikacji z innym systemem, który może być na różnych platfor-
mach lub używać innych protokołów. Typy danych z innych systemów
muszą być przetłumaczone na typy z Twojego systemu. Bardzo często
jednak łatwo jest przegapić sprawdzenie, czy inny system nie używa tego
samego modelu pojęciowego dziedziny.
Całkiem oczywiste wydaje się, że wzięcie danych z jednego systemu
i ich błędne zinterpretowanie w drugim doprowadzi do błędów. Możesz
nawet w ten sposób uszkodzić bazę danych. Nawet wtedy jednak problem
może się nam wymknąć, ponieważ myślimy, że pomiędzy systemami
przesyłamy typy podstawowe, których znaczenie nie jest wieloznaczne
i musi być takie samo po obu stronach. Takie założenie jest zazwyczaj
błędne. Subtelne, lecz ważne różnice w znaczeniu bywają spowodowane
sposobem, w jaki dane kojarzone są w każdym z systemów. I nawet jeżeli
podstawowe elementy danych mają dokładnie takie samo znaczenie, to
zazwyczaj błędne jest tworzenie interfejsu do innego systemu na tak
niskim poziomie. Interfejs niskiego poziomu rezygnuje z siły modelu
innego systemu w celu wyjaśnienia danych oraz ograniczenia ich war-
tości i relacji, narzucając nowemu systemowi konieczność interpretacji
podstawowych danych niemieszczących się w definicji jego własnego
modelu.
Dlatego musimy udostępnić metodę tłumaczenia pomiędzy czę-
ściami należącymi do różnych modeli, tak aby uchronić je przed uszko-
dzeniem przez niezinterpretowane elementy obcego modelu.
Dlatego:
Utwórz warstwę izolującą, udostępniającą klientom funkcjo-
nalność na warunkach ich własnego modelu dziedziny. Warstwa
ta powinna komunikować się z innym systemem poprzez jego
istniejący interfejs i nie powinna wymagać wcale lub tylko nie-
wiele modyfikacji tamtego systemu. Wewnątrz warstwy zgodnie
z potrzebami dokonywane są tłumaczenia w obu kierunkach
pomiędzy dwoma modelami.
***
699b15244ae879c728b313782703a4d7
6
Dyskusja na temat mechanizmu łączącego dwa systemy może przywo-
dzić na myśl problemy przesyłania danych z jednego programu do innego
lub z jednego serwera na inny. Wkrótce omówię kwestię dołączenia tech-
nicznego mechanizmu komunikacji. Jednak takie szczegóły nie powinny
zaburzyć obrazu WARSTWY ZAPOBIEGAJĄCEJ USZKODZENIU
(ang. ANTICORRUPTION LAYER), która nie jest mechanizmem do
wysyłania komunikatów do innego systemu. Jest ona raczej mechanizmem
do tłumaczenia obiektów pojęciowych oraz akcji z jednego modelu i pro-
tokołu do innego.
WARSTWA ZAPOBIEGAJĄCA USZKODZENIU może stać się
złożonym elementem oprogramowania na własnych prawach. W dalszej
części rozdziału wskażę na kilka kwestii projektowych związanych z jej
projektowaniem.
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
699b15244ae879c728b313782703a4d7
6
ZAPOBIEGAJĄCEJ USZKODZENIU. Będzie to jednak rzeczywisty
problem, jaki napotkamy podczas prób jej utworzenia. Tłumaczenie
z jednego modelu na inny (szczególnie gdy jeden z modeli będzie nie-
wyraźny) jest zadaniem trudnym samym w sobie, pomijając konieczność
jednoczesnej obsługi interfejsu podsystemu, z którym trudno jest roz-
mawiać. Na szczęście, w tym celu właśnie wymyślono FASADY.
FASADA stanowi alternatywny interfejs dla podsystemu, który
upraszcza dostęp do klienta i umożliwia łatwiejsze użycie tego podsys-
temu. Ponieważ dokładnie wiemy, których funkcjonalności innego sys-
temu chcemy użyć, możemy utworzyć FASADĘ, która będzie upraszczać
dostęp do tych funkcjonalności, a ukrywać wszystkie pozostałe. FASADA
nie zmienia modelu systemu, którego dotyka. Powinna być ona utwo-
rzona dokładnie w zgodzie z modelem tego systemu. W przeciwnym razie
w najlepszym wypadku rozrzucisz odpowiedzialność za tłumaczenie do
wielu różnych obiektów i przeciążysz FASADĘ. W najgorszym nato-
miast skończysz na utworzeniu kolejnego modelu, który nie będzie nale-
żał do KONTEKSTU ZWIĄZANEGO żadnego z systemów. FASADA
należy do KONTEKSTU ZWIĄZANEGO drugiego systemu. Prezen-
tuje jedynie inną milszą twarz, dostosowaną do Twoich potrzeb.
ADAPTER jest opakowaniem, które umożliwia klientowi użycie
protokołu innego od tego zrozumianego przez obiekt implementujący
właściwe zachowanie. Gdy klient wysyła komunikat do ADAPTERA, jest
on konwertowany do semantycznie równoważnego komunikatu i wysyłany
do „komponentu wymagającego adaptacji”. Odpowiedź jest podobnie kon-
wertowana i przekazywana w drugą stronę. Termin adapter jest używany
przeze mnie w nieco swobodny sposób, ponieważ Gamma w swojej
książce kładł nacisk na dostosowanie opakowanego obiektu do standar-
dowego interfejsu wymaganego przez klienta, podczas gdy my wybieramy
adaptowany interfejs, zaś komponent wymagający adaptacji prawdopo-
dobnie nie jest nawet obiektem. My będziemy kłaść nacisk na tłumacze-
nie pomiędzy dwoma modelami. Sądzę jednak, że jest to podejście spójne
z przeznaczeniem ADAPTERA.
Dla każdej zdefiniowanej przez nas USŁUGI będziemy potrzebo-
wać ADAPTERA, udostępniającego interfejs tej USŁUGI oraz mającego
wiedzę, w jaki sposób wykonać równoważne zapytania do innego systemu
lub FASADY.
Pozostała jeszcze kwestia tłumacza. Zadaniem ADAPTERA jest posia-
danie wiedzy, jak wykonać zapytanie. Rzeczywista konwersja obiektów
pojęciowych lub danych jest oddzielnym złożonym zadaniem, które
może być umieszczone w swoim własnym obiekcie, co sprawi, że będzie go
699b15244ae879c728b313782703a4d7
6
znacznie łatwiej zrozumieć. Tłumacz może być lekkim obiektem tworzo-
nym w razie konieczności. Nie wymaga on stanu ani nie musi być dystry-
buowany, ponieważ przynależy ściśle do ADAPTERA(ÓW), któremu(ym)
służy.
Przedstawiliśmy podstawowe elementy, których używam do utwo-
rzenia WARSTWY ZAPOBIEGAJĄCEJ USZKODZENIU. Należy jed-
nak wziąć jeszcze pod uwagę kilka kwestii:
Projektowany system (Twój podsystem) będzie zazwyczaj inicjo-
wał akcję w sposób przedstawiony na rysunku 14.8. Istnieją jednak
przypadki, gdy inny podsystem musi zażądać czegoś od Twojego
podsystemu lub też poinformować go o pewnym zdarzeniu. WAR-
STWA ZAPOBIEGAJĄCA USZKODZENIU może być dwukie-
runkowa, definiując USŁUGI na obu interfejsach z ich własnymi
ADAPTERAMI. Może również potencjalnie używać tych samych
tłumaczy do symetrycznych tłumaczeń. Chociaż implementacja
WARSTWY ZAPOBIEGAJĄCEJ USZKODZENIU nie wymaga
zazwyczaj żadnej zmiany do drugiego podsystemu, może to być
jednak niezbędne w celu umożliwienia mu wywoływania USŁUG
WARSTWY ZAPOBIEGAJĄCEJ USZKODZENIU.
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ż
699b15244ae879c728b313782703a4d7
6
istnieć przypadki, gdy cała WARSTWA ZAPOBIEGAJĄCA USZ-
KODZENIU będzie funkcjonować w innym podsystemie, umiesz-
czając łącza komunikacyjne lub mechanizm dystrybucji pomiędzy
Twoim podsystemem a USŁUGAMI tworzącymi interfejs WAR-
STWY ZAPOBIEGAJĄCEJ USZKODZENIU. Są to wszystko
decyzje implementacyjne oraz wdrożeniowe, które należy podej-
mować pragmatycznie. Nie mają one wpływu na pojęciową rolę
WARSTWY ZAPOBIEGAJĄCEJ USZKODZENIU.
Jeżeli masz dostęp do drugiego systemu, możesz odkryć, że wykona-
nie w nim małej refaktoryzacji może ułatwić całe zadanie. W szcze-
gólności postaraj się dla funkcjonalności, której będziesz używać,
napisać jaśniejsze interfejsy, w miarę możliwości zaczynając od stwo-
rzenia zautomatyzowanych testów.
W sytuacji, gdy wymagania integracji są kosztowne, koszt tłuma-
czenia również się zwielokrotnia. Dlatego w celu ułatwienia tłuma-
czenia może być konieczne dokonanie wyborów w projektowanym
systemie, które zbliżą go do systemu zewnętrznego. Dokonuj tego
bardzo ostrożnie, bez narażania integralności modelu. Jest to bardzo
selektywny sposób, używany jedynie wtedy, gdy poziom trudności
tłumaczenia wymyka się z spod kontroli. Jeżeli takie podejście wydaje
się najbardziej naturalnym rozwiązaniem dla większości ważnych
części problemu, rozważ skorzystanie w podsystemie ze wzorca
KONFORMISTY, eliminując konieczność tłumaczenia.
Jeśli drugi podsystem jest prosty lub ma przejrzysty interfejs, możesz
nie potrzebować FASADY.
Jeżeli pewna funkcjonalność jest właściwa dla relacji dwóch podsyste-
mów, może ona zostać dodana do WARSTWY ZAPOBIEGAJĄCEJ
USZKODZENIU. Dwoma przydatnymi rzeczami przychodzącymi
na myśl wydają się audyt użycia zewnętrznego systemu oraz logika
śledzenia używana do testowania wywołań drugiego interfejsu.
Pamiętaj, że WARSTWA ZAPOBIEGAJĄCA USZKODZENIU jest
środkiem używanym do połączenia dwóch KONTEKSTÓW ZWIĄ-
ZANYCH. Zazwyczaj myślimy o systemie utworzonym przez kogoś
innego. Mamy nad takim systemem małą kontrolę oraz w niewielkim
stopniu go rozumiemy. Nie jest to jednak jedyna sytuacja, w której możemy
potrzebować tłumaczenia pomiędzy podsystemami. Są nawet sytuacje,
w których sensowne jest połączenie za pomocą WARSTWY ZAPO-
BIEGAJĄCEJ USZKODZENIU dwóch podsystemów, których jesteś
699b15244ae879c728b313782703a4d7
6
autorem (gdy są one oparte na różnych modelach). W takim przypadku
prawdopodobnie będziesz mieć kontrolę nad obiema stronami i zazwy-
czaj będziesz mógł użyć prostej warstwy tłumaczącej. Jeżeli jednak dwa
KONTEKSTY ZWIĄZANE poszły OSOBNYMI DROGAMI, wciąż
mogą wymagać integracji funkcjonalnej. W takiej sytuacji WARSTWA
ZAPOBIEGAJĄCA USZKODZENIU może zminimalizować występu-
jące pomiędzy nimi tarcia.
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
699b15244ae879c728b313782703a4d7
6
tysiące lat definiował on granicę, która pozwalała chińskiej cywilizacji
opartej na rolnictwie zdefiniować swoje podstawy, minimalizując wpływ
chaosu na zewnątrz kraju.
Chociaż bez Wielkiego Muru chińska cywilizacja nie stałaby się tak
odmienna kulturowo, to jednak budowa muru była niesamowicie kosz-
towna i przyczyniła się do bankructwa przynajmniej jednej dynastii, dopro-
wadzając prawdopodobnie do jej upadku. Korzyści ze strategii izolacji
muszą równoważyć ich koszty. Wciąż jest czas na to, aby zastanowić się
pragmatycznie i podjąć stosowne kroki w stosunku do modelu, aby w lep-
szym stopniu pasował on do tego obcego.
Pewien narzut związany jest z każdym typem integracji, począwszy
od CIĄGŁEJ INTEGRACJI wewnątrz pojedynczego KONTEKSTU
ZWIĄZANEGO, poprzez mniejsze zobowiązania JĄDER WSPÓŁDZIE-
LONYCH lub ZESPOŁÓW PROGRAMISTYCZNYCH KLIENTA –
DOSTAWCY, a skończywszy na wielu stronach KONFORMISTY oraz
defensywnej postawie WARSTWY ZAPOBIEGAJĄCEJ USZKODZE-
NIU. Integracja może być bardzo cenna, lecz jest zawsze kosztowna.
Musimy mieć pewność, że jest rzeczywiście potrzebna...
699b15244ae879c728b313782703a4d7
6
ODDZIELNE DROGI
699b15244ae879c728b313782703a4d7
6
Funkcjonalności można wciąż dodać w warstwach pośrednich lub
warstwie użytkownika, jednak nie będą one mieć współdzielonej logiki,
a także będą używać niezbędnego minimalnego transferu danych przez
warstwy tłumaczenia (najlepiej wcale).
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
699b15244ae879c728b313782703a4d7
6
menu. Dużo cennych funkcjonalności zostało dostarczonych prawie
następnego dnia. Porzucenie bagażu nieistotnych funkcjonalności pozo-
stawiło oddestylowany zestaw wymagań, który przez moment wydawał
się dawać nadzieję na dostarczenie głównej aplikacji.
Takie rozwiązanie mogłoby zadziałać, jednak ostatecznie zespół
powrócił do starych przyzwyczajeń i ponownie znalazł się w sytuacji
patowej. Ostateczne jego jedynym wynikiem pracy okazały się te małe
aplikacje, które poszły ODDZIELNYMI DROGAMI.
***
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ę.
699b15244ae879c728b313782703a4d7
6
USŁUGA OTWARTEGO
GOSPODARZA
Zazwyczaj dla każdego KONTEKSTU ZWIĄZANEGO zdefiniujesz
warstwę tłumaczenia dla każdego z komponentów leżących poza KON-
TEKSTEM, który masz zintegrować. Gdy integracja jest jednorazowa,
takie podejście wstawiania warstwy tłumaczenia dla każdego z systemów
zewnętrznych przy minimalnym koszcie zapobiega uszkodzeniu modeli.
Jeżeli jednak integracja z podsystemem będzie bardzo ważna, możesz
potrzebować bardziej elastycznego podejścia.
***
W sytuacji, gdy podsystem musi być zintegrowany z wieloma
innymi, dostosowywanie do każdego z nich tłumacza może spo-
wolnić pracę zespołu. Będzie coraz więcej rzeczy do utrzyma-
nia, a przy dokonywaniu zmian będzie trzeba na nie wszystkie
zwrócić uwagę.
Zespół może wielokrotnie wykonywać tę samą rzecz. Jeżeli w pod-
systemie istnieje jakakolwiek spójność, wtedy prawdopodobnie możliwe
jest opisanie go w postaci zbioru USŁUG, które będą pokrywać wspólne
potrzeby innych podsystemów.
Utworzenie protokołu na tyle przejrzystego, aby został zrozumiany
i wykorzystany przez wiele zespołów, jest na tyle trudną rzeczą, że opłaci
się ona jedynie w sytuacji, gdy zasoby podsystemu mogą być opisane
w postaci spójnego zestawu USŁUG, a także gdy istnieje potencjalnie
duża liczba integracji. W takich okolicznościach różnica pomiędzy trybem
utrzymaniowym a stałym rozwojem może być znacząca.
Dlatego:
Zdefiniuj protokół odpowiedzialny za dostęp do podsystemu
w postaci zestawu USŁUG. Utwórz ten protokół w taki sposób,
aby każdy, kto będzie potrzebował integracji, mógł z niego sko-
rzystać. Rozszerz i rozwiń protokół, aby mógł on obsługiwać
nowe wymagania dotyczące integracji, pominąwszy przypadki,
w których tylko jeden zespół ma dziwne wymagania. Następnie
użyj jednorazowego tłumacza w celu wsparcia protokołu jedynie
dla tego specjalnego przypadku, tak aby współdzielony protokół
pozostał prosty i spójny.
***
699b15244ae879c728b313782703a4d7
6
Tego rodzaju formalizacja komunikacji wymaga użycia pewnego języka
dla współdzielonego modelu — podstawy interfejsów USŁUGI. W rezul-
tacie jedne podsystemy stają się związane z modelem OTWARTEGO
GOSPODARZA (ang. OPEN HOST), a inne muszą nauczyć się tego
określonego dialektu używanego przez zespół GOSPODARZA. W nie-
których przypadkach używanie dobrze znanego JĘZYKA OPUBLIKO-
WANEGO (ang. PUBLISHED LANGUAGE) w charakterze modelu
wymiennego może zmniejszyć związanie oraz ułatwić jego zrozumienie.
699b15244ae879c728b313782703a4d7
6
JĘZYK OPUBLIKOWANY
Tłumaczenie pomiędzy modelami dwóch KONTEKSTÓW ZWIĄZA-
NYCH wymaga wspólnego języka.
***
Kiedy dwa modele dziedziny muszą ze sobą wspólnie egzystować,
a także wymieniać informacje, sam proces tłumaczenia może stać się
złożony oraz trudny do udokumentowania i zrozumienia. Jeżeli two-
rzymy nowy system, zazwyczaj wierzymy, że nasz nowy model jest naj-
lepszym z dostępnych, dlatego też będziemy chcieli wszystko na niego
tłumaczyć. Niekiedy jednak chcemy udoskonalić zestaw starych syste-
mów i podejmujemy próby ich integracji. Wybór jednego z dwóch bała-
ganiarskich modeli może być wybraniem mniejszego zła.
Inna sytuacja: Jeżeli dwie organizacje chcą wymienić ze sobą infor-
macje, w jaki sposób mogą to zrobić? Oczekiwanie, że jedna z nich zaadap-
tuje model dziedziny drugiej, jest nie tylko nierealistyczne, lecz może
być nawet niepożądane dla obu stron. Model dziedziny tworzy się, aby
rozwiązać problemy swoich użytkowników. Tego rodzaju model może
zawierać funkcjonalności, które niepotrzebnie skomplikują komunikację
z innym systemem. Dodatkowo, jeżeli model jednej aplikacji jest używany
w charakterze medium komunikacyjnego, wtedy nie może być w dowolny
sposób adaptowany do nowych potrzeb, lecz raczej musi zachować sta-
bilność, aby wspierać rolę bieżącej komunikacji.
Bezpośrednie tłumaczenie z istniejących modeli dziedziny
lub na te modele nie musi być wcale dobrym rozwiązaniem. Te
modele mogą być zbyt złożone lub też w słaby sposób zrefakto-
ryzowane. Prawdopodobnie są też nieudokumentowane. Jeżeli
któryś z nich zostanie użyty w charakterze języka wspierającego
wymianę danych, zostaje dosłownie zamrożony i nie może od-
powiadać nowym potrzebom projektowym.
USŁUGA OTWARTEGO GOSPODARZA (ang. OPEN HOST
SERVICE) używa ustandaryzowanego protokołu w celu wielostronnej
integracji. Stosuje on model dziedziny w celu wymiany informacji pomię-
dzy systemami, nawet wtedy, gdy model ten nie może być użyty wew-
nętrznie przez te systemy. Możemy też pójść krok dalej i opublikować
ten język lub odnaleźć taki, który już został opublikowany. Przez publi-
kację rozumiem, że język jest powszechnie dostępny dla społeczności,
która może być zainteresowana jego zastosowaniem, a także wystarczająco
udokumentowany w celu zapewnienia wzajemnej zgodności jego nieza-
leżnych interpretacji.
699b15244ae879c728b313782703a4d7
6
Ostatnio świat usług handlu elektronicznego bardzo zainteresował
się nową technologią, jaką jest rozszerzalny język znaczników (ang.
eXtensible Markup Language — XML). Język ten obiecuje upraszczać
wymianę danych. Bardzo ważną cechą języka XML jest fakt, iż przez
dokument definicji typu (ang. document type definition — DTD) lub też
schematy XML pozwala na formalną definicję specjalizowanego języka
dziedziny, do którego dane mogą zostać przetłumaczone. Zaczęły pow-
stawać grupy przemysłowe mające za zadanie zdefiniowanie pojedynczego
standardu DTD dla swoich dziedzin przemysłowych, tak aby — dajmy
na to — informacje o formule chemicznej lub kodzie genetycznym mogły
być komunikowane pomiędzy wieloma stronami. Zasadniczo grupy te
tworzą współdzielony model dziedziny w postaci definicji języka.
Dlatego:
W charakterze wspólnego medium komunikacyjnego używaj
dobrze udokumentowanego współdzielonego języka, który może
wyrazić niezbędne informacje dziedzinowe. W razie potrzeby
dokonuj odpowiednich tłumaczeń z tego języka lub do niego.
Sam język nie musi być tworzony od podstaw. Wiele lat temu pod-
pisałem kontrakt z firmą, która miała aplikację napisaną w Smalltalku,
używającą do zachowywania swoich danych bazy DB2. Firma chciała uzy-
skać możliwość dystrybuowania swojego oprogramowania do użytkowni-
ków bez licencji na bazę DB2 i zatrudniła mnie w celu napisania interfejsu
do bazy Btrieve, będącej lżejszą wersją silnika bazodanowego z darmową
licencją uruchomieniową. Btrieve nie jest w pełni relacyjna, jednak mój
klient używał tylko małego podzbioru możliwości bazy DB2, a to, czego
używał, mieściło się w obrębie wspólnego najmniejszego mianownika
obu baz. Programiści firmy stworzyli na bazie DB2 pewne abstrakcje słu-
żące do zachowywania obiektów. Zdecydowałem się użyć ich efektów
pracy w charakterze interfejsu do mojego komponentu Btrieve.
To podejście zadziałało. Program płynnie zintegrował się z syste-
mem klienta. Niestety, brak formalnej specyfikacji lub dokumentacji
utworzonych abstrakcji trwałych obiektów w projekcie klienta oznaczał
dla mnie dużą ilość pracy niezbędnej do rozpoznania wymagań nowego
komponentu. Nie było też zbyt wielu okazji do ponownego wykorzy-
stania komponentu w celu zmigrowania innych aplikacji z bazy DB2 do
Btrieve. Nowe oprogramowanie jeszcze bardziej wzmocniło firmowy
model gwarantujący trwałość obiektów, powodując, że jego refaktoryza-
cja byłaby jeszcze trudniejsza.
Lepszym sposobem mogłoby być zidentyfikowanie i wspieranie
podzbioru interfejsu DB2 używanego przez firmę. Interfejs DB2 jest
stworzony z języka SQL oraz szeregu licencjonowanych protokołów.
699b15244ae879c728b313782703a4d7
6
Mimo swojej złożoności jest ściśle specyfikowany i udokumentowany.
Jego złożoność zostałaby zmniejszona, ponieważ jedynie mały podzbiór
interfejsu był w użyciu. Gdyby został utworzony komponent emulujący
wymagany podzbiór interfejsu DB2, mógłby zostać w wydajny sposób
udokumentowany dla programistów najzwyczajniej tylko przez identyfi-
kację tego podzbioru. Aplikacja, z którą ten interfejs został zintegrowany,
wiedziała już, w jaki sposób rozmawiać z bazą DB2, dlatego wymagana
była tylko niewielka ilość dodatkowej pracy. Kolejne przebudowy war-
stwy trwałości w przyszłości mogłyby używać jedynie podzbioru DB2,
podobnie jak przed rozszerzeniem.
Interfejs DB2 jest przykładem JĘZYKA OPUBLIKOWANEGO
(ang. PUBLISHED LANGUAGE). W tym przykładzie dwa modele nie
znajdują się w dziedzinie biznesowej, jednak stosowane są te same pryn-
cypia. Ponieważ jeden model jest już OPUBLIKOWANYM JĘZYKIEM,
nie ma konieczności wprowadzania trzeciego języka.
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
699b15244ae879c728b313782703a4d7
6
została w celu utworzenia graficznych widoków struktur chemicznych
zapisanych w języku CML. Jeżeli więc umieścisz dane w formacie CML,
będziesz mieć dostęp do tego rodzaju narzędzi wizualizacyjnych.
W rzeczywistości CML uzyskał podwójną korzyść, wykorzystując
XML, będący rodzajem „opublikowanego metajęzyka”. Dzięki temu
osobom zaznajomionym z językiem XML nauka CML przyjdzie z łatwo-
ścią. Również implementacja jest ułatwiona poprzez dostępność różnych
gotowych narzędzi, jak chociażby parserów. Natomiast dokumentacja
jest wspomagana przez wiele książek omawiających wszystkie aspekty
obsługi XML.
Poniżej przedstawiony jest mały przykład w języku CML. Jest on
tylko nieco zrozumiały dla laików takich jak ja, lecz samo pryncypium
jest jasne.
<CML.ARR ID="array3" EL.TYPE=FLOAT NAME="ATOMIC ORBITAL ELECTRON POPULATIONS"
SIZE=30 GLO.ENT=CML.THE.AOEPOPS>
1.17947 0.95091 0.97175 1.00000 1.17947 0.95090 0.97174 1.00000
1.17946 0.98215 0.94049 1.00000 1.17946 0.95091 0.97174 1.00000
1.17946 0.95091 0.97174 1.00000 1.17946 0.98215 0.94049 1.00000
0.89789 0.89790 0.89789 0.89789 0.89790 0.89788
</CML.ARR>
***
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ć.
699b15244ae879c728b313782703a4d7
6
Nie gorszy, choć ostatni Szósty,
najpowolniejszy, bo był tłusty i dał się innym minąć,
rzekł, gdy za ogon słonia chwycił:
— Nie przypuszczałem nigdy w życiu, że słoń jest zwykłą liną!
2
J. Godfrey Saxe, Księga nonsensu, tłum. Antoni Marianowicz, Wydawnictwa
Artystyczne i Filmowe, Warszawa 1986 — przyp. red.
699b15244ae879c728b313782703a4d7
6
Rysunek 14.9.
Cztery konteksty
bez integracji
Rysunek 14.10.
Cztery konteksty
— minimalna
integracja
Rysunek 14.11.
Jeden kontekst
— przybliżona
integracja
699b15244ae879c728b313782703a4d7
6
bardzo często tylko jeden różniący je aspekt. Sprawy komplikują się
znacznie, gdy dwa modele spoglądają na tę samą część w różny sposób.
Gdyby dwie osoby dotknęły trąby i jedna opisałaby ją jako węża, a druga
jako wąż strażacki, wtedy miałyby zdecydowanie więcej problemów.
Nikt nie mógłby zaakceptować drugiego modelu, ponieważ stałby on
w sprzeczności z jego własnym wyobrażeniem. W rzeczywistości będzie
potrzebna nowa abstrakcja, łącząca w sobie „ożywienie” węża z funkcjonal-
nością strzelania wodą węża strażackiego, jednak musiałaby ona pomijać
wszystkie niestosowności pierwszych modeli, w rodzaju oczekiwania
prawdopodobnie jadowitych zębów lub też możliwości odłączenia od
reszty ciała i zwinięcia w rulon mieszczący się w przedziale wozu stra-
żackiego.
Nawet jeżeli połączyliśmy części w całość, to jednak wynikowy
model jest przybliżony. Jest on niespójny, gdyż brakuje mu sensownego
zarysu wspierającej go dziedziny. Nowe spostrzeżenia mogłyby prowa-
dzić do odkrycia głębszego modelu w procesie ciągłego ulepszania. Zwrot
w kierunku głębszego modelu mogą również wymusić nowe wymagania
do aplikacji. Jeżeli słoń zacznie się poruszać, wtedy teoria „drzew” zanik-
nie, a nasze niewidome osoby tworzące model mogą odkryć przełomowe
pojęcie „nóg”.
Rysunek 14.12.
Jeden kontekst
— głębszy model
699b15244ae879c728b313782703a4d7
6
przez każdy z modeli. Jeżeli natomiast wymagana jest większa integracja,
wtedy zunifikowany model nie musi osiągnąć dojrzałości w pierwszej
wersji. Dla niektórych potrzeb może być adekwatne rozpatrywanie sło-
nia w postaci muru, podtrzymywanego pieńkami, z liną na jednym końcu
i wężem na drugim. Później, dzięki nowym wymaganiom oraz ulepszo-
nemu zrozumieniu, a także właściwej komunikacji, model może zostać
pogłębiony oraz ulepszony.
Rozpoznawanie wielu sprzecznych modeli dziedziny jest najzwyczaj-
niej smutną rzeczywistością. Definiując bezpośrednio kontekst, w któ-
rym każdy z tych modeli będzie mieć zastosowanie, możemy utrzymać
ich integralność, a także łatwo ujrzeć implikacje dowolnego z interfejsów,
które będziesz chciał utworzyć pomiędzy dwoma modelami. Dostrzeże-
nie przez niewidomą osobę pełnego obrazu słonia jest niemożliwe, jednak
problem tej grupy osób mógłby stać się możliwy do zarządzania, gdyby
tylko uświadomiły one sobie niekompletność swojego wyobrażenia.
699b15244ae879c728b313782703a4d7
6
nikować wnioski na temat kosztów, a także podjąć kroki w celu ich unik-
nięcia. Rozpocznij od realistycznej MAPY KONTEKSTÓW i zachowaj
pragmatyzm w trakcie wyboru transformacji.
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.
699b15244ae879c728b313782703a4d7
6
Preferowanie mniejszych KONTEKSTÓW ZWIĄZANYCH
Narzut komunikacyjny pomiędzy programistami jest zmniejszony.
CIĄGŁA INTEGRACJA jest prostsza w mniejszych zespołach
i mniejszym kodzie.
Większe konteksty mogą potrzebować bardziej uniwersalnych modeli
abstrakcyjnych, wymagając umiejętności, których zazwyczaj brakuje.
Różne modele mogą zaspokajać specjalne potrzeby, a także obejmo-
wać żargon wąskich grup użytkowników wraz ze specjalistycznymi
dialektami JĘZYKA WSZECHOBECNEGO.
Głęboka integracja funkcjonalności pomiędzy różnymi KONTEK-
STAMI ZWIĄZANYMI nie jest praktyczna. Integracja jest ograniczona
do tych części jednego modelu, które można sposób rygorystyczny wyra-
zić w warunkach drugiego modelu, a nawet taki poziom integracji może
wymagać zauważalnego wysiłku. Ma to sens jedynie w przypadku małego
interfejsu pomiędzy dwoma systemami.
699b15244ae879c728b313782703a4d7
6
Relacje z systemami zewnętrznymi
Istnieją trzy wzorce, które można tu zastosować. Najpierw rozważ uży-
cie ODDZIELNYCH DRÓG. Tak, nie uwzględniłbyś ich, gdybyś nie
potrzebował integracji. Jednak bądź tego naprawdę pewny. Czy wystar-
czyłoby nadać użytkownikowi wczesny dostęp do obu systemów? Integra-
cja jest kosztowna i rozprasza uwagę, dlatego uwolnij swój system, jak to
tylko możliwe.
Jeżeli integracja jest naprawdę kluczowa, możesz wybrać pomiędzy
dwoma biegunami: KONFORMISTĄ oraz WARSTWĄ ZAPOBIEGA-
JĄCĄ USZKODZENIU. Bycie KONFORMISTĄ nie jest zabawne.
Twoja kreatywność oraz możliwość wyboru nowych funkcjonalności będą
ograniczone. Podczas tworzenia dużego nowego systemu bardzo mało
praktyczne wydaje się dostosowanie do modelu starego lub zewnętrznego
systemu (w końcu, dlaczego tworzysz nowy system?). Jednak zachowanie
starego modelu może być właściwe w przypadku zewnętrznych rozsze-
rzeń do dużego systemu, który pozostanie dominujący. Takim przykła-
dem mogłyby być lekkie systemy wsparcia decyzji, które często pisane są
w Excelu lub innych prostych narzędziach. Jeżeli Twoja aplikacja jest
w rzeczywistości rozszerzeniem istniejącego systemu, a oczekiwany inter-
fejs będzie duży, wtedy tłumaczenie pomiędzy KONTEKSTAMI może
być trudniejszym zadaniem niż utworzenie samej funkcjonalności. Wciąż
pozostanie również przestrzeń na dobrą pracę projektową, nawet jeżeli
umieściłeś swój kod w KONTEKŚCIE ZWIĄZANYM innego systemu.
Jeżeli za innym systemem kryje się rozpoznawalny model dziedziny,
możesz udoskonalić swoją implementację poprzez ujawnienie tego modelu
w większym stopniu, niż miało to miejsce w starym systemie, o ile dosto-
sowuje się do tego starego modelu. Jeżeli decydujesz się na wybór wzorca
KONFORMISTY, musisz robić to z całym oddaniem. Ograniczasz się
przecież jedynie do rozszerzeń, bez modyfikacji do istniejącego modelu.
Kiedy funkcjonalność systemu w projektowaniu wydaje się prze-
wyższać przypadki rozszerzania istniejącego systemu lub gdy interfejs do
innego systemu jest mały albo system ten jest zaprojektowany w niepo-
prawny sposób, wtedy będziesz potrzebował własnego KONTEKSTU
ZWIĄZANEGO, co będzie oznaczać konieczność utworzenia warstwy
tłumaczenia lub nawet WARSTWY ZAPOBIEGAJĄCEJ USZKO-
DZENIU.
699b15244ae879c728b313782703a4d7
6
System w projektowaniu
Aplikacja, którą w rzeczywistości tworzy Twój zespół, jest systemem w pro-
jektowaniu. W jej obszarze możesz zadeklarować KONTEKSTY ZWIĄ-
ZANE oraz w każdym z nich zastosować CIĄGŁĄ INTEGRACJĘ w celu
ich unifikacji. Jednak jak wielu z nich będziesz potrzebować? Jakie powinny
być ich wzajemne relacje? Odpowiedzi nie są już tak jednoznaczne, jak
w przypadku systemów zewnętrznych, ponieważ w tym przypadku możesz
mieć więcej swobody oraz kontroli.
Mogłoby to wyglądać całkiem prosto: pojedynczy KONTEKST
ZWIĄZANY dla całego systemu w projektowaniu. Prawdopodobnie byłby
to całkiem dobry wybór dla zespołu liczącego mniej niż dziesięć osób,
pracującego nad bardzo powiązanymi ze sobą funkcjonalnościami.
Kiedy zespół rozrośnie się bardziej, CIĄGŁA INTEGRACJA może
okazać się trudna (chociaż widziałem, że była stosowana również w nieco
większych zespołach). Możesz też spojrzeć na JĄDRO WSPÓŁDZIE-
LONE i wydzielić względnie niezależne zestawy funkcjonalności do
osobnych KONTEKSTÓW ZWIĄZANYCH, każdego liczącego mniej
niż dziesięciu ludzi. Jeżeli dodatkowo wszystkie zależności pomiędzy
dwoma z nich zmierzają w jednym kierunku, mógłbyś również utwo-
rzyć ZESPOŁY PROGRAMISTYCZNE KLIENTA – DOSTAWCY.
Mógłbyś też zauważyć, że sposób myślenia tych dwóch grup różni
się od siebie znacznie, przez co przeszkadzają sobie nawzajem podczas
modelowania. Mogą one bowiem w rzeczywistości wymagać od modelu
całkiem innych rzeczy; może to także wynikać jedynie z różnic w pod-
stawowej wiedzy. Ostatecznie może być to również wynikiem struktury
zarządzania projektu, w którym te grupy pracują. Jeżeli przyczyna pro-
blemów jest czymś, czego nie jesteś w stanie lub nie chcesz zmienić,
możesz pozwolić modelom pójść ODDZIELNYMI DROGAMI. Wszędzie
tam, gdzie wymagana jest integracja, dwa zespoły mogą w ramach CIĄ-
GŁEJ INTEGRACJI utworzyć oraz utrzymywać wspólnie warstwę tłu-
maczenia. Jest to inne podejście niż w przypadku integracji z systemami
zewnętrznymi, w której WARSTWA ZAPOBIEGAJĄCA USZKODZE-
NIU ma zazwyczaj wykorzystać inny system w takim stanie, w jakim jest,
bez zbytniej pomocy z drugiej strony.
Ogólnie mówiąc, istnieje zależność typu jeden zespół dla jednego
KONTEKSTU ZWIĄZANEGO. Jeden zespół może utrzymywać wiele
KONTEKSTÓW ZWIĄZANYCH, jednak praca wielu zespołów nad
jednym kontekstem jest trudna (chociaż nie jest niemożliwa).
699b15244ae879c728b313782703a4d7
6
Dostosowanie do specjalnych potrzeb
przy użyciu odrębnych modeli
Różne grupy w tej samej organizacji często opracowują swoją własną
specjalizowaną terminologię, która może się od siebie różnić. Ten lokalny
żargon może być bardzo precyzyjny i dostosowany do ich potrzeb. Jego
zmiana (na przykład poprzez narzucenie jednej standaryzowanej termi-
nologii, wspólnej dla całego przedsiębiorstwa) wymaga dużego treningu
oraz analizy prowadzącej do usunięcia występujących różnic. Nawet
wtedy jednak nowa terminologia może nie funkcjonować tak sprawnie,
jak dobrze dopracowana wersja, którą już posiadają.
Możesz zdecydować, by dostosować się do tych specjalnych potrzeb
w oddzielnych KONTEKSTACH ZWIĄZANYCH, pozwalając, aby
modele poszły ODDZIELNYMI DROGAMI, z wyjątkiem CIĄGŁEJ
INTEGRACJI stosowanej dla warstw tłumaczenia. Wokół tych modeli oraz
specjalistycznego żargonu, na którym są oparte, rozwiną się różne dialekty
JĘZYKA WSZECHOBECNEGO. Jeżeli dwa dialekty mają wiele części
wspólnych, potrzebnej specjalizacji może dostarczyć JĄDRO WSPÓŁ-
DZIELONE, minimalizując jednocześnie koszt związany z tłumaczeniem.
Wszędzie tam, gdzie integracja nie jest potrzebna lub jest względ-
nie ograniczona, sytuacja taka pozwala na ciągłe stosowanie tradycyjnej
terminologii oraz zapobiegnięcie uszkodzeniom modeli. Pociąga to rów-
nież za sobą określone koszty i ryzyko:
Utrata wspólnego języka ograniczy komunikację.
Integracja będzie wymagać dodatkowego wysiłku.
Utworzenie różnych modeli tych samych czynności biznesowych
oraz encji będzie wymagać podwójnego wysiłku.
Być może jednak największym ryzykiem jest fakt, że może to posłu-
żyć jako argument przeciwko zmianie oraz uzasadnienie dla dziwacznego,
prowincjonalnego modelu. Jak dużo wysiłku będzie potrzebne, aby dosto-
sować konkretną część systemu do bardziej specjalistycznych potrzeb?
Najważniejsza będzie odpowiedź na pytanie, jak cenny jest dany żargon dla
tej grupy użytkowników. Musisz rozważyć wartość bardziej niezależnych
działań zespołu i ryzyko tłumaczenia, mając na uwadze racjonalizację
odmian terminologii, które nie mają wartości.
Niekiedy pojawia się model dogłębny, który może ujednolicić te
różne języki i usatysfakcjonować obie grupy. Kruczek tkwi w tym, że
modele dogłębne, jeżeli już powstają, to w późniejszych etapach cyklu
życia, po dużej ilości pracy oraz przetwarzania wiedzy. Pojawienia się
699b15244ae879c728b313782703a4d7
6
modelu dogłębnego nie można zaplanować. Można jedynie zaakcepto-
wać nowe możliwości, gdy już się on pojawi, zmienić swoją strategię oraz
zrefaktoryzować kod.
Miej na uwadze, że wszędzie tam, gdzie wymagania integracyjne są
rozległe, zwiększa się również koszt tłumaczenia. Koordynacja zespołów,
począwszy od precyzyjnych modyfikacji jednego obiektu ze skompliko-
wanym tłumaczeniem rozciągającym się aż do JĄDRA WSPÓŁDZIELO-
NEGO, może ułatwić tłumaczenie, nie wymagając wciąż pełnej unifikacji.
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.
699b15244ae879c728b313782703a4d7
6
Kompromis
Podsumowując powyższe wskazówki, należy stwierdzić, że istnieje szereg
strategii służących do unifikacji lub integracji modeli. Ogólnie mówiąc,
będziesz musiał zdecydować się na kompromis pomiędzy korzyściami
z bezbolesnej integracji funkcjonalności a dodatkowym wysiłkiem zwią-
zanym z koordynacją oraz komunikacją. W ten sposób będziesz balan-
sować pomiędzy bardziej niezależnymi akcjami a płynniejszą komunikacją.
Ambitniejsza unifikacja wymaga kontroli nad projektem powiązanych
podsystemów.
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.
699b15244ae879c728b313782703a4d7
6
Kiedy już rozrysujesz prawdziwe KONTEKSTY ZWIĄZANE oraz
opiszesz istniejące w nich relacje, kolejną czynnością będzie wzmocnienie
praktyk zespołu wokół obecnej organizacji. Rozwiń CIĄGŁĄ INTEGRA-
CJĘ wewnątrz KONTEKSTÓW. Zrefaktoryzuj oraz przenieś kod tłu-
maczenia do WARSTW ZAPOBIEGAJĄCYCH USZKODZENIU.
Nazwij istniejące KONTEKSTY ZWIĄZANE i upewnij się, że są one
umieszczone w JĘZYKU WSZECHOBECNYM.
W tym momencie będziesz już gotów na rozważenie zmian doty-
czących granic oraz samych relacji. Zmiany te będą oczywiście sterowane
tymi samymi pryncypiami, które zostały już opisane dla nowego pro-
jektu. Będzie jednak trzeba rozbić je na mniejsze elementy, wybrane
w sposób pragmatyczny, aby przyniosły największą wartość przy naj-
mniejszym koszcie i bałaganie.
W kolejnym podrozdziale opiszemy, w jaki sposób wprowadzić
zmiany do granic KONTEKSTU, jeżeli już się na nie zdecydujesz.
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ń.
699b15244ae879c728b313782703a4d7
6
Jeżeli nawet Twoim celem będzie całkowite połączenie do postaci
pojedynczego KONTEKSTU przy użyciu CIĄGŁEJ INTEGRACJI, roz-
pocznij od przejścia do JĄDRA WSPÓŁDZIELONEGO.
1. Zbadaj początkową sytuację. Upewnij się, że dwa KONTEKSTY są
rzeczywiście zunifikowane wewnętrznie, zanim zaczniesz je ujed-
nolicać.
2. Utwórz proces. Będziesz musiał zdecydować, w jaki sposób będzie
współdzielony kod oraz jaka będzie konwencja nazewnicza modu-
łów. Musi odbywać się co najmniej cotygodniowa integracja kodu
JĄDRA WSPÓŁDZIELONEGO. Konieczne jest również stworze-
nie zestawu testów. Zdefiniuj go, zanim zaczniesz tworzyć jakikol-
wiek współdzielony kod (zestaw testów będzie pusty, więc ich speł-
nienie powinno być proste).
3. Wybierz małą poddziedzinę, od której zaczniesz — coś, co jest
zduplikowane w obu KONTEKSTACH, jednak nie stanowi części
DZIEDZINY GŁÓWNEJ. To pierwsze połączenie ustanowi proces,
więc najlepiej będzie użyć czegoś prostego, relatywnie generycznego
oraz niekrytycznego. Sprawdź aktualnie istniejące integracje oraz
tłumaczenia. Wybór czegoś, co już jest tłumaczone, ma taką zaletę,
że rozpoczniemy od sprawdzonego podejścia, a także przyczyni się
do zmniejszenia warstwy tłumaczenia.
W tym momencie będziesz mieć dwa modele dotyczące tej samej
poddziedziny. Istnieją trzy podstawowe podejścia do łączenia kontekstów.
Możesz wybrać jeden model i zrefaktoryzować drugi KONTEKST w taki
sposób, aby był z nim zgodny. Ta decyzja może być podjęta na większą
skalę, poprzez ustanowienie zamiaru systematycznej zamiany jednego
modelu KONTEKSTU oraz utrzymywania spójności utworzonego
modelu jako całości. Możesz też wybierać poszczególne elementy, praw-
dopodobnie ostatecznie tworząc ich wersję najlepszą dla obu kontekstów
(uważaj jednak, aby nie skończyć z bezładną mieszaniną).
Trzecią możliwością jest odnalezienie modelu prawdopodobnie
głębszego niż oba początkowe, który będzie mieć możliwość przyjęcia
odpowiedzialności obu kontekstów.
4. Utwórz grupę od dwóch do czterech programistów z obu zespo-
łów pracujących nad współdzielonym modelem dla poddziedziny.
Niezależnie od tego, w jaki sposób ten model będzie tworzony,
musi być on dość szczegółowy. Oznaczać to będzie ciężką pracę
nad identyfikacją synonimów oraz mapowaniem wszystkich pojęć,
TRANSFORMACJE 435
699b15244ae879c728b313782703a4d7
6
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,
699b15244ae879c728b313782703a4d7
6
a ty uzyskałbyś ten sam efekt, co w przypadku celowego łączenia. W przy-
padku takiego przejścia (które może trwać długo lub nieskończenie długo)
końcowy efekt będzie mieć zwyczajne zalety oraz wady używania
ODDZIELNYCH DRÓG, które będziesz musiał zrównoważyć z zale-
tami oraz wadami JĄDRA WSPÓŁDZIELONEGO.
Łą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
699b15244ae879c728b313782703a4d7
6
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ą.
699b15244ae879c728b313782703a4d7
6
W każdej iteracji:
1. Zidentyfikuj specyficzną starą funkcjonalność, która mogłaby zostać
dodana w jednej iteracji do jednego z preferowanych systemów.
2. Zidentyfikuj dodatki, które będą wymagane przez WARSTWĘ
ZAPOBIEGAJĄCĄ USZKODZENIU.
3. Zaimplementuj.
4. Przeprowadź wdrożenie.
Niekiedy będzie konieczne poświęcenie więcej niż jednej iteracji
na tworzenie funkcjonalności równoważnych wycofywanemu staremu
mechanizmowi. Jednak wciąż planuj nowe funkcje jako małe jednostki,
mieszczące się w iteracji, czekające jedynie kilka iteracji na wdrożenie.
Wdrożenie jest osobną kwestią, w której istnieje zbyt wiele odmian,
aby można je było opisać w tym miejscu. Byłoby bardzo przyjemnie,
gdyby te małe przyrostowe zmiany mogły zostać wdrożone do produkcji
bezpośrednio, jednak zazwyczaj wymagać to będzie zorganizowania
większych wdrożeń. Użytkownik musi być przecież wyszkolony, aby
używać nowego systemu. Niekiedy pomyślny musi być również toczący
się równolegle proces. Trzeba również rozwiązać wiele problemów
logistycznych.
Kiedy jednak ostatecznie program jest wdrożony:
5. Zidentyfikuj wszystkie niepotrzebne części WARSTWY ZAPO-
BIEGAJĄCEJ USZKODZENIU i usuń je.
6. Rozważ usunięcie obecnie nieużywanych modułów starego systemu,
chociaż może to okazać się niepraktyczne. Jak na ironię. im lepiej
zaprojektowany jest stary system, tym łatwiej będzie go wygasić.
Jednak źle zaprojektowane oprogramowanie trudno jest rozmon-
tować w krótkim czasie. Można wprawdzie zwyczajnie zignorować
nieużywane części na pewien czas, kiedy pozostałe części również
zostaną wygaszone i będzie można wyłączyć cały program.
Powtarzaj to wszystko przez cały czas. Stary system powinien być coraz
mniej ważny dla biznesu i ostatecznie powinniśmy ujrzeć światło w tunelu,
aby móc go wyłączyć. WARSTWA ZAPOBIEGAJĄCA USZKODZENIU
będzie rozszerzać się i kurczyć, ponieważ różne kombinacje będą zwiększać
lub zmniejszać zależność pomiędzy systemami. Jeżeli inne czynniki pozo-
staną identyczne, najpierw powinieneś rozważyć migrację tych funkcji,
które będą prowadzić do mniejszych WARSTW ZAPOBIEGAJĄCYCH
TRANSFORMACJE 439
699b15244ae879c728b313782703a4d7
6
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ść.
699b15244ae879c728b313782703a4d7
6
Liderzy projektu powinni zdefiniować KONTEKSTY ZWIĄZANE na
bazie funkcjonalnych wymagań integracji oraz relacji zespołów progra-
mistycznych. Gdy tylko zostaną jasno zdefiniowane i będą respektowane
KONTEKSTY ZWIĄZANE oraz MAPA KONTEKSTÓW, wtedy
powinna zostać ochroniona logiczna spójność systemów. Problemy zwią-
zane z komunikacją powinny być przynajmniej uwidocznione, tak aby
można było je rozwiązać.
Niekiedy jednak konteksty modelu są — celowo lub w wyniku
naturalnej zbieżności — w niepoprawny sposób stosowane do rozwią-
zywania problemów innych niż logiczna niespójność wewnątrz systemu.
Zespół może odkryć, że model dużego KONTEKSTU wydaje się zbyt
złożony, aby go zrozumieć jako całość lub całkowicie przeanalizować.
Prowadzi to często do celowego lub przypadkowego podziału KON-
TEKSTU na łatwiejsze do zarządzania części. Taka fragmentacja prowadzi
do utraty wielu możliwości. Warto przypatrzeć się decyzji ustanowienia
dużego modelu w szerszym KONTEKŚCIE, a jeżeli ze względów orga-
nizacyjnych lub politycznych nie jest możliwe, aby trzymać wszystko
wspólnie, to w przypadku jego fragmentacji warto narysować oraz zdefi-
niować granice, które można zachować. Jeżeli jednak duży KONTEKST
ZWIĄZANY odpowiada ważnym potrzebom integracyjnym i poza tą
złożonością modelu wydaje się wykonalny, wtedy podział takiego KON-
TEKSTU może nie być najlepszą odpowiedzią.
Zanim podejmie się decyzję o tego rodzaju poświęceniu, warto
rozważyć inne środki umożliwiające śledzenie dużych modeli. W kolej-
nych dwóch rozdziałach skoncentrujemy się na zarządzaniu złożonością
w dużym modelu dzięki zastosowaniu dwóch szerszych pryncypiów —
destylacji oraz struktur dużej skali.
TRANSFORMACJE 441
699b15244ae879c728b313782703a4d7
6
442 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU
699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 15.
Destylacja
443
699b15244ae879c728b313782703a4d7
6
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.
699b15244ae879c728b313782703a4d7
6
Rysunek 15.1.
Mapa nawigacji
dla strategicznej
destylacji
699b15244ae879c728b313782703a4d7
6
DZIEDZINA GŁÓWNA
699b15244ae879c728b313782703a4d7
6
aktywów, jego najważniejsze jądro musi być eleganckie i w pełni
wykorzystane do utworzenia funkcjonalności aplikacji. Jednak
bardzo rzadko doświadczeni programiści zdają się kierować w stro-
nę technicznej infrastruktury lub starannie definiowalnych pro-
blemów dziedzinowych, które można zrozumieć bez specjalistycz-
nej wiedzy.
Takie części systemu wydają się interesujące dla naukowców zaj-
mujących się informatyką i są traktowane jako coś przyczyniającego się do
zwiększenia profesjonalnych umiejętności, polepszających ich życiorys.
Jądro specjalistyczne, będące częścią modelu, która naprawdę wyróżnia
aplikację i sprawia, że jest ona atutem biznesowym, jest zazwyczaj skła-
dane przez mniej doświadczonych programistów, który pracują z admi-
nistratorami bazy danych nad utworzeniem schematu danych, a następnie
kodują funkcjonalność po funkcjonalności, bez zaprzątania sobie głowy
siłą pojęciową modelu.
Słaby projekt lub implementacja tej części programu prowadzi do
powstania aplikacji, która nigdy nie rozwiązuje istotnych problemów użyt-
kownika, niezależnie od tego, jak dobrze działa techniczna infrastruktura,
a także jak ładne są wspierające funkcjonalności. Pozornie nieistotny pro-
blem może wziąć górę w przypadku, gdy projektowi brak wyraźnego ogól-
nego obrazu, a także względnego znaczenia jego różnych elementów.
Na tę przypadłość cierpiał początkowo jeden z najbardziej pomyśl-
nych projektów, do którego dołączyłem. Jego celem było utworzenie bar-
dzo złożonego systemu pożyczek konsorcjalnych. Większość utalentowa-
nych osób przydzielonych do projektu pracowała nad warstwami mapowania
bazy danych, a także interfejsami do komunikatów, podczas gdy model
biznesowy był w rękach programistów dopiero początkujących w tech-
nologii obiektowej.
Jedyny wyjątek stanowił doświadczony programista obiektowy pra-
cujący nad problemem dziedzinowym, którego celem było utworzenie spo-
sobu dołączania komentarzy do dowolnego długo istniejącego obiektu
dziedziny. Te komentarze mogły być porządkowane w taki sposób, aby
agenci mogli przyjrzeć się uzasadnieniom zapisanym dla niektórych histo-
rycznych decyzji. Programista utworzył również elegancki interfejs użyt-
kownika, który umożliwiał intuicyjny dostęp do uniwersalnych funkcjo-
nalności modelu komentarza.
Te funkcjonalności były przydatne i dobrze zaprojektowane, zostały
więc wdrożone do produkcji.
Niestety, stanowiły one jedynie margines. Utalentowany programista
zamodelował we własny interesujący i ogólny sposób funkcjonalność
699b15244ae879c728b313782703a4d7
6
komentowania, czysto ją zaimplementował ją i przekazał do rąk użytkow-
ników. W tym czasie niekompetentny programista zmienił krytyczny dla
biznesu moduł „pożyczek” w niezrozumiałą sieć, z której projekt prawie
nie mógł się wyplątać.
Proces planowania musi przypisywać zasoby do najbardziej krytycz-
nych miejsc w modelu oraz projekcie. Do tego potrzebne jest zidentyfi-
kowanie tych miejsc oraz ich zrozumienie przez wszystkich podczas faz
planowania i rozwoju.
Wszystkie te miejsca modelu rozróżnialne i centralne dla celów pro-
jektowanej aplikacji tworzą DZIEDZINĘ GŁÓWNĄ. Leży ona w tym
miejscu, w którym do systemu powinna być dodana największa wartość.
Dlatego:
Odparuj system. Znajdź DZIEDZINĘ GŁÓWNĄ oraz udo-
stępnij środki służące do jej łatwego oddzielenia od ogromu wspie-
rającego modelu oraz kodu. Nadaj najbardziej wartościowym i spe-
cjalizowanym pojęciom najbardziej wyostrzony kształt. Staraj się
zachować niewielki RDZEŃ dziedziny.
Do DZIEDZINY GŁÓWNEJ przypisz największe talenty
i odpowiednio je zrekrutuj. Poświęć trochę wysiłku na RDZEŃ
dziedziny, aby odnaleźć model dogłębny i utworzyć projekt ela-
styczny — wystarczający do wypełnienia wizji systemu. Uzasadniaj
inwestycję w dowolną inną część systemu tym, w jakim stopniu
wspiera ona oddestylowany RDZEŃ dziedziny.
Destylacja DZIEDZINY GŁÓWNEJ nie jest prosta, jednak prowadzi
do prostych decyzji. Z pewnością wiele wysiłków będzie kosztować Cię
wyróżnienie RDZENIA, podczas gdy pozostała część projektu utrzymy-
wana będzie w sposób najbardziej praktyczny i ogólny. Jeżeli musisz ukryć
pewne aspekty projektu przed konkurencją, będą one z pewnością sta-
nowić rdzeń DZIEDZINY GŁÓWNEJ. Nie ma właściwie powodu, aby
marnować wysiłki w celu ukrycia pozostałej części. A w przypadku, gdy
z powodu ograniczeń czasowych trzeba dokonać jakiegokolwiek wyboru
pomiędzy dwiema pożądanymi refaktoryzacjami, najpierw należy wybrać
tę, która przynosi najwięcej korzyści DZIEDZINIE GŁÓWNEJ.
***
Wzorce umieszczone w tym rozdziale ułatwiają dostrzeżenie DZIEDZINY
GŁÓWNEJ, jej użycie oraz zmianę.
699b15244ae879c728b313782703a4d7
6
Wybór RDZENIA dziedziny
Będziemy przyglądać się częściom modelu właściwym do reprezentowania
dziedziny biznesowej oraz rozwiązywania problemów biznesowych.
RDZEŃ DZIEDZINY, który wybierzesz, zależy od Twojego punktu
widzenia. Na przykład wiele aplikacji wymaga ogólnego modelu pieniędzy,
mogącego reprezentować różne waluty oraz ich kursy wymiany, a także
przeliczniki. Z drugiej strony, aplikacja wspierająca wymianę walut może
wymagać bardziej rozbudowanego modelu pieniędzy, co byłoby traktowane
jako część jej RDZENIA. Nawet w takim przypadku może istnieć część
modelu, która będzie bardzo ogólna. Ponieważ spostrzeżenia z dziedziny
pogłębiają się wraz z nabywaniem doświadczenia, proces destylacji może
być kontynuowany poprzez oddzielenie ogólnych pojęć modelu pieniądza
i zachowanie tylko tych specjalizowanych w DZIEDZINIE GŁÓWNEJ.
W przypadku aplikacji do obsługi logistyki morskiej RDZENIEM
dziedziny byłby prawdopodobnie model określający, w jaki sposób towary
są łączone ze sobą w celu przewozu, w jaki sposób przekazywana jest
odpowiedzialność w chwili, gdy kontenery przechodzą z rąk do rąk, oraz
w jaki sposób dla poszczególnych kontenerów wyznaczana jest trasa, aby
na koniec dotrzeć do celu. W bankowości inwestycyjnej RDZEŃ mógłby
obejmować modele tworzenia syndykatów dla aktywów uczestników oraz
udziałowców.
DZIEDZINA GŁÓWNA jednej aplikacji może być ogólnym wspie-
rającym komponentem dla innej. Wciąż jednak w jednym projekcie i zazwy-
czaj w jednej firmie może być określony spójny RDZEŃ. Podobnie do
każdej innej części projektu, identyfikacja DZIEDZINY GŁÓWNEJ
powinna następować poprzez kolejne iteracje. Ważna rola pewnego zestawu
relacji może nie być od razu oczywista. Obiekty, które wydają się na
pierwszy rzut oka centralne, mogą następnie przyjąć role wspierające.
Więcej wskazówek na temat tych decyzji może przynieść dyskusja
w kolejnych podrozdziałach, a w szczególności opisującym OGÓLNE
PODDZIEDZINY.
699b15244ae879c728b313782703a4d7
6
Niezmiernie istotne jest, aby przerwać ten cykl, tworząc zespół silnych
programistów, którzy wykazują długoterminowe zaangażowanie oraz zain-
teresowanie pełnieniem funkcji repozytorium wiedzy dziedzinowej, z jed-
nym lub dwoma ekspertami dziedzinowymi, znającymi dogłębnie dany
biznes. Projektowanie dziedziny jest zadaniem interesującym oraz tech-
nicznie wymagającym, pod warunkiem że zostanie potraktowane poważnie.
Można też znaleźć podobnie myślących programistów.
Zazwyczaj niepraktyczne jest wynajmowanie na krótki okres zewnętrz-
nych ekspertów od projektowania tylko po to, aby zdefiniowali oni DZIE-
DZINĘ GŁÓWNĄ, ponieważ to zespół musi zgromadzić wiedzę dziedzi-
nową, a tymczasowy członek zespołu stanowi najsłabsze ogniwo. Z drugiej
strony, dysponowanie ekspertem w roli nauczyciela/mentora może być
bardzo cenne, pomagając zespołowi budować swoje umiejętności w pro-
jektowaniu dziedziny i ułatwiając zastosowanie zaawansowanych pryncy-
piów, których członkowie zespołu prawdopodobnie jeszcze nie opanowali.
Z podobnych przyczyn bardzo mało prawdopodobne jest, aby DZIE-
DZINA GŁÓWNA mogła zostać zakupiona. Zostały już poczynione pewne
wysiłki w celu utworzenia szkieletów modeli właściwych dla różnych dzie-
dzin przemysłowych, do których najbardziej spektakularnych przykładów
należy zaliczyć szkielet CIM do automatyzacji wytwarzania półprzewod-
ników, opracowany przez konsorcjum z tej gałęzi przemysłu o nazwie
SEMATECH. Podobnie IBM opracował już szkielety modeli dla szeregu
biznesów pod wspólną nazwą „San Francisco”. Chociaż jest to kusząca
idea, to jednak jak dotychczas rezultaty nie były zadowalające, być może
z wyjątkiem JĘZYKÓW OPUBLIKOWANYCH, ułatwiających wymianę
danych (patrz rozdział 14.). W książce Domain-Specific Application Frame-
works (Fayad i Johnson 2000 r.) opisany został pobieżnie obecny stan rzeczy.
W miarę jak ta dziedzina będzie się rozwijać, będzie powstawać coraz więcej
różnych szkieletów modeli.
Nawet wtedy jednak należy zachować szczególną ostrożność. Naj-
większa wartość oprogramowania pisanego na zamówienie wynika z cał-
kowitej kontroli na DZIEDZINĄ GŁÓWNĄ. Dobrze zaprojektowany
szkielet może być w stanie udostępnić wysokopoziomową abstrakcję, która
będzie mogła zostać przystosowana do własnych potrzeb. Może to oszczę-
dzić konieczności tworzenia bardziej ogólnych części modelu i pozwolić
na skoncentrowanie się na RDZENIU. Jeżeli jednak szkielet taki będzie
ograniczać Cię w większym stopniu, wtedy istnieją trzy możliwości:
1. Tracisz zasadniczy atut programu. Odsuń ograniczający szkielet od
DZIEDZINY GŁÓWNEJ.
699b15244ae879c728b313782703a4d7
6
2. Obszar rozpatrywany w szkielecie modelu nie jest tak modyfikowalny,
jak myślałeś. Ponownie wyznacz granice DZIEDZINY GŁÓWNEJ,
biorąc pod uwagę jedynie naprawdę charakterystyczną część modelu.
3. Nie posiadasz w DZIEDZINIE GŁÓWNEJ specjalnych potrzeb.
Rozważ rozwiązanie niższego ryzyka, jak na przykład zakup programu
do integracji ze swoją aplikacją.
Niezależnie od sposobu, utworzenie wyróżniającego się oprogramo-
wania wymaga stabilnego zespołu, gromadzącego wiedzę specjalistyczną
i przetwarzającego ją do postaci bogatego modelu. Nie ma dróg na skróty.
Nie ma też cudownych rozwiązań.
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.
699b15244ae879c728b313782703a4d7
6
PODDZIEDZINY OGÓLNE
Niektóre części modelu dodają złożoności bez wychwytywania lub
komunikacji specjalistycznej wiedzy. Wszystko, co nieistotne,
utrudnia zrozumienie i dostrzeżenie DZIEDZINY GŁÓWNEJ.
Model może zostać zablokowany ogólnymi, znanymi przez wszyst-
kich pojęciami lub też innymi szczegółami, które nie należą do
obszaru podstawowego zainteresowania, lecz odgrywają rolę
pomocniczą. Wciąż jednak, pomimo iż ogólne, te inne elementy
są zasadnicze dla funkcjonowania systemu i pełnego wyrażenia
modelu.
Istnieje część modelu, którą chciałbyś przyjąć za daną. Stanowi ona
niezaprzeczalnie część modelu dziedziny, jednak tworzy abstrakcję pojęć,
które byłyby prawdopodobnie potrzebne wielu biznesom. Na przykład
schemat organizacyjny jest potrzebny w pewnej postaci tak różnym rodza-
jom działalności jak przewozy morskie, bankowość lub systemy produk-
cyjne. Wiele aplikacji śledzi księgi przychodów i rozchodów, a także inne
wielkości finansowe, które mogłyby zostać obsłużone przez ogólny model
księgowości.
Bardzo często dużo wysiłku poświęca się na zewnętrzne problemy
dziedziny. Osobiście widziałem dwa oddzielne projekty, które zatrudniły
swoich najlepszych programistów na długie tygodnie, aby przeprojekto-
wać moduły odpowiedzialne za obliczanie dat i czasów w różnych stre-
fach czasowych. Chociaż oczywiście te moduły muszą działać, to jednak
nie stanowią one przecież pojęciowego rdzenia systemu.
Nawet jeżeli tego rodzaju ogólny element modelu okaże się krytyczny,
to jednak cały model dziedziny musi zwracać uwagę na te aspekty sys-
temu, które przynoszą najwięcej wartości dodanej. Aby mogły one przy-
nosić jak najwięcej zysku, muszą być ustrukturyzowane. Bardzo trudno
jest to osiągnąć w sytuacji, gdy RDZEŃ jest wymieszany z tymi wszyst-
kimi innymi czynnikami.
Dlatego:
Zidentyfikuj spójne poddziedziny niestanowiące motywacji
dla projektu. Utwórz ogólne modele tych poddziedzin i umieść
je w oddzielnych MODUŁACH. Nie pozostawiaj w nich żadnych
śladów specjalizacji.
Po ich wydzieleniu nadaj ich rozwojowi niższy priorytet od
tego przyznanego DZIEDZINIE GŁÓWNEJ i unikaj przypisywa-
nia swoich najlepszych programistów do zadań z tych modułów
699b15244ae879c728b313782703a4d7
6
(ponieważ nie zwiększą oni w ten sposób swojej wiedzy dziedzi-
nowej). Dla tego rodzaju OGÓLNYCH PODDZIEDZIN rozważ
również zastosowanie rozwiązań standardowych lub modeli opu-
blikowanych.
***
W trakcie tworzenia tych pakietów możesz skorzystać z kilku dodatko-
wych możliwości:
699b15244ae879c728b313782703a4d7
6
systemów z rozbudowanym API. Widziałem również pomyślne zintegro-
wanie z aplikacją pakietu do rejestrowania błędów. Niekiedy OGÓLNE
PODDZIEDZINY są pakowane w postaci szkieletu, implementującego
bardzo prosty model abstrakcyjny, który może być rozszerzony i zinte-
growany z własną aplikacją. Im bardziej ogólny jest taki komponent,
a także im bardziej oddestylowany jest Twój model, tym większe praw-
dopodobieństwo, że takie połączenie będzie przydatne.
699b15244ae879c728b313782703a4d7
6
Rozwiązanie 3. Oddelegowanie implementacji
Zalety:
Umożliwia głównemu zespołowi skoncentrowanie się na DZIE-
DZINIE GŁÓWNEJ, w której potrzeba najwięcej wiedzy groma-
dzonej w tej dziedzinie.
Umożliwia wykonanie większej pracy bez zwiększania na stałe zespołu,
a także bez zdradzania wiedzy o DZIEDZINIE GŁÓWNEJ.
Wymusza stosowanie projektu używającego interfejsów i pomaga
w utrzymaniu ogólnej poddziedziny, ponieważ specyfikacja jest prze-
kazywana na zewnątrz.
Wady:
Wciąż wymaga czasu głównego zespołu, ponieważ interfejs, standardy
kodowania oraz wszystkie inne ważne aspekty muszą być ogłoszone.
Wymusza bardzo duży narzut związany z przekazaniem własności
kodu z powrotem do zespołu, ponieważ kod utworzony na zewnątrz
musi zostać zrozumiany. (Wciąż jednak ten narzut jest mniejszy niż
dla poddziedzin specjalizowanych, ponieważ zrozumienie ogólnego
modelu prawdopodobnie nie wymaga specjalistycznego przygo-
towania).
Jakość kodu może być różna, w zależności od różnic w zaawanso-
waniu obu zespołów.
Zautomatyzowane testy mogą odgrywać istotną rolę w delegacji.
Od osób implementujących kod powinno się wymagać utworzenia testów
jednostkowych dla dostarczanego przez nie kodu. Szczególnie potężnym
podejściem, gwarantującym pewien poziom jakości, wyjaśniającym spe-
cyfikację, a także ułatwiającym integrację, jest określenie lub nawet utwo-
rzenie automatycznych testów akceptacyjnych dla komponentów, których
utworzenie zostało oddelegowane na zewnątrz. Podejście z „oddelego-
waną implementacją” może być bardzo dobrym połączeniem z „użyciem
opublikowanego projektu lub modelu”.
699b15244ae879c728b313782703a4d7
6
Wady:
Wysiłek związany z bieżącym utrzymaniem oraz szkoleniami.
Bardzo łatwo jest nie doszacować czasu i kosztu związanych z two-
rzeniem tego rodzaju pakietów.
Oczywiście, takie podejście również bardzo dobrze łączy się z „opu-
blikowanym projektem lub modelem”.
OGÓLNE PODDZIEDZINY są miejscem, w którym można spró-
bować zastosować pochodzącą z zewnątrz wiedzę ekspercką dotyczącą
projektowania, ponieważ nie wymagają one głębokiego zrozumienia spe-
cjalizowanej DZIEDZINY GŁÓWNEJ i nie stanowią dobrej możliwości
nauczenia się tej dziedziny. Również bezpieczeństwo informacji nie jest
aż tak dużym zmartwieniem, ponieważ z tymi modelami związanych będzie
niewiele chronionych informacji lub praktyk biznesowych. OGÓLNA
PODDZIEDZINA zmniejsza obciążenie związane ze szkoleniem osób,
które nie są zobligowane do dogłębnego poznania związanej z nią wiedzy.
W miarę upływu czasu zrozumienie tego, co składa się na model
RDZENIA, będzie się zawężać, a coraz więcej ogólnych modeli będzie
dostępnych w postaci zaimplementowanych szkieletów modeli lub cho-
ciaż opublikowanych modeli lub wzorców analitycznych. Obecnie jednak
większość z nich wciąż musimy sami utworzyć. Oddzielenie ich od modelu
DZIEDZINY GŁÓWNEJ jest również bardzo ważne.
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-
699b15244ae879c728b313782703a4d7
6
blem stał się złożony z powodu różnych odmian pomiędzy wieloma
krajami, a także skomplikowanych szczegółów działania międzynarodo-
wej linii zmiany daty. Dysponując już bardziej zdefiniowanymi wyma-
ganiami, zespół zaczął szukać gotowego rozwiązania, lecz żadnego nie
znalazł. Nie miał więc innego wyjścia, niż stworzyć je samemu.
Zadanie wymagało analizy i precyzyjnej inżynierii, dlatego liderzy
zespołów przypisali do niego jednego z najlepszych programistów. Jednak
samo zadanie nie wymagało żadnej wiedzy na temat operacji morskich
i wcale jej nie rozwijało. Z tej przyczyny wybrany został programista, który
pracował przy projekcie w ramach tymczasowego kontraktu.
Programista nie rozpoczął pracy od początku. Sprawdził natomiast
kilka istniejących implementacji stref czasowych, z których większość nie
spełniała wymagań, i zdecydował się zaadaptować publicznie dostępne
rozwiązanie z systemu Unix BSD, używające rozległej bazy danych oraz
implementacji w języku C. Programista odtworzył logikę i napisał proce-
durę importu dla bazy danych.
Problem okazał się nawet trudniejszy, niż oczekiwano (wymagał na
przykład zaimportowania bazy danych ze specjalnymi przypadkami), jed-
nak ostatecznie kod został utworzony i zintegrowany z RDZENIEM,
a sam produkt końcowy został dostarczony.
W drugim projekcie sprawy zaczęły biec innym torem. Firma ubez-
pieczeniowa tworzyła nowy system do obsługi zgłoszeń i planowała prze-
chwytywać czasy różnych zdarzeń (na przykład wypadku samochodowego,
wystąpienia gradu itp.). Dane miały być zapisywane w czasie lokalnym,
dlatego wymagana była funkcjonalność stref czasowych.
Kiedy dołączyłem do projektu, zespół przypisał do zadania młodego,
lecz bardzo bystrego programistę, chociaż dokładne wymagania aplikacji
były wciąż tworzone i nawet nie podjęto prób wykonania początkowej
iteracji. Programista zaczął sumiennie budować model stref czasowych od
podstaw.
Nie mając wiedzy, co będzie wymagane, założył, że model powinien
być na tyle elastyczny, aby obsłużyć wszystkie przypadki. Potrzebował
pomocy przy tak trudnym problemie, dlatego do zadania został również
dołączony starszy członek zespołu. Razem utworzyli złożony kod, jednak
ponieważ nie używała go żadna konkretna aplikacja, nigdy nie było jasne,
czy kod działał poprawnie.
Z różnych przyczyn projekt uziemiono, a kod do stref czasowych nie
został nigdy użyty. Jednak nawet gdyby go zastosowano, mogłoby wystar-
czyć proste zachowanie czasów lokalnych z etykietą wskazującą na strefę
czasową, nawet bez żadnej konwersji, ponieważ były to głównie dane
referencyjne, a nie podstawa obliczeń. Nawet gdyby okazało się, że
699b15244ae879c728b313782703a4d7
6
konwersje są niezbędne, to wszystkie dane były gromadzone z terenu
Ameryki Północnej, w której tłumaczenia stref czasowych są relatywnie
proste.
Głównym kosztem takiej koncentracji na strefach czasowych było
zaniedbanie modelu DZIEDZINY GŁÓWNEJ. Gdyby tyle samo energii
skierowano na tamten obszar, zespół mógłby utworzyć funkcjonujący
prototyp swojej własnej aplikacji oraz pierwszą wersję działającego modelu
dziedziny. Co więcej, zaangażowani programiści, związani długoterminowo
z projektem, powinni byli zagłębić się w dziedzinę ubezpieczeń, budując
w zespole tę krytyczną wiedzę.
Jedyną rzeczą, którą oba projekty uczyniły dobrze, było jasne oddzie-
lenie OGÓLNEGO modelu stref czasowych od DZIEDZINY GŁÓW-
NEJ. Model właściwy dla transportu morskiego lub ubezpieczeń niepo-
trzebnie związałby właściwy model z ogólnym, który go wspierał. A to
z kolei utrudniłoby zrozumienie RDZENIA (ponieważ zawierałby nie-
istotne szczegóły na temat stref czasowych). Utrudniłoby to również utrzy-
manie MODUŁU odpowiedzialnego za strefy czasowe (ponieważ zajmująca
się tym osoba musiałaby zrozumieć RDZEŃ oraz wspólne relacje ze stre-
fami czasowymi).
699b15244ae879c728b313782703a4d7
6
Ogólny nie oznacza
możliwy do ponownego wykorzystania
Zauważ, że chociaż uwypukliłem zalety wynikające z ogólności poddzie-
dzin, to jednak nie wspomniałem nic o ponownym wykorzystaniu kodu.
W różnych okolicznościach zastosowanie gotowych rozwiązań mogłoby
mieć sens lub też okazać się zupełnie nieuzasadnione. Zakładając jednak,
że potrzebny kod będziemy implementować sami (w projekcie lub jako
zadanie oddelegowane), nie powinniśmy się zbytnio przejmować możli-
wością ponownego wykorzystania tego kodu. Zaprzeczałoby to zasadni-
czej motywacji leżącej u podstaw destylacji, mówiącej o tym, że powinie-
neś jak najwięcej wysiłku koncentrować na DZIEDZINIE GŁÓWNEJ,
a w OGÓLNE PODDZIEDZINY inwestować tylko wtedy, gdy jest to
konieczne.
Możliwość ponownego użycia niekiedy się zdarza, jednak nie zawsze
dotyczy to kodu. Często lepiej jest ponownie użyć modelu, jak w przy-
padku projektu lub modelu, który został opublikowany. Jeżeli jednak
musisz utworzyć swój własny model, mógłby być on cenny dla któregoś
z późniejszych projektów. Chociaż jednak pojęcia tego modelu można
zastosować w wielu sytuacjach, nie musisz go rozwijać całego. Możesz
wybrać jedynie te części, które są potrzebne dla biznesu.
Chociaż bardzo rzadko może zdarzyć Ci się konieczność projektowania do
powtórnego wykorzystania, musisz przestrzegać stosowania pojęć generycznych.
Wprowadzenie elementów modelu właściwych dla danej dziedziny prze-
mysłu ma dwojaki koszt. Po pierwsze, utrudni to przyszłe programowanie.
Chociaż w tym momencie potrzebujesz jedynie małej części modelu
poddziedziny, to jednak Twoje potrzeby będą wzrastać. Wprowadzając do
projektu cokolwiek, co nie jest częścią pojęcia, zdecydowanie utrudniasz
czyste rozszerzenie systemu, bez konieczności całkowitego przebudo-
wania starszych części, a także przeprojektowania innych używających
ich modułów.
Drugim i zdecydowanie bardziej istotnym powodem jest fakt, iż
te pojęcia właściwe dla przemysłu przynależą bardziej do DZIEDZINY
GŁÓWNEJ lub ich własnych bardziej specjalizowanych poddziedzin.
A specjalizowane modele są nawet bardziej wartościowe niż ogólne.
699b15244ae879c728b313782703a4d7
6
Początkowa wersja systemu bardzo często potwierdza techniczną archi-
tekturę i skłania projektantów do utworzenia systemów zewnętrznych
obsługujących niektóre z pomocniczych PODDZIEDZIN OGÓLNYCH,
ponieważ są one zazwyczaj łatwiejsze do analizy. Bądź ostrożny, ponieważ
może to udaremnić cel zarządzania ryzykiem.
Projekty narażone są na ryzyko z obu stron. Niektóre pociągają za
sobą większe ryzyko techniczne, a inne większe ryzyko związane z mode-
lowaniem dziedziny. Szkielet działającego systemu może zminimalizować
ryzyko jedynie do tego stopnia, do jakiego jest on początkową wersją
uwzględniającą problematyczne elementy rzeczywistego systemu. Bardzo
łatwo jest nie doszacować ryzyka związanego z modelowaniem dziedziny.
Może ono przyjąć postać nieprzewidzianej złożoności, nieodpowiedniego
dostępu do ekspertów biznesowych lub też luk w kluczowych umiejęt-
nościach programistów.
Dlatego z wyjątkiem sytuacji, w której zespół ma udokumentowane
umiejętności, a dziedzina jest bardzo znajoma, pierwsza wersja systemu
powinna być oparta na części DZIEDZINY GŁÓWNEJ, chociaż może
ona być wciąż prosta.
To samo pryncypium stosuje się do dowolnego procesu próbującego
popchnąć do przodu zadanie o dużym stopniu ryzyka. Sama DZIEDZINA
GŁÓWNA stanowi duże ryzyko, ponieważ jest ona często niespodzie-
wanie trudna, a bez niej projekt nie może zakończyć się powodzeniem.
***
Większość wzorców destylacyjnych w tym rozdziale pokazuje, w jaki
sposób zmienić model wraz z kodem w celu destylacji DZIEDZINY
GŁÓWNEJ. Niemniej jednak dwa kolejne wzorce, tj. OPIS WIZJI
DZIEDZINY (ang. DOMAIN VISION STATEMENT) oraz RDZEŃ
WYRÓŻNIONY (ang. HIGHLIGHTED CORE), ukażą, w jaki sposób
przy użyciu dodatkowych dokumentów można z małą inwestycją uspraw-
nić komunikację, a także świadomość RDZENIA, jak również ukierun-
kować wysiłki implementacyjne.
699b15244ae879c728b313782703a4d7
6
OPIS WIZJI DZIEDZINY
Na początku projektu model zazwyczaj nawet wcale nie istnieje.
Tymczasem istnieje już potrzeba ukierunkowania prac nad jego
rozwojem. W późniejszych etapach rozwoju będzie istnieć
konieczność wyjaśnienia wartości systemu, bez odwoływania się
do głębokiej analizy modelu. Krytyczne aspekty systemu mogą
również rozciągać się na wiele KONTEKSTÓW ZWIĄZANYCH,
jednak z definicji wynika, że te oddzielne modele nie mogą być
ustrukturyzowane, aby ukazywać swój główny przedmiot zain-
teresowania.
Wiele zespołów projektowych tworzy „opisy wizji” dla kierownictwa.
Najlepsze z nich przedstawiają określoną wartość, jaką aplikacja przyniesie
organizacji. Niektóre wspominają, że strategicznym majątkiem firmy jest
utworzenie modelu dziedziny. Dokument opisujący wizję bywa zazwyczaj
porzucany po znalezieniu finansowania dla projektu i nigdy nie jest uży-
wany w rzeczywistym procesie implementacyjnym lub nawet nie jest
odczytywany przez zespół techniczny.
OPIS WIZJI DZIEDZINY jest modelowany już po utworzeniu
takich dokumentów, jednak koncentruje się na naturze modelu dziedziny,
a także jego przydatności dla firmy. Dokument ten może być używany
bezpośrednio przez kierownictwo oraz zespół techniczny w trakcie
wszystkich faz implementacji w celu nadzorowania procesu przypisywania
zasobów, kierowania wyborami podczas modelowania, a także edukacji
członków zespołu. Jeżeli model dziedziny służy wielu osobom, wtedy taki
dokument może wskazać, w jaki sposób ich interesy są równoważone.
Dlatego:
Napisz krótki opis (około jednej strony) DZIEDZINY POD-
STAWOWEJ oraz korzyści, jakie ona przynosi, czyli „propozycji
wartości”. Zignoruj aspekty, które nie wyróżniają modelu dzie-
dziny spośród innych. Pokaż, w jaki sposób model dziedziny
służy różnym interesom i je równoważy. Postaraj się utrzymać go
w postaci zawężonej. Utwórz opis wcześnie i rozwijaj go w trakcie
uzyskiwania nowych przemyśleń na temat dziedziny.
OPIS WIZJI DZIEDZINY może być wykorzystywany jako dro-
gowskaz, wskazujący zespołowi projektowemu wspólny kierunek podczas
ciągłego procesu destylacji modelu oraz kodu. Może być on dzielony
z nietechnicznymi członkami zespołów, kierownictwem, a nawet klien-
tami (oczywiście z wyjątkiem sytuacji, w których zawiera on informacje
zastrzeżone).
699b15244ae879c728b313782703a4d7
6
A to, chociaż jest bardzo ważne,
To jest część OPISU WIZJI DZIEDZINY nie stanowi OPISU WIZJI DZIEDZINY
System rezerwacji dla linii lotniczych System rezerwacji dla linii lotniczych
Model może reprezentować priorytety pasażerów, Interfejs użytkownika powinien być prosty dla
a także strategie rezerwacji linii lotniczych i rów- doświadczonych użytkowników, lecz także dostępny
noważyć te obie kwestie za pomocą elastycznej dla tych, którzy go używają po raz pierwszy.
polityki. Model pasażera powinien odzwierciedlać System będzie dostępny przez internet, przez
„relacje”, jakie linia lotnicza chce utworzyć z lojal- transfer danych do innych systemów, a także być
nymi klientami. Dlatego też powinien on repre- może przy użyciu innych interfejsów użytkownika.
zentować historię pasażera w przydatnej, skonden- Dlatego interfejs programu będzie zaprojektowany
sowanej postaci, jego uczestnictwo w specjalnych wokół języka XML wraz z niezbędnymi war-
programach, przynależność do grupy biznesowych stwami transformacji do stron WWW lub innych
klientów strategicznych itp. systemów.
Reprezentowane są różne role różnych użyt- Kolorowa animowana wersja logo musi być
kowników (np. pasażera, agenta, kierownika) w celu zachowywana na maszynie klienta, aby mogła zostać
wzbogacenia modelu relacji, a w razie koniecz- szybko pobrana podczas kolejnych przyszłych wizyt.
ności także wysłania niezbędnych informacji do Gdy klient wyśle rezerwację, potwierdź ją
szkieletu bezpieczeństwa. graficznie w ciągu 5 s.
Model powinien wspierać efektywne poszu- Szkielet bezpieczeństwa zautoryzuje użytkow-
kiwanie trasy/miejsca, a także integrację z innymi nika, a następnie ograniczy jego dostęp do pewnych
powszechnymi systemami do rezerwacji biletów funkcjonalności na podstawie poziomu dostępu
lotniczych. przypisanego do różnych ról użytkowników.
***
699b15244ae879c728b313782703a4d7
6
OPIS WIZJI DZIEDZINY wyznacza zespołowi wspólny kierunek.
Zazwyczaj potrzebny będzie łącznik pomiędzy wysokopoziomowym
OPISEM a szczegółowym kodem lub modelem...
699b15244ae879c728b313782703a4d7
6
RDZEŃ WYRÓŻNIONY
OPIS WIZJI DZIEDZINY identyfikuje DZIEDZINĘ GŁÓWNĄ w szer-
szym zakresie, pozostawia jednak kwestię identyfikacji poszczególnych
elementów modelu RDZENIA indywidualnym subiektywnym kapry-
som programisty. O ile w zespole nie ma komunikacji na szczególnie
wysokim poziomie, wtedy sam OPIS WIZJI będzie mieć na niego nie-
wielki wpływ.
***
Nawet jeżeli członkowie zespołów mogą znać szeroko
wszystko to, co składa się na DZIEDZINĘ GŁÓWNĄ, to różne
osoby nie będą wybierać tych samych elementów, a nawet ta sama
osoba może nie być spójna w kolejnych dniach. Wysiłek umysłowy
związany ze stałym filtrowaniem modelu w celu określenia jego
kluczowych elementów absorbuje naszą koncentrację, którą mogli-
byśmy lepiej wykorzystać do myślenia nad projektem, a co wię-
cej, wymaga on rozległej wiedzy na temat modelu. DZIEDZINĘ
GŁÓWNĄ musi być łatwiej dostrzec.
Istotne zmiany strukturalne do kodu są idealnym sposobem
wyróżnienia DZIEDZINY GŁÓWNEJ, jednak w krótkim termi-
nie nie są one praktyczne. W rzeczywistości takie duże zmiany kodu
trudno jest przeprowadzić bez dobrego widoku, którego zespołowi
brakuje.
Strukturalne zmiany w organizacji modelu, w rodzaju partycjono-
wania PODDZIEDZIN OGÓLNYCH, a także kilku innych, które przed-
stawimy w późniejszej części tego rozdziału, mogą umożliwić MODU-
ŁOM przekazanie historii. Jednakże jest to zbyt ambitne zadanie, aby
zabrać się do niego od razu.
Do wspierania tych agresywnych technik będziesz prawdopodobnie
potrzebować czegoś lżejszego. Mogą wystąpić ograniczenia, które będą
uniemożliwiać Ci fizyczne oddzielenie RDZENIA. Możesz też mieć do
dyspozycji istniejący kod, który nie będzie dobrze wyróżniać RDZENIA,
jednak aby efektywnie zrefaktoryzować kod ku lepszemu zrozumieniu,
będziesz musiał w jakiś sposób ten RDZEŃ dostrzec. A nawet w zaawan-
sowanym stadium projektu kilka ostrożnie wybranych diagramów lub
dokumentów może stanowić punkt zaczepienia oraz rozpoczęcia działań
reszty zespołu.
Tego rodzaju problemy narastają jednakowo dla projektów wykorzy-
stujących rozbudowane modele UML, a także takich (szczególnie pro-
jektów XP), które utrzymują tylko niewiele zewnętrznych dokumentów,
a polegają za to na kodzie jako podstawowym repozytorium modelu.
699b15244ae879c728b313782703a4d7
6
Zespół projektowy stosujący programowanie ekstremalne mógłby zacho-
wać podejście minimalistyczne, utrzymując te elementy wspierające w bar-
dziej swobodnej i przemijającej postaci (na przykład jako odręcznie nary-
sowany diagram na ścianie, widoczny dla wszystkich zainteresowanych).
Takie techniki mogą jednak ładnie wpisywać się w cały proces.
Wyznaczona uprzywilejowana część modelu razem z uwzględniającą
ją implementacją jest refleksem na modelu, a niekoniecznie stanowi skład-
nik tego modelu. Sprawdzi się dowolna technika, która ułatwi wszystkim
zrozumienie DZIEDZINY GŁÓWNEJ. Tego rodzaju klasę rozwiązań
mogą reprezentować dwie specyficzne techniki.
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.
699b15244ae879c728b313782703a4d7
6
Najlepszym sposobem ograniczenia tych ryzyk jest zachowanie
absolutnie minimalistycznego podejścia. Trzymaj się z dala od niecie-
kawych szczegółów, koncentrując się na centralnych abstrakcjach oraz
ich interakcjach. Umożliwi to powolniejszą dezaktualizację dokumentu,
ponieważ model na tym poziomie pozostaje zazwyczaj bardziej stabilny.
Utwórz dokument, który będzie rozumiany przez nietechnicznych
członków zespołu. Używaj go jako wspólnego widoku nakreślającego
wszystko to, co powinno się wiedzieć, oraz podręcznika, od którego
wszyscy członkowie zespołu mogą rozpocząć swoją analizę modelu oraz
kodu.
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
699b15244ae879c728b313782703a4d7
6
jednak pewny, że musieliśmy jakoś dojść do wspólnego obrazu naszej
DZIEDZINY GŁÓWNEJ i wszystkie wysiłki powinny być skoncentro-
wane na tym celu.
Zamiast refaktoryzowania jeszcze raz przeszedłem przez dokument,
posiłkując się pomocą analityków biznesowych, mających dużą ilość ogól-
nej wiedzy o dziedzinie ubezpieczeniowej, a także w szczególności o wyma-
ganiach aplikacji, która mieliśmy utworzyć. Dzięki temu zidentyfikowałem
kilka podrozdziałów prezentujących podstawowe wyróżniające się pojęcia,
z którymi musieliśmy pracować. Dostarczyłem też opis poruszania
się po modelu, który w jasny sposób pokazywał RDZEŃ oraz jego relacje
z pomocniczymi funkcjonalnościami.
Od takiego nowego spojrzenia rozpoczęliśmy kolejne wysiłki w pro-
totypowaniu aplikacji i w bardzo krótkim czasie uzyskaliśmy uproszczoną
aplikację, prezentującą wymaganą funkcjonalność.
Prawie dwa kilogramy papieru zostały przekształcone w majątek
firmy poprzez stworzenie kilku stron tabel i kilku podkreśleń w żółtym
kolorze.
Taka technika nie jest właściwa tylko dla diagramów obiektowych na
papierze. Zespół używający diagramów w języku UML mógłby do identy-
fikacji kluczowych elementów użyć „stereotypu”. Zespół wykorzystujący
w charakterze repozytorium modelu kod programu mógłby użyć w tym
celu komentarzy, być może ustrukturyzowanych jako Java Doc, lub zasto-
sować własne narzędzie w swoim środowisku programistycznym. Dopóki
programista może w łatwy sposób zrozumieć, co jest w DZIEDZINIE
GŁÓWNEJ, a co poza nią, nie będzie ważna żadna konkretna technika.
Dlatego (jako kolejna z postaci RDZENIA WYRÓŻNIONEGO):
Oznacz każdy element DZIEDZINY GŁÓWNEJ w podsta-
wowym repozytorium modelu, nie starając się szczególnie wyja-
śniać ich znaczenia. Chodzi o to, by programista bez dużego
wysiłku mógł zrozumieć, co jest w RDZENIU, a co poza nim.
DZIEDZINA GŁÓWNA jest teraz jasno widoczna dla wszystkich
osób pracujących z modelem. Do jej wyróżnienia nie będzie potrzebny
duży wysiłek, a koszt utrzymania będzie znikomy, przynajmniej w sytuacji,
gdy model jest utworzony na tyle dobrze, aby wyróżniać sposób współ-
działania poszczególnych części.
699b15244ae879c728b313782703a4d7
6
oraz koordynacji. Podczas pracy w warstwie infrastruktury wpływ zmiany
może być jasny, lecz może nie być już tak oczywisty w warstwie dziedziny.
Dzięki pojęciu DZIEDZINY GŁÓWNEJ wpływ zmian może być
jasny. Zmiany do modelu DZIEDZINY GŁÓWNEJ powinny wywoły-
wać duży efekt. Zmiany do powszechnie używanych elementów ogólnych
mogą wymagać wielu zmian kodu, jednak wciąż nie powinny powodować
zmiany pojęciowej, której przyczyną mogą być zmiany w RDZENIU.
Używaj dokumentu destylacji jako przewodnika. Kiedy programiści
zdadzą sobie sprawę, że sam dokument destylacji wymaga zmiany, aby
pozostać zsynchronizowany ze zmianą w kodzie oraz modelu, sami zwrócą
się o pomoc. Nie ma znaczenia, czy w sposób zasadniczy zmieniają ele-
menty DZIEDZINY GŁÓWNEJ lub ich relacje, czy też zmieniają granice
RDZENIA, dołączając do niego lub odejmując coś innego. Rozpowszech-
nianie zmiany w modelu na cały zespół jest niezbędne, niezależnie od tego,
jakich kanałów komunikacyjnych zespół używa, włączając konieczność
dystrybucji nowej wersji dokumentu destylacji.
W przypadku, gdy dokument destylacji wskazuje na podstawy
DZIEDZINY GŁÓWNEJ, służy jako dobra wskazówka znaczenia
zmiany w modelu. Jeżeli zmiana w modelu lub kodzie dotyka
dokumentu destylacji, wymaga konsultacji z innymi członkami
zespołu. Kiedy zmiana zostanie już wykonana, wymagać będzie
natychmiastowego powiadomienia członków zespołu oraz roz-
powszechnienia nowej wersji dokumentu. Zmiany poza RDZE-
NIEM lub dotyczące szczegółowych elementów nieumieszczonych
w dokumencie destylacji mogą być zintegrowane bez konsultacji
oraz powiadamiania i będą napotykane przez innych członków
zespołu w trakcie ich pracy. Następnie programiści będą mieć
pełną samodzielność sugerowaną przez metodologię XP.
***
Chociaż OPIS WIZJI oraz RDZEŃ WYRÓŻNIONY przekazują infor-
macje i wskazują kierunek, to jednak same nie modyfikują modelu ani
kodu. Podział PODDZIEDZIN OGÓLNYCH w sposób fizyczny elimi-
nuje pewne przeszkadzające elementy. W kolejnych wzorcach przyjrzymy
się sposobom strukturalnej zmiany modelu oraz projektu sprawiającym,
że DZIEDZINA GŁÓWNA staje się bardziej przejrzysta i łatwiejsza do
zarządzania.
699b15244ae879c728b313782703a4d7
6
SPÓJNE MECHANIZMY
Mechanizmy hermetyzacji stanowią standardowe pryncypium projekto-
wania obiektowego. Ukrywanie złożonych algorytmów w metodach wraz
z nazwami odkrywającymi intencję oddziela „co” od „jak”. Taka technika
ułatwia zrozumienie oraz zastosowanie projektu. Wciąż jednak napotyka
na naturalne ograniczenia.
Obliczenia mogą niekiedy osiągnąć granicę złożoności, która
będzie zaciemniać projekt. Pojęciowe „co” jest zagmatwane w tech-
nicznym „jak”. Duża liczba metod udostępniających algorytmy do
rozwiązywania problemów zaciemnia metody wyrażające problem.
Rozrastanie się procedur stanowi oznakę problemu w modelu. Refak-
toryzacja ku głębszemu zrozumieniu może odsłonić w modelu oraz pro-
jekcie to, które elementy są lepiej dostosowane do rozwiązania problemu.
Pierwsze podejście polega na poszukaniu modelu, który będzie upraszczać
mechanizmy obliczeniowe. Wtedy może powstawać wrażenie, że nie-
które mechanizmy są spójne. Obliczenia pojęciowe nie będą prawdopo-
dobnie obejmować wszystkich skomplikowanych przypadków, które będą
wymagane. Nie mówimy teraz o wyłapaniu wszystkich „obliczeń”, jednak
wydobycie części wspólnej powinno ułatwić zrozumienie pozostałych
mechanizmów.
Dlatego:
Podziel pojęciowo SPÓJNY MECHANIZM na oddzielne lek-
kie szkielety. Zwróć uwagę szczególnie na formalizmy oraz dobrze
udokumentowane kategorie algorytmów. Ujawnij możliwości
danego szkieletu przy użyciu INTERFEJSU UJAWNIAJĄCEGO
ZAMIAR. W tym momencie inne elementy dziedziny będą mogły
skoncentrować się na wyrażeniu problemu („co”), delegując niu-
anse rozwiązania („jak”) do danego szkieletu aplikacji.
Wszystkie oddzielone w ten sposób mechanizmy są umieszczane
w swoich wspierających rolach, pozostawiając mniejszą i bardziej wyra-
zistą DZIEDZINĘ GŁÓWNĄ, używającą mechanizmu przez interfejs
o bardziej deklaratywnym stylu.
Rozpoznanie standardowego algorytmu lub formalizmu przenosi część
złożoności projektu do przestudiowanego zestawu pojęć. Z taką pomocą
możemy implementować rozwiązanie z zaufaniem, a także niewielką liczbą
prób i błędów. Możemy też liczyć na innych programistów znających
mechanizm lub przynajmniej mogących na niego spojrzeć. Przypomina
to zalety opublikowania modelu PODDZIEDZIN OGÓLNYCH, jednak
opublikowany algorytm lub formalne obliczenia można odnaleźć znacznie
częściej, ponieważ ten poziom nauk informatycznych jest lepiej zbadany.
699b15244ae879c728b313782703a4d7
6
Wciąż jednak znacznie częściej będziesz musiał sam utworzyć coś nowego.
Postaraj się skoncentrować na obliczeniach i unikaj dodawania do SPÓJ-
NEGO MECHANIZMU wyrazistego modelu dziedziny. Istnieje wszak
podział odpowiedzialności: model DZIEDZINY GŁÓWNEJ lub POD-
DZIEDZIN OGÓLNYCH formułuje fakt, regułę lub problem. SPÓJNY
MECHANIZM rozwiązuje regułę lub dokańcza obliczenie w sposób
określony przez model.
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.
699b15244ae879c728b313782703a4d7
6
Gdyby mechanizm został wstawiony do modelu dziedziny, koszto-
wałoby to nas podwójnie. Model zostałby połączony z określoną metodą
rozwiązywania problemu, ograniczając przyszłe opcje. Co więcej, model
organizacji zostałby znacznie skomplikowany i zamącony. Utrzymywanie
mechanizmu oddzielonego od modelu umożliwiło użycie stylu dekla-
ratywnego w opisie organizacji, który był znacznie bardziej przejrzysty.
A skomplikowany kod do manipulacji grafem został odizolowany w cał-
kowicie technicznym szkielecie na podstawie dowiedzionych algorytmów,
które mogły być utrzymywane i testowane w pełnej izolacji.
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.
699b15244ae879c728b313782703a4d7
6
Kiedy MECHANIZM
jest częścią DZIEDZINY GŁÓWNEJ
Prawie zawsze będziesz chciał usunąć MECHANIZMY z DZIEDZINY
GŁÓWNEJ. Jedynym wyjątkiem będzie zastrzeżony MECHANIZM,
będący kluczowym elementem składającym się na wartość oprogramowa-
nia. Dzieje się tak niekiedy w przypadku wysoce specjalizowanych algo-
rytmów. Na przykład, jeżeli jedną z wyróżniających się funkcjonalności
aplikacji do logistyki morskiej byłby szczególnie efektywny algorytm opra-
cowywania harmonogramów, wtedy taki MECHANIZM mógłby zostać
uznany za część pojęciowego RDZENIA. Pracowałem kiedyś przy prze-
znaczonym dla banku inwestycyjnego projekcie, w którym ściśle chronione
algorytmy do szacowania ryzyka były zdecydowanie częścią DZIEDZINY
GŁÓWNEJ (w rzeczywistości były one tak ściśle chronione, że nawet
większość programistów RDZENIA nie miała możliwości ich zobaczyć).
Oczywiście, te algorytmy są prawdopodobnie szczególną implementacją
zestawu reguł przewidujących ryzyko. Głębsza analiza mogłaby prowadzić
do głębszego modelu, który ujawniłby te reguły, a pozostawił zahermety-
zowany mechanizm rozwiązujący.
Jednak to stanowiłoby kolejne inkrementalne usprawnienie w pro-
jekcie na kolejny dzień. Decyzja, czy podjąć ten kolejny krok, byłaby
oparta na analizie korzyści i kosztu. Jak trudne byłoby opracowanie nowego
projektu? Jak trudno jest zrozumieć i zmodyfikować obecny projekt? Czy
bardziej zaawansowany projekt uprościłby życie osób zmuszonych go uży-
wać? I oczywiście, czy ktokolwiek ma pomysł, jaką postać może przyjąć
nowy model?
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.
699b15244ae879c728b313782703a4d7
6
Takie pełne cykle są powszechne, jednak nie wracają one do swojego
punktu startowego. Rezultat końcowy jest zazwyczaj dogłębnym modelem
różnicującym w bardziej precyzyjny sposób swoje fakty, cele oraz MECHA-
NIZMY. Pragmatyczna refaktoryzacja zachowuje ważne zalety pośred-
nich etapów, odrzucając niepotrzebne komplikacje.
699b15244ae879c728b313782703a4d7
6
RDZEŃ ODDZIELONY
Elementy w modelu mogą częściowo służyć DZIEDZINIE GŁÓW-
NEJ, a częściowo odgrywać role wspierające. Elementy RDZENIA
mogą być ściśle związane z tymi bardziej ogólnymi. Pojęciowa
spójność RDZENIA może nie być silna ani widoczna. Wszystko to
przyczynia się do zamieszania i zaplątania RDZENIA. Programiści
nie mogą jasno ujrzeć najbardziej istotnych relacji, co prowadzi
do słabego projektu.
Poprzez refaktoryzację PODDZIEDZIN OGÓLNYCH możesz
oczyścić dziedzinę z niejasnych szczegółów, sprawiając, aby RDZEŃ był
bardziej widoczny. Niemniej jednak identyfikacja oraz wyjaśnianie wszyst-
kich tych poddziedzin jest trudnym zadaniem, a niektóre z nich nie wy-
dają się warte zachodu. Jednocześnie w tym czasie cała ważna DZIE-
DZINA GŁÓWNA jest pozostawiona ze splątanymi pozostałościami.
Dlatego:
Zrefaktoryzuj model, aby oddzielić pojęcia RDZENIA od
pojęć wspierających (włączając również błędnie zdefiniowane),
i wzmocnij jego spójność, redukując zależność od reszty kodu.
Zrefaktoryzuj wszystkie elementy ogólne oraz wspierające do
postaci innych obiektów i umieść je w oddzielnych pakietach,
nawet w sytuacji, gdy oznacza to refaktoryzację modelu w sposób,
który oddziela ściśle związane ze sobą elementy.
Takie działanie spełnia te same pryncypia, które stosowaliśmy do
PODDZIEDZIN OGÓLNYCH, jednak z innej strony. Spójne poddzie-
dziny zajmujące centralne miejsce w aplikacji mogą być zidentyfikowane
oraz partycjonowane do własnych spójnych pakietów. To, co stanie się
z nierozróżnialną masą pozostawioną z boku, jest też ważne, lecz nie aż
tak bardzo. Może ona pozostać mniej więcej na swoim miejscu lub też
zostać umieszczona w pakietach opartych na znaczących klasach. Osta-
tecznie coraz więcej pozostałości można zrefaktoryzować do postaci POD-
DZIEDZIN OGÓLNYCH, jednak w krótkiej perspektywie zadziała każde
rozwiązanie, dlatego skoncentrujmy się na RDZENIU ODDZIELONYM.
***
Kroki niezbędne do refaktoryzacji RDZENIA ODDZIELONEGO są
typowe i przypominają następujące:
1. Zidentyfikuj RDZEŃ poddziedziny (opierając się prawdopodobnie
na dokumencie destylacji).
699b15244ae879c728b313782703a4d7
6
2. Przesuń powiązane klasy do nowego MODUŁU, nazwanego zgodnie
z wiążącymi je pojęciami.
3. Zrefaktoryzuj kod w celu odcięcia danych i funkcjonalności, które
nie są bezpośrednim wyrażeniem pojęcia. Umieść usunięte aspekty
do (prawdopodobnie nowych) klas w innych pakietach. Spróbuj
umieścić je z pojęciowo związanymi zadaniami, jednak nie marnuj
zbyt wiele czasu, starając się uzyskać perfekcyjny podział. Skoncentruj
się na wyczyszczeniu RDZENIA poddziedziny, a także sprawieniu,
aby odwołania z niego do innych pakietów były bezpośrednie
i oczywiste.
4. Zrefaktoryzuj nowy MODUŁ ODDZIELONEGO RDZENIA
w celu uproszczenia jego relacji oraz interakcji, a także usprawnienia
komunikacji i zminimalizowania oraz objaśnienia jego relacji z innymi
MODUŁAMI (ten cel stanie się bieżącym celem refaktoryzacji).
5. Powtarzaj te same kroki z kolejnymi RDZENIAMI poddziedziny do
chwili, gdy RDZEŃ ODDZIELONY będzie kompletny.
699b15244ae879c728b313782703a4d7
6
Czas na oddzielenie RDZENIA ODDZIELONEGO przychodzi
wtedy, gdy dysponujesz dużym KONTEKSTEM ZWIĄZANYM krytycz-
nym dla całego systemu, w którym zasadnicza część modelu jest zagmatwana
przez dużą liczbę wspierających ją funkcjonalności.
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.
699b15244ae879c728b313782703a4d7
6
Rysunek 15.2.
Zastanówmy się teraz, co stanowi esencję modelu. Zazwyczaj najlep-
szym miejscem do rozpoczęcia analizy jest pewna „konkluzja”. Może ona
skłonić nas do przyjrzenia się taryfom lub fakturom, jednak z pewnością
będziemy musieli spojrzeć na OPIS WIZJI DZIEDZINY. Oto wyjątek
z tej wizji:
Zwiększa widoczność operacji i udostępnia narzędzia spełniające
wymagania klientów w sposób szybszy i bardziej niezawodny...
699b15244ae879c728b313782703a4d7
6
Rysunek 15.3.
Wiarygodne W przypadku najważniejszej części klasy zostały już przeniesione
dostarczenie do nowego pakietu, jednak do samego modelu zostało wprowadzonych kilka
ładunku zgodnie zmian.
z wymaganiami Po pierwsze, klasa Customer Agreement (umowa z klientem) ogra-
klienta jest nicza teraz Handling Step (etap obsługi). Jest to spostrzeżenie typowe dla
kluczowym celem momentu, gdy zespół oddziela RDZEŃ. Ponieważ uwaga jest skupiona
tego projektu na efektywnym i poprawnym dostarczeniu ładunku, staje się oczywiste,
699b15244ae879c728b313782703a4d7
6
że ograniczenia dotyczące transportu w umowie z klientem (Customer
Agreement) są zasadnicze i powinny być wyrażone jasno w modelu.
Kolejna zmiana jest bardziej pragmatyczna. W zrefaktoryzowanym
modelu klasa Customer Agreement (umowa z klientem) jest połączona
bezpośrednio z klasą Cargo (ładunek) i nie wymaga przejścia przez klasę
Customer (klient), co oznacza, że będzie musiała zostać dołączona
w momencie zarezerwowania transportu ładunku, podobnie jak klasa
Customer. W czasie rzeczywistego transportu klasa Customer nie jest
aż tak właściwa dla operacji logistycznych, jak sama umowa. W innym
modelu musi zostać odnaleziony właściwy klient, zgodnie z rolą, jaką pełni
on w procesie transportu, a następnie należy go odpytać o umowę. Taka
interakcja będzie zaciemniać każdą historię, jaką będziesz chciał opo-
wiedzieć o tym modelu. Nowa asocjacja czyni najważniejsze scenariusze
tak prostymi i bezpośrednimi, jak to tylko możliwe. W tej chwili łatwe
staje się wyciągnięcie klienta (Customer) całkowicie z RDZENIA.
Ale dlaczego właściwie mielibyśmy to robić? Przecież nasza uwaga
jest skupiona na spełnianiu wymagań klienta, dlatego na pierwszy rzut oka
klasa Customer wydaje się przynależeć do RDZENIA. Kiedy jednak
teraz klasa Customer Agreement dostępna jest bezpośrednio, interakcje
w trakcie transportu nie muszą zazwyczaj dotykać klasy Customer.
A podstawowy model klienta jest teraz bardzo ogólny.
Dla klasy Leg (etap) istnieje silny argument przemawiający za pozo-
stawieniem jej w RDZENIU. Staram się zachować minimalizm w sto-
sunku do RDZENIA, a klasa Leg ma ściślejsze relacje z klasami Transport
Schedule (harmonogram transportu), Routing Service (usługa traso-
wania) oraz Location (położenie), z których żadna nie musi być umiesz-
czona wewnątrz RDZENIA. Gdyby jednak wiele historii, które chciałbym
opowiedzieć, używało klasy Leg, przeniósłbym ją do pakietu Delivery
i cierpiał z powodu dziwności tego sztucznego rozdzielenia jej od
innych klas.
W tym przykładzie wszystkie definicje klas są takie same jak wcześniej.
Jednak bardzo często destylacja wymaga refaktoryzacji samych klas w celu
oddzielenia odpowiedzialności ogólnych od właściwych dla dziedziny.
Teraz mamy już ODDZIELONY RDZEŃ, więc refaktoryzacja
dobiega końca. Jednak pakiet Shipping zawiera „wszystko to, co pozo-
stało po oddzieleniu RDZENIA”. Aby uzyskać bardziej opisowy pakiet,
moglibyśmy wykonać kolejne refaktoryzacje w sposób przedstawiony na
rysunku 15.4.
Dotarcie do tego miejsca mogłoby wymagać kilku powtórzeń tego
procesu. Nie wszystko musi zostać wykonane od razu. W tej chwili
uzyskaliśmy jeden pakiet ODDZIELONEGO RDZENIA, jeden dla
699b15244ae879c728b313782703a4d7
6
Rysunek 15.4.
PODDZIEDZINY OGÓLNEJ oraz dwa właściwe dla dziedziny wystę-
MODUŁY istotne
dla poddziedzin pujące w rolach pomocniczych. Głębsze spostrzeżenia mogłyby ostatecznie
poza RDZENIEM utworzyć z klasy Customer PODDZIEDZINĘ OGÓLNĄ; moglibyśmy
pojawiają się, też stworzyć większą specjalizację dla pakietu transportu (shipping).
gdy RDZEŃ Rozpoznawanie przydatnych, istotnych MODUŁÓW jest czynnością
ODDZIELONY należącą do zakresu modelowania (jak omówiliśmy to w rozdziale 5.). Pro-
jest gotowy gramiści współpracują z ekspertami dziedzinowymi w zakresie destylacji
strategicznej jako składowej procesu przetwarzania wiedzy.
699b15244ae879c728b313782703a4d7
6
RDZEŃ ABSTRAKCYJNY
699b15244ae879c728b313782703a4d7
6
Dlatego:
Zidentyfikuj najbardziej podstawowe pojęcia w modelu i zre-
faktoryzuj je do oddzielnych klas, klas abstrakcyjnych lub inter-
fejsów. Zaprojektuj ten model abstrakcyjny w taki sposób, aby
wyrażał większość interakcji pomiędzy znaczącymi komponen-
tami. Umieść ten ogólny abstrakcyjny model w swoim własnym
MODULE, podczas gdy specjalizowane szczegółowe klasy imple-
mentacji powinny zostać pozostawione w swoich własnych
MODUŁACH, zdefiniowanych przez poddziedziny.
Większość specjalizowanych klas będzie teraz odwoływać się do
MODUŁU RDZENIA ABSTRAKCYJNEGO zamiast do innych specja-
lizowanych MODUŁÓW. RDZEŃ ABSTRAKCYJNY daje konkretny
widok na główne pojęcia oraz ich interakcje.
Proces faktoryzacji RDZENIA ABSTRAKCYJNEGO nie jest mecha-
niczny. Na przykład, jeżeli wszystkie klasy, do których często odwoływano
się w MODUŁACH, zostałyby automatycznie przeniesione do oddzielnego
MODUŁU, skutkowałoby to prawdopodobnie bezsensownym bałaganem.
Modelowanie RDZENIA ABSTRAKCYJNEGO wymaga głębokiego zro-
zumienia kluczowych pojęć oraz ról, które one odgrywają w głównych
interakcjach systemu. Innymi słowy, stanowi to przykład refaktoryzacji ku
głębszemu zrozumieniu. A taka zmiana wymaga zazwyczaj istotnej zmiany
projektu.
RDZEŃ ABSTRAKCYJNY powinien na koniec w dużym stopniu
przypominać dokument destylacji (jeżeli oba były użyte w tym samym
projekcie, a dokument destylacji był rozwijany razem z aplikacją w miarę
pogłębiania się zrozumienia dziedziny). Oczywiście, RDZEŃ ABSTR-
KACYJNY zostanie utworzony w kodzie, dlatego będzie bardziej rygo-
rystyczny i kompletny.
***
699b15244ae879c728b313782703a4d7
6
Chociaż wystąpienie przełomu do dogłębnego modelu zawsze
przynosi korzyści, to jednak właśnie DZIEDZINA GŁÓWNA może
zmienić trajektorię całego projektu.
699b15244ae879c728b313782703a4d7
6
484 ROZDZIAŁ 15. DESTYLACJA
699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 16.
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
Programiści zaczęli więc refaktoryzować kod w celu dostosowania
go do nowej struktury. Należało ponownie zdefiniować MODUŁY w taki
sposób, aby nie przekraczały warstw. W niektórych przypadkach zostały
też zrefaktoryzowane niektóre odpowiedzialności obiektów, tak aby każdy
z nich jasno przynależał do określonej warstwy. W trakcie tego procesu
definicje warstw pojęciowych zostały dostosowane na podstawie doświad-
czenia w ich stosowaniu. Warstwy, MODUŁY oraz obiekty ewoluowały
razem do czasu, gdy ostatecznie cały projekt był zgodny z zarysami tej
nowej struktury warstwowej.
Warstwy nie były MODUŁAMI ani żadnym innym artefaktem
w kodzie. Były one raczej obejmującym całość zestawem reguł wyzna-
czających granice oraz relacje każdego z MODUŁÓW oraz obiektów
w projekcie, nawet w przypadku interfejsów z innymi systemami.
Wymuszenie tego uporządkowania spowodowało powrót projektu
do komfortowej spójności. Ludzie mogli już mniej więcej wiedzieć,
gdzie szukać określonej funkcji. Osoby pracujące samodzielnie mogły
podejmować decyzje projektowe pozostające w szerokiej spójności ze
sobą. Granica złożoności została podniesiona.
699b15244ae879c728b313782703a4d7
6
sposób poszczególne elementy pasują do siebie, a także uniemożliwia
ujrzenie całości obrazu. Nauka wyniesiona z jednej części projektu nie
będzie przenoszona do innych elementów, co sprawi, że projekt ostatecz-
nie będzie mieć specjalistów od różnych MODUŁÓW, którzy nie będą
w stanie pomóc sobie nawzajem poza swoim wąskim zakresem specjali-
zacji. CIĄGŁA INTEGRACJA rozbija fragmenty KONTEKSTÓW
ZWIĄZANYCH.
W dużych systemach pozbawionych obejmujących całość
pryncypiów, pozwalających na interpretację elementów w kon-
tekście ich roli we wzorcach rozciągających się na cały projekt,
programiści nie mogą ujrzeć całości struktury. Musimy być w stanie
zrozumieć rolę poszczególnych części w całości bez zagłębiania się w szcze-
góły innych struktur.
Dlatego „struktury dużej skali” stanowią swoisty język pozwalający prowa-
dzić dyskusję o systemie w dużej skali i tak go rozumieć. Zestaw wysokopo-
ziomowych pojęć lub reguł określa wzorzec projektu dla całego systemu.
To porządkujące pryncypium może prowadzić projekt, jak również poma-
gać w jego zrozumieniu. Pomaga również koordynować niezależne efekty
pracy, ponieważ istnieć będzie wspólne pojęcie dotyczące większego obrazu,
czyli tego, w jaki sposób role różnych części kształtują cały system.
Opracuj zestaw reguł lub ról, a także relacji rozciągających
się na cały system, które będą umożliwiać zrozumienie miejsca,
które każdy z elementów zajmuje w systemie — nawet bez szcze-
gółowej wiedzy o odpowiedzialności tego elementu.
Struktura może być ograniczona do jednego KONTEKSTU ZWIĄ-
ZANEGO, jednak zazwyczaj będzie rozciągać się na więcej, umożliwia-
jąc umieszczenie w tym porządku pojęciowym wszystkich zespołów oraz
podsystemów związanych z projektem. Dobra struktura udostępnia spoj-
rzenie w model i dopełnia proces destylacji.
Większości struktur dużej skali nie można odwzorować w języku
UML i właściwie nie ma takiej potrzeby. Większość tego rodzaju struktur
kształtuje oraz wyjaśnia model i projekt, jednak nie pojawia się w nim.
Stanowią one zwyczajnie dodatkowy poziom komunikacji na temat pro-
jektu. W przykładach umieszczonych w tym rozdziale dostrzeżesz wiele
nieformalnych diagramów UML, na które nałożyłem informacje o struk-
turach dużej skali.
W przypadku względnie małego zespołu i niezbyt skomplikowanego
modelu podział na dobrze nazwane MODUŁY, destylacja oraz niefor-
malna koordynacja pośród programistów mogą okazać się wystarczające
do utrzymania modelu w dobrej organizacji.
699b15244ae879c728b313782703a4d7
6
Struktury dużej skali mogą uratować projekt, jednak źle dopasowana
struktura może w dużym stopniu przeszkodzić w rozwoju systemu. W tym
rozdziale zajmiemy się wzorcami umożliwiającymi pomyślne ustruktury-
zowanie projektu na tym poziomie.
Rysunek 16.1.
Niektóre wzorce
struktury dużej skali
699b15244ae879c728b313782703a4d7
6
PORZĄDEK EWOLUCYJNY
Wielu programistów doświadczyło kosztu związanego z nieustrukturyzo-
wanym projektem. W celu uniknięcia anarchii projekty narzucają archi-
tektury, które ograniczają na szereg sposobów swobodne programowanie.
Niektóre architektury techniczne rzeczywiście rozwiązują techniczne
problemy, w rodzaju komunikacji sieciowej lub trwałości danych, jed-
nak kiedy architektura próbuje wkraczać w obszar modelu aplikacji lub
dziedziny, może to spowodować inne problemy. Architektury bardzo często
uniemożliwiają programistom tworzenie projektów lub modeli, które
funkcjonują dobrze dla istotnych problemów. Te najbardziej ambitne
mogą nawet oddzielić programistów aplikacji od potęgi technicznej samym
języków programowania. I niezależnie od tego, czy jest to architektura
ukierunkowana technicznie, czy też na dziedzinę, to jeżeli z góry unie-
możliwia wiele decyzji projektowych, może stać się ciasnym gorsetem
w chwili, gdy zmienią się wymagania lub też pogłębi się ich zrozumienie.
W ciągu ostatnich lat niektóre architektury techniczne (jak J2EE)
stały się obowiązujące, natomiast struktury dużej skali w warstwie dzie-
dziny nie zostały zbytnio zbadane. Potrzeby różnią się zdecydowanie
pomiędzy poszczególnymi aplikacjami.
Jakiekolwiek odgórne ograniczenie dotyczące struktury dużej skali
będzie prawdopodobnie kosztowne. W miarę postępowania prac projek-
towych prawie na pewno znajdziesz bardziej dopasowaną strukturę, a może
nawet odkryjesz, że ta wybrana będzie uniemożliwiać Ci podjęcie drogi
projektowej, która w znaczny sposób uprościłaby i wyjaśniła aplikację.
Możesz być w stanie używać określonej struktury, jednak utracisz pewne
możliwości. Praca zwolni, gdy będziesz próbował poszukiwać obejść lub
też negocjować z architektami. Jednak Twój szef będzie myślał, że archi-
tektura jest gotowa. Przecież miała upraszczać aplikację, dlaczego więc
nie pracujesz nad aplikacją, zamiast męczyć się z tymi wszystkimi pro-
blemami architektury? Kierownicy oraz zespoły architektów mogą być
nawet niekiedy otwarci na propozycje, jeżeli jednak każda zmiana będzie
stanowić heroiczną bitwę, będzie to zbyt wyczerpujące.
Projekty otwarte dla wszystkich tworzą systemy, których nikt
nie może zrozumieć w całości, a utrzymanie takich systemów jest
bardzo kosztowne. Jednak architektury mogą ograniczać projekt
z powodu odgórnych założeń i pozbawiać programistów lub
projektantów poszczególnych elementów aplikacji zbyt wielu
możliwości. Bardzo szybko programiści ograniczą aplikację w taki
sposób, aby pasowała ona do struktury, lub też zepsują ją i nie
będzie ona mieć żadnej struktury, co ponownie przywróci pro-
blem niekoordynowanego rozwoju aplikacji.
699b15244ae879c728b313782703a4d7
6
Sam problem nie tkwi w istnieniu reguł, lecz raczej w ich sztywno-
ści oraz źródle pochodzenia. Jeżeli reguły zarządzające projektem rzeczy-
wiście pasują do okoliczności, nie będą stawać na przeszkodzie, lecz raczej
rzeczywiście popchną rozwój w dobrym kierunku, jak również wymu-
szą spójność.
Dlatego:
Pozwól na powstanie w aplikacji pojęciowych struktur dużej
skali, umożliwiając zmiany struktury na zupełnie inny rodzaj
w trakcie trwania projektu. Nie ograniczaj zbytnio decyzji doty-
czących szczegółów modelu oraz projektu, które muszą zostać
podjęte z użyciem szczegółowej wiedzy.
Poszczególne elementy mają naturalną i przydatną organizację i są
wyrażone w sposób, który nie musi stosować się do całości, dlatego narzu-
canie globalnych reguł sprawia, że te elementy będą mniej doskonałe.
Decyzja o stosowaniu struktur dużej skali faworyzuje łatwość zarządzania
modelem jako całością kosztem optymalnej struktury poszczególnych
jego elementów. Dlatego będzie istnieć kompromis pomiędzy unifikacją
struktury a wolnością wyrażania indywidualnych komponentów w naj-
bardziej naturalny dla nich sposób. Ten problem może zostać zminima-
lizowany poprzez wybór struktury na podstawie rzeczywistego doświad-
czenia i wiedzy z zakresu dziedziny, a także poprzez unikanie struktur
zbytnio restrykcyjnych. Bardzo ładne dopasowanie struktury do dziedziny
i wymagań rzeczywiście znacznie ułatwia szczegółowe modelowanie oraz
projektowanie, pomagając w szybkim wyeliminowaniu wielu alternatyw.
Struktura udostępnia również skróty prowadzące do decyzji pro-
jektowych, które mogłyby w zasadzie być zdefiniowane również podczas
pracy na poziomie poszczególnych obiektów, jednak w praktyce wymaga-
łoby to zbyt długiego czasu lub przynosiłoby niespójne rezultaty. Oczy-
wiście, konieczne jest cały czas stosowanie stałego procesu refaktoryzacji,
jednak struktury uczynią ten proces znacznie łatwiejszym do zarządzania
i pomogą różnym osobom w utworzeniu wspólnych rozwiązań.
Struktura dużej skali musi być zazwyczaj możliwa do zastosowania
w poprzek KONTEKSTÓW ZWIĄZANYCH. Dzięki kolejnym powtó-
rzeniom procesu w rzeczywistym projekcie struktura utraci swoje cechy
ściśle wiążące ją z określonym modelem i utworzy te, które będą nawią-
zywać do ZARYSÓW KONCEPCYJNYCH dziedziny. Nie oznacza to
jednak, że nie będzie mieć założeń w stosunku do modelu, lecz nie będzie
narzucać całemu projektowi pomysłów dopasowanych do określonej lokal-
nej sytuacji. Musi on pozostawić zespołom projektowym pracującym
w różnych kontekstach pewną swobodę różnicowania modelu w sposób
zaspokajający ich lokalne potrzeby.
699b15244ae879c728b313782703a4d7
6
Struktury dużej skali muszą również zawierać praktyczne ograni-
czenia w zakresie rozwoju aplikacji. Na przykład projektanci mogą nie
mieć kontroli nad modelem pewnych części systemu, szczególnie w przy-
padku podsystemów starych lub zewnętrznych. Taka sytuacja mogłaby
zostać obsłużona poprzez zmianę struktury w taki sposób, aby lepiej
pasowała do określonych elementów zewnętrznych — na przykład poprzez
określenie specyficznych sposobów odwoływania się aplikacji do tych
elementów. Innym sposobem jest nadanie strukturze wystarczającej swo-
body w celu objęcia dziwnej rzeczywistości.
W przeciwieństwie do MAPY KONTEKSTÓW, struktura dużej
skali jest opcjonalna. Powinna być ona narzucana w przypadku, gdy prze-
mawiają za tym czynniki kosztowe oraz potencjalne zyski, a także gdy
odnaleziona zostanie pasująca struktura. W rzeczywistości nie jest ona
konieczna w systemach na tyle prostych, aby mogły być zrozumiane po
ich podziale na MODUŁY. Struktury dużej skali powinny być stoso-
wane w przypadku, gdy zostanie odnaleziona taka, która w dużym
stopniu wyjaśnia system bez narzucania nienaturalnych ograni-
czeń na tworzenie modelu. Ponieważ źle dopasowania struktura
jest dużo gorsza od żadnej, najlepiej nie próbować znaleźć ide-
alnej i ogólnej, lecz jednak postarać się wyodrębnić minimalny
zestaw rozwiązujący powstałe problemy. Mniej znaczy lepiej.
Struktura dużej skali może być bardzo pomocna, lecz wciąż może
istnieć kilka wyjątków od jej stosowania. Muszą one jednak być w jakiś
sposób oznaczone, aby programiści mogli założyć, że struktura jest stoso-
wana powszechnie w projekcie, chyba że zostanie to jasno zastrzeżone.
Jeżeli jednak takie wyjątki zaczną się powielać, wtedy struktura musi
zostać zmieniona lub odrzucona.
***
Jak już wspomnieliśmy, nie lada wyczynem jest utworzenie struktury,
która daje programistom wystarczającą wolność, wciąż jednak chroniąc
przed chaosem. Chociaż w zakresie technicznej architektury systemów
informatycznych zostało już wykonane wiele pracy, to jednak na temat
strukturyzacji warstwy dziedziny powstało mało opracowań. Niektóre
podejścia osłabiają paradygmat obiektowości, jak chociażby te, które
rozbijają dziedzinę poprzez zadania aplikacji lub przypadki użycia. Cały
obszar jest wciąż mało rozwinięty. Zauważyłem już kilka ogólnych struk-
tur dużej skali, które powstały w różnych projektach. W tym rozdziale
omówię cztery z nich. Jeden z nich może pasować do Twoich potrzeb
lub też prowadzić do pomysłów na strukturę dopasowaną do Twojego
projektu.
699b15244ae879c728b313782703a4d7
6
METAFORA SYSTEMU
Myślenie w przenośni dominuje w tworzeniu oprogramowania, szcze-
gólnie w przypadku modelowania. Jednak w ujęciu stosowanym w pro-
gramowaniu ekstremalnym pojęcie „metafory” zaczęło oznaczać określony
sposób użycia metafory do uporządkowania rozwoju całego systemu.
***
Podobnie jak zapora ogniowa może ochronić budynek przed roz-
przestrzeniającym się z sąsiednich budynków pożarem, tak samo progra-
mowa „zapora ogniowa” może ochronić lokalną sieć komputerową od
zagrożeń z większych sieci na zewnątrz. Ta metafora wpłynęła na archi-
tekturę sieciową i ukształtowała całą kategorię produktową. Dla klientów
dostępnych jest wiele rywalizujących ze sobą zapór ogniowych, opraco-
wanych niezależnie, jednak w ogólnym rozumieniu mogą one być stoso-
wane w pewnym stopniu wymiennie. Osoby początkujące w kwestiach
sieciowych bardzo szybko rozumieją pomysł. Tego rodzaju wspólne
zrozumienie w całej branży, a także wśród klientów zawdzięcza całkiem
sporo dobrze dobranej metaforze.
Wciąż jednak nie jest to dokładna analogia, a jej wydźwięk działa
w obie strony. Zastosowanie metafory zapory ogniowej doprowadziło do
powstania barier programowych, które są niekiedy niedostatecznie wybiór-
cze i wpływają na pożądaną wymianę danych, a nie oferują żadnego zabez-
pieczenia przeciw zagrożeniom powstającym wewnątrz zapory. Na przy-
kład bardzo zagrożone są sieci bezprzewodowe. Jasność wybranej metafory
zapory ogniowej była błogosławieństwem, jednak wszystkie metafory mają
ograniczenia2.
Projekty informatyczne są zazwyczaj bardzo abstrakcyjne
i trudne do zrozumienia. Programiści wraz z użytkownikami
potrzebują namacalnych sposobów umożliwiających zrozumie-
nie systemu i uwspólnianie całościowego jego obrazu.
Na jednym poziomie metafora działa tak dogłębnie, że możemy
uznać, że ma ona wpływ na cały projekt. Systemy mają „warstwy”, które
„leżą na” innych. Mają one „rdzenie” oraz ich „środki”. Niekiedy jednak
przychodzi metafora, która może wyrażać wspólną ideę całego projektu
i umożliwiać wspólne jej zrozumienie wszystkim członkom zespołu.
W takiej sytuacji system jest rzeczywiście kształtowany przez meta-
forę. Programista może podjąć decyzje projektowe zgodne z metaforą
2
Pojęcie METAFORY SYSTEMU zrozumiałem ostatecznie, gdy słuchałem
wykładu Warda Cunninghama o przykładzie z zaporą ogniową.
699b15244ae879c728b313782703a4d7
6
systemu. Tego rodzaju spójność pozwoli innym programistom zinter-
pretować wiele elementów złożonego systemu w kontekście tej samej
metafory. Programiści oraz eksperci dziedzinowi będą mieć w dyskusji
wspólny punkt odniesienia, który może być bardziej konkretny od samego
modelu.
METAFORA SYSTEMU jest luźną, łatwą do zrozumienia strukturą
dużej skali, która harmonizuje z paradygmatem obiektowym. Ponieważ
METAFORA SYSTEMU jest wciąż tylko analogią dziedziny, różne modele
mogą jej używać jedynie w przybliżonym stopniu, co pozwala na jej
stosowanie w wielu KONTEKSTACH ZWIĄZANYCH, pomagając
w koordynacji pracy pomiędzy nimi.
METAFORA SYSTEMU stała się popularnym podejściem, ponie-
waż jest ona jedną z głównych praktyk w programowaniu ekstremalnym
(Beck 1999 r.). Niestety, w przypadku bardzo małej liczby projektów
odnaleziono naprawdę przydatne METAFORY, dlatego podjęto próby
spychania całego pomysłu do dziedzin, gdzie dają one efekt przeciwny do
zamierzonego. Wymowna metafora wprowadza ryzyko, że projekt zaczerp-
nie te aspekty analogii, które nie są pożądane dla danego problemu, lub
że cała analogia być może jest kusząca, jednak może nie być odpowiednia.
Reasumując, METAFORA SYSTEMU jest dobrze znaną postacią
struktury dużej skali, przydatną w niektórych projektach. W ładny sposób
również ilustruje ona ogólne pojęcie struktury.
Dlatego:
Jeżeli w systemie ujawni się konkretna analogia wychwytu-
jąca wspólne zrozumienie przez członków zespołu, a także wyda-
jąca się prowadzić sposób rozumowania w przydatnym kierunku,
zaadaptuj ją do struktury dużej skali. Wokół tej metafory zorga-
nizuj cały projekt i dołącz ją do JĘZYKA WSZECHOBECNEGO.
METAFORA SYSTEMU powinna ułatwiać komunikację doty-
czącą systemu i kierować jego rozwojem. W ten sposób zwiększa
się spójność różnych części systemu, potencjalnie nawet pomię-
dzy różnymi KONTEKSTAMI ZWIĄZANYMI. Ponieważ jednak
wszystkie metafory są niedokładne, staraj się ciągle sprawdzać
ich dostosowanie i bądź gotów porzucić je w chwili, gdy staną na
drodze projektu.
***
699b15244ae879c728b313782703a4d7
6
Dlaczego nie potrzebujemy metafory „naiwnej”
Ponieważ w większości projektów nie uwidacznia się żadna przydatna
metafora, niektóre osoby w społeczności programowania ekstremalnego
zaczęły dyskutować nad metaforami naiwnymi, przez które rozumiały sam
model dziedziny.
Jednym z problemów związanych z tym pojęciem jest fakt, iż dojrzały
model dziedziny jest wszystkim, tylko nie modelem naiwnym. W rzeczy-
wistości stwierdzenie: „Przetwarzanie listy płac przypomina linię mon-
tażową” jest prawdopodobnie widokiem znacznie bardziej naiwnym od
modelu będącego produktem wielu powtórzeń procesu przetwarzania
wiedzy z ekspertami dziedzinowymi, który został już udowodniony
poprzez jego ścisłe związanie z implementacją działającej aplikacji.
Określenie naiwna metafora powinno być wycofane.
METAFORY SYSTEMU nie są przydatne na wszystkich projektach.
Ogólnie biorąc, struktura dużej skali nie jest zasadnicza. W 12 zasadach
programowania ekstremalnego rola METAFORY SYSTEMU może zostać
zastąpiona JĘZYKIEM WSZECHOBECNYM. Projekty powinny roz-
szerzyć ten JĘZYK METAFORAMI SYSTEMU lub innymi strukturami
dużej skali, jeżeli tylko zostaną odnalezione takie, które dobrze do tego
systemu pasują.
699b15244ae879c728b313782703a4d7
6
WARSTWY ODPOWIEDZIALNOŚCI
W całej książce poszczególnym obiektom przypisano wąskie zestawy
powiązanych odpowiedzialności. Projektowanie sterowane odpowiedzial-
nością ma zastosowanie również w większej skali.
***
W sytuacji, gdy każdy poszczególny obiekt ma już określone
odpowiedzialności, nie istnieją wskazówki, możliwości i obszary
spójne umożliwiające jednolitą obsługę dużych części dziedziny.
Aby nadać spójność dużemu modelowi, przydatne staje się narzu-
cenie na przypisywane odpowiedzialności pewnej struktury.
Kiedy nabędziesz głębokiego zrozumienia dziedziny, zaczną stawać
się widoczne szerokie wzorce. Niektóre dziedziny dysponują naturalnym
rozwarstwieniem. Pewne pojęcia lub czynności zachodzą w tle innych
elementów, zmieniających się z różnych powodów niezależnie i z różną
szybkością. W jaki sposób możemy wykorzystać tę naturalną strukturę,
uwidocznić ją i uczynić przydatną? Rozwarstwienie sugeruje podział na
warstwy, będący jednym z najbardziej udanych wzorców projektowania
architektonicznego (Buschman i inni 1996 r.).
Warstwy są podziałem systemu, w którym każdy członek danej
warstwy jest świadomy usług znajdujących się w warstwie „poniżej” i może
ich użyć. Jest natomiast niezależny od warstw „powyżej” nie ma ich
świadomości. W trakcie rysowania zależności MODUŁÓW są one często
układane w taki sposób, że MODUŁ z zależnościami znajduje się poniżej
obiektów, od których zależy. W taki sposób niekiedy warstwy porządkują
się tak, że żaden obiekt z niższego poziomu nie jest pojęciowo zależny
od tych z poziomów wyższych.
Jednak tego rodzaju tymczasowe tworzenie warstw, chociaż ułatwia
śledzenie zależności i bardzo często jest zgodne z intuicją, nie daje zbyt
dużo przemyśleń na temat modelu, a także nie ukierunkowuje decyzji
modelujących. Musimy dysponować czymś bardziej zamierzonym.
W modelu z naturalnym rozwarstwieniem warstwy pojęciowe mogą
być zdefiniowane wokół głównych odpowiedzialności, jednocząc dwa silne
pryncypia podziału na warstwy oraz projektu sterowanego odpowie-
dzialnością.
Takie odpowiedzialności muszą być zdecydowanie szersze od tych,
które zazwyczaj przyporządkowywane są do indywidualnych obiektów,
co zaprezentujemy wkrótce w odpowiednich przykładach. W trakcie pro-
jektowania indywidualnych MODUŁÓW oraz AGREGATÓW są one
kształtowane w taki sposób, aby zawierać się w granicach jednej z tych
699b15244ae879c728b313782703a4d7
6
Rysunek 16.2.
Tymczasowy podział
na warstwy: O czym
mówią te wszystkie
pakiety?
ważnych odpowiedzialności. Tego rodzaju nazwane samodzielne grupo-
wanie odpowiedzialności mogłoby zwiększyć komunikatywność systemu
modułowego, ponieważ w ten sposób odpowiedzialności MODUŁÓW
mogłyby być łatwiej zinterpretowane. Jednak połączenie wysokopozio-
mowych odpowiedzialności z podziałem na warstwy daje nam nowe orga- Wzorcem podziału
nizacyjne pryncypium dla systemu. na warstwy,
który najlepiej nadaje
Dlatego:
się do WARSTW
Spójrz na pojęciowe zależności w modelu, a także na często- ODPOWIEDZIAL-
tliwości oraz źródła zmian różnych elementów dziedziny. Jeżeli NOŚCI, jest odmiana
wykryjesz w niej naturalne warstwy, zrzutuj je jako odpowie- nazywana
SWOBODNYM
dzialności o szerokim abstrakcie. Te odpowiedzialności powinny SYSTEMEM
przekazywać historię o wysokopoziomowym celu oraz projekcie WARSTWOWYM
Twojego systemu. Zrefaktoryzuj model w taki sposób, aby odpo- (ang. RELAXED
LAYERED SYSTEM)
wiedzialności każdego obiektu dziedziny, AGREGATU i MODU-
(Buschmann i inni
ŁU ładnie pasowały do odpowiedzialności jednej z warstw. 1996 r., str. 45),
Jest to dość abstrakcyjny opis, jednak po zapoznaniu się z kilkoma umożliwiająca
przykładami stanie się on oczywisty. Symulator komunikacji z satelitami, komponentom warstwy
dostęp do dowolnej
o którym opowieść otwierała ten rozdział, miał podział na warstwy swojej
niższej warstwy,
odpowiedzialności. Widziałem już dobry efekt stosowania WARSTW a nie tylko tej
ODPOWIEDZIALNOŚCI w dziedzinach tak różnych jak kontrola pro- znajdującej się
dukcji lub zarządzanie finansowe. bezpośrednio poniżej.
699b15244ae879c728b313782703a4d7
6
***
Kolejny przykład szczegółowo bada WARSTWY ODPOWIEDZIAL-
NOŚCI, aby spowodować odczucie odkrycia dowolnego typu struktury
dużej skali, a także sposobu, w jaki kierują one i ograniczają modelowa-
nie oraz projektowanie.
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.
699b15244ae879c728b313782703a4d7
6
Rysunek 16.3.
Podstawowy model
dziedziny transportu
dla wyznaczania tras
ładunków
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
699b15244ae879c728b313782703a4d7
6
użytkownicy tego systemu nie martwią się tym problemem (gdyby firma
zaangażowana była w obie te czynności i chciała je koordynować, wtedy
zespół programistów mógłby rozważyć inny schemat podziału na warstwy,
być może z dwiema różnymi warstwami w rodzaju „Operacji transporto-
wych” oraz „Operacji ładunkowych”).
Trudniejszą decyzję stanowi miejsce umieszczenia klienta (Custo-
mer). W niektórych rodzajach działalności klienci stają się przelotni. Są
istotni jedynie w trakcie dostarczania paczki, a następnie prawie się o nich
zapomina aż do kolejnego razu. Taka właściwość uczyniłaby klientów
jedynie problemem operacyjnym, szczególnie w usłudze dostarczania
paczek, skupiającej się na obsłudze poszczególnych klientów. Jednak nasza
hipotetyczna firma transportowa stara się kultywować długotrwałe rela-
cje z klientami, a większość zleceń przychodzi od powracających klientów.
Biorąc pod uwagę te zamierzenia użytkowników biznesowych, należy stwier-
dzić, że obiekt klienta przynależy do warstwy potencjału. Jak widzisz, nie
była to jedynie techniczna decyzja, a raczej próba wychwycenia oraz komu-
nikacji wiedzy o dziedzinie.
Ponieważ asocjacja pomiędzy ładunkiem (Cargo) a klientem (Custo-
mer) może być analizowana tylko w jednym kierunku, REPOZYTO-
RIUM ładunku będzie potrzebować zapytania, które będzie odszukiwać
wszystkie ładunki dla danego klienta. I tak istniały dobre powody do zapro-
jektowania tego w ten sposób, jednak z narzutem struktury dużej skali
ten model będzie stanowić teraz wymaganie.
Rozróżnienie pomiędzy operacjami a potencjałem rozjaśnia cały obraz,
więc uporządkowanie modelu wciąż jest rozwijane. Po kilku tygodniach
eksperymentowania zespół decyduje się podjąć inne rozróżnienie. W więk-
szości elementów obie początkowe warstwy koncentrują się na sytu-
acjach lub planach takich, jakie są. Jednak obiekt Router (oraz wiele innych
elementów pominiętych w tym przykładzie) nie jest częścią bieżących
operacji ani planów. Pomaga za to podjąć decyzje o ich zmianie. Zespół
definiuje nową warstwę odpowiedzialną za „wsparcie decyzji”.
699b15244ae879c728b313782703a4d7
6
Rysunek 16.5.
Zapytanie zastępuje
dwukierunkową
asocjację,
która narusza
podział na warstwy
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
699b15244ae879c728b313782703a4d7
6
Rysunek 16.7.
Refaktoryzacja
modelu w celu z potencjałem transportowym. Struktura dużej skali oparta na głębokim
dostosowania go
zrozumieniu dziedziny bardzo często popycha model w kierunku, który
do nowej
wyjaśnia jego znaczenie.
struktury
warstwowej
Nowy model pasuje teraz dobrze do struktury dużej skali.
Programista przyzwyczajony do wybranych warstw może łatwiej
dostrzegać role oraz zależności pomiędzy poszczególnymi elementami.
Wartość struktury dużej skali wzrasta wraz ze zwiększaniem się złożoności.
Zauważ, że chociaż do ilustracji tego przykładu użyłem zmodyfi-
kowanego diagramu UML, ten rysunek jest tylko jednym ze sposobów
komunikacji podziału na warstwy. UML nie obejmuje tej notacji, dlatego te
dodatkowe informacje są nakładane jedynie dla czytelnika. Jeżeli w Twoim
projekcie ostatecznym dokumentem projektowym jest kod, pomocne
byłoby posiadanie narzędzia do przeglądania klas po warstwie lub przy-
najmniej raportowanie ich w ten sposób.
699b15244ae879c728b313782703a4d7
6
Rysunek 16.8.
Istnieje wiele możliwych podejść do tego problemu. W przypadku
Model
braku struktury dużej skali jednym z atrakcyjnych rozwiązań byłoby odda- zrestrukturyzowany
nie odpowiedzialności za wdrażanie tych reguł trasowania do obiektu, który i zrefaktoryzowany
zawiera specyfikację trasy (Route Specification) oraz kod materiału nie-
bezpiecznego (Hazardous Material, w skrócie HazMat), czyli obiektu
ładunku (Cargo).
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
699b15244ae879c728b313782703a4d7
6
Rysunek 16.10.
ładnie do odpowiedzialności warstwy wsparcia decyzji. Problemem nato-
miast jest zależność ładunku (Cargo), będącego obiektem operacyjnym,
od usługi polityki trasowania materiałów niebezpiecznych (HazMat
Route Policy Service), będącej obiektem wsparcia decyzji. Dopóki pro-
jekt przestrzega tych warstw, na taki model nie może być zgody. Spowo-
dowałby on bowiem zamieszanie wśród programistów oczekujących
przestrzegania struktury.
Zawsze istnieje wiele możliwości projektowych, z których zwy-
czajnie musimy wybrać tę, która przestrzega reguł struktury dużej skali.
Usługa HazMat Route Policy Service jest w porządku, jednak musimy
przenieść odpowiedzialność za używanie tej polityki. Spróbujmy zatem
obiektowi Router nadać odpowiedzialność za zbieranie odpowiednich
polityk przed wyszukaniem trasy. Oznaczać to będzie zmianę interfejsu
tej klasy w celu dołączenia obiektów, od których mogą zależeć polityki.
Rysunek 16.11 przedstawia przykładowy projekt.
Typowa interakcja jest przedstawiona na rysunku 16.12.
Ten projekt nie jest koniecznie lepszy od innych. Wszystkie one
mają wady i zalety. Jeżeli jednak wszyscy w projekcie podejmowaliby
decyzje w spójny sposób, wtedy projekt jako całość byłby znacznie bar-
dziej zrozumiały, a to samo w sobie warte jest pewnych kompromisów
w szczegółowych wyborach projektowych.
699b15244ae879c728b313782703a4d7
6
Rysunek 16.11.
Jeśli struktura narzuca wiele dziwnych wyborów projektowych, być Projekt spójny
może powinna zostać jeszcze raz sprawdzona i być może zmodyfikowana z warstwami
lub nawet odrzucona, w zgodzie z PORZĄDKIEM EWOLUCYJNYM.
699b15244ae879c728b313782703a4d7
6
Rysunek 16.12.
jest krytyczny, chociaż błędne decyzje dodają pracy. Struktura może
równie dobrze przekształcić się w coś zupełnie nierozpoznawalnego.
Dlatego też wszystkie wskazówki zasugerowane w tym rozdziale powinny
być stosowane niezależnie od tego, czy struktura jest przekształcana, czy
tworzona od nowa.
W trakcie wyłączania, łączenia, podziału i zmiany definicji warstw
przydatne są pewne charakterystyki, których należy poszukiwać i prze-
strzegać:
Opowiadanie historii. Warstwy powinny komunikować podstawowe
realia lub priorytety dziedziny. Wybór struktury dużej skali jest mniej
kwestią techniczną niż decyzją wpływającą na modelowanie biznesu.
Warstwy powinny wydobywać priorytety biznesu.
Zależność pojęciowa. Pojęcia w „wyższej” klasie powinny mieć znaczenie
wynikające z „niższych” warstw, natomiast pojęcia z warstw niż-
szych powinny zachowywać znaczenie, nawet gdy są rozpatrywane
samodzielnie.
699b15244ae879c728b313782703a4d7
6
ZARYSY KONCEPCYJNE. Jeżeli obiekty z różnych warstw
powinny charakteryzować się różną częstotliwością lub źródłem
zmian, warstwa powinna je odcinać.
Nie zawsze należy rozpoczynać, od początku definiując warstwy
dla każdego nowego modelu. Pewne warstwy pojawiają się w całych
rodzinach powiązanych dziedzin.
Na przykład w biznesach opartych na wykorzystywaniu dużych
zasobów kapitałowych, jak chociażby fabrykach lub statkach transporto-
wych, oprogramowanie logistyczne mogłoby być często zorganizowane
w warstwę „potencjału” oraz warstwę „operacji”.
Potencjał. Co może zostać zrobione? Nie jest ważne, co planujemy
zrobić, lecz co moglibyśmy zrobić. Zasoby organizacji, włączając jej
pracowników oraz sposób organizacji tych zasobów, stanowią rdzeń
warstwy potencjału. Definiują go również kontrakty z innymi
dostawcami. Ta warstwa może zostać wyróżniona w prawie każdej
dziedzinie biznesowej i jest zasadniczym elementem historii dzia-
łalności w rodzaju usług transportowych lub produkcji, posiadających
relatywnie duże stałe inwestycje kapitałowe, umożliwiające prowa-
dzenie biznesu. Potencjał obejmuje również przejściowe aktywa,
jednakże biznes, który jest sterowany głownie nimi, mógłby wybrać
inne warstwy wzmacniające ten przekaz. Omówimy to później.
Operacje. Co jest wykonywane? Co zrobiliśmy, aby wykorzystać ten
potencjał? Podobnie jak w warstwie potencjału, ta warstwa powinna
odzwierciedlać rzeczywistą sytuację, a nie nasze oczekiwania. W tej
warstwie próbujemy dostrzec nasze własne wysiłki i czynności.
Chcemy zobaczyć, co sprzedajemy, a nie co umożliwia nam sprze-
daż. Bardzo często obiekty operacyjne odwołują się do obiektów
potencjału lub nawet są z nich złożone. Jednak obiekt potencjału
nie powinien odwoływać się do warstwy operacji.
W licznych systemach obejmujących wiele różnych dziedzin tego
rodzaju (być może nawet w większości) te dwie warstwy pokrywają
wszystko (chociaż mógłby wystąpić również zupełnie inny, bardziej odpo-
wiedni moment przełomowy). Śledzą one obecną sytuację oraz aktywne
plany operacyjne i tworzą odpowiednie raporty lub dokumenty. Jednak
śledzenie nie jest niekiedy wystarczające. W sytuacji, gdy projekty poszu-
kują wskazówek lub asysty dla użytkowników lub też automatyzują podej-
mowanie decyzji, istnieje dodatkowy zestaw odpowiedzialności, który może
zostać zorganizowany w jeszcze jedną warstwę ponad operacjami.
699b15244ae879c728b313782703a4d7
6
Wsparcie decyzji. Jakie należy podjąć akcje lub jaką ustanowić politykę?
Ta warstwa służy do analizy oraz podejmowania decyzji. Opiera
ona swoje analizy na informacjach z niższych warstw, w rodzaju
potencjału lub operacji. Oprogramowanie wspierające decyzję może
używać informacji historycznych, aby aktywnie poszukiwać moż-
liwości dla obecnych lub przyszłych operacji.
Systemy wsparcia decyzji posiadają zależności pojęciowe od innych
warstw, w rodzaju operacji lub potencjału, ponieważ decyzje nie są podej-
mowane w próżni. Dużo projektów implementuje wsparcie decyzji przy
użyciu technologii hurtowni danych. Warstwa ta stała się oddzielnym
KONTEKSTEM ZWIĄZANYM w relacji KLIENT – DOSTAWCA
w oprogramowaniu operacyjnym. W innych projektach jest ona głęboko
zintegrowana, jak w poprzednim rozszerzonym przykładzie. Jedną z nieod-
łącznych zalet warstw jest fakt, iż niższe warstwy mogą istnieć bez wyższych.
Może to ułatwić stopniowe wprowadzanie lub rozszerzanie wyższych
warstw zbudowanych na podstawie starszych systemów operacyjnych.
Innym przykładem jest oprogramowanie wymuszające rozbudo-
wane reguły biznesowe lub wymagania prawne, które mogą składać się na
WARSTWĘ ODPOWIEDZIALNOŚCI.
Polityka. Jakie istnieją reguły i cele? Reguły oraz cele są głównie
pasywne, jednak ograniczają zachowanie w innych warstwach.
Projektowanie tych interakcji może być subtelne. Niekiedy polityka
jest przekazywana jako argument do metod niższej warstwy. Innym
razem stosowany jest wzorzec STRATEGII. Polityka działa dobrze
w połączeniu z warstwą wsparcia decyzji, udostępniającą środki poszu-
kiwania celów określonych przez politykę, ograniczoną regułami okre-
ślonymi w polityce.
Warstwy polityki mogą być napisane w tym samym języku co inne,
jednak niekiedy są one implementowane przy użyciu silników reguł. Nie
musi to od razu oznaczać konieczności umieszczenia ich w oddzielnych
KONTEKSTACH ZWIĄZANYCH. W rzeczywistości trudność koordy-
nacji tak różnych technologii implementacyjnych może być uproszczona
przez pedantyczne stosowanie w nich tego samego modelu. W sytuacji,
gdy reguły są utworzone przy użyciu innego modelu niż obiekty, do
których mają zastosowanie, zwiększa się złożoność lub też ograniczane
są możliwości obiektów w celu umożliwienia zarządzania całością.
Wiele rodzajów działalności nie opiera swoich możliwości na fabryce
i wyposażeniu. W usługach finansowych lub ubezpieczeniach (wymie-
niając tylko dwie dziedziny) potencjał jest w dużym stopniu określony
699b15244ae879c728b313782703a4d7
6
Rysunek 16.13.
bieżącymi operacjami. Potencjał firmy ubezpieczeniowej w zakresie pod- Zależności
jęcia nowego ryzyka poprzez spisanie nowej polityki jest oparty na dywer- pojęciowe oraz
syfikacji bieżącej działalności. Warstwa potencjału prawdopodobnie miejsca podziału
w systemie
zostałaby połączona z operacjami, a w drodze ewolucji zostałby zdefinio-
automatyzacji
wany nowy podział na warstwy.
fabryki
Jednym z obszarów, który w takich sytuacjach bardzo często wychodzi
na wierzch, są zobowiązania do klientów.
Zobowiązania. Co obiecaliśmy? Ta warstwa ma naturę polityki pod
tym względem, że wyraża cele kierujące przyszłymi operacjami. Jed-
nak ma też cechy operacji, biorąc pod uwagę, że zobowiązania pow-
stają i są zmieniane jako element stałych czynności biznesowych.
Warstwy potencjału oraz zobowiązania nie wykluczają się wzajem-
nie. Dziedzina, w której obie te warstwy rzucają się w oczy, dajmy na to
firma transportowa z dużą liczbą negocjowanych usług transportowych,
mogłaby użyć ich obu. Inne warstwy, bardziej właściwe tym dziedzinom,
mogłyby być również przydatne. Zmieniaj rzeczy i eksperymentuj. Najle-
piej jest jednak utrzymywać prosty system podziału na warstwy. Prze-
kroczenie czterech lub pięciu staje się niepraktyczne. Dysponowanie zbyt
wieloma warstwami nie jest aż tak wydajne jak opowiadanie historii, a pro-
blemy związane ze złożonością, które miały być rozwiązane przez struktury
dużej skali, powrócą w nowej postaci. Struktura dużej skali musi być bardzo
mocno destylowana.
699b15244ae879c728b313782703a4d7
6
Rysunek 16.14.
Zależności Chociaż tych pięć warstw może być stosowanych do szeregu syste-
pojęciowe oraz mów klasy Enterprise, to jednak nie wychwytuje istotnych odpowie-
miejsca podziału dzialności wszystkich dziedzin. W innych przypadkach dostosowywanie
w systemie projektu na siłę do przedstawionego powyżej kształtu mogłoby zmniej-
bankowości szyć produktywność. Może jednak istnieć naturalny zestaw WARSTW
inwestycyjnej
ODPOWIEDZIALNOŚCI, który w takiej sytuacji działałby świetnie.
W przypadku dziedzin zupełnie niezwiązanych z tymi omówionymi przed
chwilą utworzone warstwy być może musiałyby być zupełnie inne. Osta-
tecznie to Ty musisz użyć swojej intuicji, rozpocząć w pewnym miejscu
i zezwolić na UPORZĄDKOWANIE EWOLUCYJNE.
699b15244ae879c728b313782703a4d7
6
POZIOM WIEDZY
699b15244ae879c728b313782703a4d7
6
jakie aplikacja miała założone na początku, uległoby zmianie, ponieważ
cała semantyka została zmieniona przez użytkowników. Utworzyliby
oni całe obejścia pierwotnej logiki lub nawet wyłączyliby pewne funk-
cjonalności aplikacji wyższego poziomu. Musieliby także nauczyć się
skomplikowanych mapowań pomiędzy ich zadaniami w pracy a sposobem
działania programu. W ten sposób program nigdy nie służyłby popraw-
nie użytkownikom.
Gdyby system musiał być zmieniony lub zastąpiony, programiści
odkryliby (wcześniej lub później), że znaczenia funkcjonalności były inne
od tych, którymi się wydawały. Mogłyby one oznaczać zupełnie inne
rzeczy w różnych społecznościach użytkowników lub w różnych sytu-
acjach. Zmiana czegokolwiek bez zniszczenia tych dodatkowych przy-
padków użycia byłaby bardzo żmudna. Migracja danych do lepiej dosto-
sowanego systemu wymagałaby zrozumienia i zakodowania wszystkich
tych odstępstw.
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ń
699b15244ae879c728b313782703a4d7
6
Rysunek 16.16.
Niektórzy
pracownicy
zaprezentowani
przy użyciu
starego modelu
Rysunek 16.17.
Zaproponowany
model. Teraz zawiera
on zbyt mało
ograniczeń
699b15244ae879c728b313782703a4d7
6
Rysunek 16.18.
Pracownicy mogą
być przypisywani
do złych planów
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).
699b15244ae879c728b313782703a4d7
6
Rysunek 16.20.
Każdy rodzaj
pracownika
(Employee Type)
Taki model będzie spełniać wymagania. Programiści wyczuwają jest przypisany
jedno lub dwa ukryte pojęcia, jednak w tej sytuacji będzie to jedynie ich do planu
dręczyć. Nie mają żadnych solidnych pomysłów, jak ten problem rozwiązać, emerytalnego
więc kończą na chwilę pracę. (Retirement Plan)
699b15244ae879c728b313782703a4d7
6
POZIOM WIEDZY jest zastosowaniem w warstwie dziedziny wzorca
REFLEKSJI (ang. REFLECTION), używanego w wielu architekturach
programistycznych oraz technicznych, jak zostało to dobrze opisane
w książce Buschmanna z 1996 r. REFLEKSJA uwzględnia zmieniające
się potrzeby poprzez sprawienie, że program staje się „świadomy sie-
bie samego”, a także dostosowanie wybranych aspektów jej struktury
i zachowania do ciągłej adaptacji i zmiany. Dzieje się tak dzięki podzia-
łowi programu na „poziom podstawowy”, odpowiedzialny za operacyjną
odpowiedzialność aplikacji, oraz „metapoziom”, reprezentujący wiedzę
o strukturze i zachowaniu programu.
Co znamienne, ten wzorzec nie jest zwany „warstwą” wiedzy.
Chociaż przypomina on podział na warstwy, to jednak REFLEKSJA
zakłada wzajemne zależności w obu kierunkach.
Java ma minimalną wbudowaną REFLEKSJĘ w postaci protoko-
łów służących do odpytywania klasy o jej metody itp. Taki mechanizm
pozwala programowi zadać pytania o swój własny projekt. CORBA dys-
ponuje nieco bardziej rozbudowanymi, lecz podobnymi protokołami
REFLEKSJI. Niektóre technologie służące do obsługi trwałości danych
rozszerzają bogactwo metod samoopisowych poprzez obsługę częściowo
zautomatyzowanych mapowań pomiędzy tabelami bazodanowymi a obiek-
tami. Istnieją również inne techniczne przykłady. Ten wzorzec może być
również zastosowany wewnątrz warstwy dziedziny.
3
POSA jest skrótem od architektury programów sterowanej wzorcami
(ang. Pattern Oriented Software Architecture) z książki Buschmanna z 1996 r.
699b15244ae879c728b313782703a4d7
6
być bardziej przydatny od bardzo ogólnego szkieletu aplikacji. POZIOM
WIEDZY jest prostszy i może komunikować określony zamiar projek-
tanta.
Dlatego:
Utwórz oddzielny zestaw obiektów, który mógłby być uży-
wany do opisu i ograniczenia struktury oraz zachowania modelu
podstawowego. Utrzymuj te dwa pojęcia w postaci oddzielonej
na dwóch „poziomach”, jednym bardzo konkretnym, a drugim
odzwierciedlającym reguły oraz wiedzę, które użytkownik zwy-
kły lub specjalny ma możliwość zindywidualizować.
Podobnie jak wszystkie inne pomysły o wielu możliwościach,
REFLEKSJA oraz POZIOMY WIEDZY mogą być odurzające. Sam wzo-
rzec powinien być używany rozsądnie. Może on zmniejszyć złożoność
poprzez uwolnienie obiektów operacyjnych od konieczności bycia specja-
listami od wszystkich dziedzin, jednak wprowadzany przezeń pośredni
poziom może z powrotem nieco zagmatwać sytuację. Jeżeli POZIOM
WIEDZY stanie się złożony, wtedy zachowanie systemu również stanie
się trudne do zrozumienia zarówno przez programistów, jak i użytkow-
ników. Użytkownicy zwykli (lub specjalni) wykonujący konfigurację będą
potrzebować umiejętności programistycznych, a nawet będą musieli być
programistami metapoziomu. Jeżeli popełnią błędy, aplikacja będzie zacho-
wywać się niepoprawnie.
Nie znikną również całkowicie podstawowe problemy związane
z migracją danych. W sytuacji, gdy zmieniona zostanie struktura
POZIOMU WIEDZY, istniejące obiekty na poziomie operacyjnym będą
musiały sobie z tym poradzić. Być może będzie możliwość wspólnego
istnienia obiektów nowych ze starymi, jednak tak czy inaczej, wymagana
będzie dogłębna analiza.
Wszystkie te problemy nakładają wiele obowiązków na projektanta
POZIOMU WIEDZY. Projekt musi być na tyle silny, aby obsługiwać
nie tylko scenariusze zaprezentowane podczas tworzenia systemu, lecz
także dowolny scenariusz, do którego użytkownik mógłby skonfiguro-
wać system w przyszłości. Zastosowane w sposób roztropny w miej-
scach, w których konfiguracja jest niezbędna, gdyż w innym przypadku
zaburzyłaby projekt, POZIOMY WIEDZY mogą rozwiązać problemy,
które w innym przypadku bardzo trudno byłoby obsłużyć.
699b15244ae879c728b313782703a4d7
6
Przykład
System do wypłat pracowniczych, część 2.
— POZIOM WIEDZY
Członkowie naszego zespołu powrócili już do pracy, odświeżeni nocną
drzemką. Jeden z nich zaczął się przyglądać jednemu z dziwnym miejsc.
Czemu niektóre obiekty są zabezpieczone, a inne mogą być swobodnie
zmieniane? Zestaw ograniczonych obiektów przypomniał mu o wzorcu
POZIOMU WIEDZY, więc zdecydował się na jego wypróbowanie.
Ostatecznie odkrył, że istniejący model może być rozpatrywany właśnie
w taki sposób.
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.
699b15244ae879c728b313782703a4d7
6
Jednak nie było to rzeczywiście stwierdzenie występujące w JĘZYKU
WSZECHOBECNYM. W modelu przecież nie istniała „lista płac”. Dys-
kusja przebiegała raczej przy użyciu języka oczekiwanego niż posiadanego.
Pojęcie listy płac było niejawne w używanym modelu, pogrążone razem
z rodzajem użytkownika. Dopóki nie został wydzielony POZIOM WIE-
DZY, nie było to wszystko tak oczywiste. Po tym fakcie wszystkie elementy
z kluczowego zdania pojawiły się na tym samym poziomie... z wyjąt-
kiem jednego.
Bazując na tym spostrzeżeniu, programistka jeszcze raz zrefaktory-
zowała model wspierający wcześniejsze stwierdzenie.
Potrzeba umożliwienia użytkownikowi kontroli reguł dla skojarzo-
nych z nimi obiektów skłoniła zespół do stworzenia modelu dysponują-
cego niejawnym POZIOMEM WIEDZY.
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ć.
699b15244ae879c728b313782703a4d7
6
Rysunek 16.23.
***
Każdy rodzaj
pracownika Na pierwszy rzut oka POZIOM WIEDZY przypomina specjalny przypa-
(Employee Type) dek WARSTW ODPOWIEDZIALNOŚCI, a szczególnie warstwę „poli-
posiada teraz plan tyki”, jednak jest to mylące. W POZIOMIE WIEDZY zależności wystę-
emerytalny pują pomiędzy poziomami, natomiast w WARSTWACH niższe warstwy
(Retirement Plan)
są niezależne od wyższych.
oraz listę płac
W rzeczywistości POZIOM WIEDZY może istnieć wspólnie z więk-
(Payroll)
szością innych struktur dużej skali, wprowadzając do organizacji dodat-
kowy wymiar.
699b15244ae879c728b313782703a4d7
6
SZKIELET KOMPONENTÓW
DOŁĄCZANYCH
W bardzo dojrzałym modelu, który jest odpowiednio głęboki oraz pod-
legał destylacji, pojawiają się różne możliwości. SZKIELET KOMPO-
NENTÓW DOŁĄCZANYCH wchodzi w grę zazwyczaj już po opraco-
waniu w tej samej dziedzinie kilku aplikacji.
***
W sytuacji, gdy musi ze sobą współdziałać szereg różnych
aplikacji, z których każda oparta jest na tej samej abstrakcji, lecz
została niezależnie zaprojektowana, tłumaczenia pomiędzy wie-
loma KONTEKSTAMI ZWIĄZANYMI ograniczają integrację.
JĄDRA WSPÓŁDZIELONEGO nie da się zastosować w zespołach,
które nie pracują ze sobą blisko. Duplikacja oraz fragmentacja pod-
noszą koszt tworzenia oprogramowania oraz instalacji, zaś wza-
jemne współdziałanie aplikacji ze sobą staje się bardzo utrudnione.
Niektóre pomyślne projekty są podzielone na różne komponenty,
z których każdy ma odpowiedzialność za określone kategorie funkcji.
Wszystkie komponenty podłączają się zazwyczaj do centralnego węzła,
który obsługuje wszystkie potrzebne protokoły i wie, w jaki sposób komu-
nikować się z odpowiednimi interfejsami. Możliwe jest również użycie
innych wzorców łączenia ze sobą komponentów. Projekty tych interfej-
sów oraz łączącego ich węzła muszą być ze sobą skoordynowane. Więcej
swobody możliwe jest jedynie w trakcie projektowania wnętrza tych
interfejsów.
Omawiany wzorzec jest wspierany przez kilka powszechnie uży-
wanych technicznych szkieletów, jest to jednak zupełnie inny problem.
Szkielet techniczny jest wymagany jedynie w przypadku, gdy rozwiązuje
on zasadniczy problem techniczny, w rodzaju dystrybucji lub też współ-
dzielenia komponentu pomiędzy różne aplikacje. Podstawowy wzorzec
stanowi pojęciowe uporządkowanie odpowiedzialności i jako taki może
być w prosty sposób zastosowany w pojedynczym programie Java.
Dlatego:
Wydestyluj RDZEŃ ABSTRAKCYJNY dla interfejsów oraz
interakcji i utwórz szkielet, który umożliwi dowolne zastępowa-
nie różnych implementacji tych interfejsów. Podobnie umożliw
dowolnej aplikacji korzystanie z tych komponentów, o ile będą
one operować ściśle poprzez interfejsu RDZENIA ABSTRAK-
CYJNEGO.
699b15244ae879c728b313782703a4d7
6
Wysokopoziomowe abstrakcje są identyfikowane oraz uwspólniane
pomiędzy wieloma systemami. Specjalizacja natomiast następuje w MODU-
ŁACH. Centralnym węzłem aplikacji jest RDZEŃ ABSTRAKCYJNY
w JĄDRZE WSPÓŁDZIELONYM. Jednak za zahermetyzowanymi inter-
fejsami komponentów może kryć się wiele KONTEKSTÓW ZWIĄZA-
NYCH. Taka struktura jest szczególnie wygodna w sytuacji, gdy wiele
komponentów pochodzi z wielu różnych źródeł lub też gdy komponenty
hermetyzują istniejące wcześniej oprogramowanie w celu integracji.
Nie mówię jednak, że komponenty muszą mieć rozbieżne modele.
Jeżeli dane zespoły stosują CIĄGŁĄ INTEGRACJĘ lub mogą utworzyć
inne JĄDRO WSPÓŁDZIELONE utrzymywane przez blisko związany
zestaw komponentów, wiele z tych komponentów można utworzyć wew-
nątrz pojedynczego KONTEKSTU. Wszystkie strategie tego rodzaju mogą
w prosty sposób wspólnie istnieć wewnątrz struktury dużej skali wyko-
rzystującej KOMPONENTY DOŁĄCZANE. W niektórych przypad-
kach inną możliwością byłoby użycie JĘZYKA OPUBLIKOWANEGO
dla dołączanego interfejsu węzła.
Istnieje kilka wad SZKIELETU KOMPONENTÓW DOŁĄCZA-
NYCH. Jedną z nich stanowi fakt, iż jest to wzorzec bardzo trudny do
zastosowania. Wymaga on bowiem precyzyjnego projektu interfejsów
oraz wystarczająco głębokiego modelu, mogącego wychwycić niezbędne
zachowanie RDZENIA ABSTRAKCYJNEGO. Koleją dużą wadą jest
ograniczone zastosowanie tego wzorca. Jeżeli aplikacja wymaga innego
podejścia do DZIEDZINY GŁÓWNEJ, struktura ta będzie stanowić
przeszkodę. Programiści mogą specjalizować model, jednak nie mogą zmie-
nić RDZENIA ABSTRAKCYJNEGO bez zmiany protokołu wszystkich
różnorodnych komponentów. W rezultacie proces ciągłego ulepszania
RDZENIA oraz refaktoryzacja ku głębszemu zrozumieniu zostaną mniej
lub bardziej zamrożone.
Dobre spojrzenie na różne ambitne próby zastosowania SZKIE-
LETÓW KOMPONENTÓW DOŁĄCZANYCH w różnych dziedzi-
nach ukazują w swej książce z roku 2000 Fayad oraz Johnson. Omawiają
oni również wzorzec SEMATECH CIM. Sukces tego rodzaju szkieletów
uzależniony jest od wielu czynników. Najprawdopodobniej największą
przeszkodą będzie dojrzałość zrozumienia wymagana do zaprojektowania
przydatnego szkieletu. SZKIELET KOMPONENTÓW DOŁĄCZA-
NYCH nie powinien być pierwszą ani też drugą strukturą dużej skali
zastosowaną w projekcie. Najbardziej pomyślne przykłady zastosowań
następowały po pełnym utworzeniu wielu specjalizowanych aplikacji.
699b15244ae879c728b313782703a4d7
6
Przykład
Szkielet SEMATECH CIM
W fabryce produkującej układy komputerowe grupy (zwane partiami)
krzemowych wafli przesuwane są od jednej maszyny do innej poprzez
setki różnych modyfikujących je etapów, aż proces nadruku na nich
mikroskopijnych obwodów zostanie całkowicie zakończony. Fabryka
potrzebuje oprogramowania, które będzie w stanie śledzić każdą z partii,
zachowywać informacje o każdej jej modyfikacji, a następnie nakazywać
pracownikom fabryki lub automatom przeniesienie wafli do innej maszyny
lub też zastosowanie na nich kolejnego wymaganego procesu. Taki pro-
gram nazywa się systemem realizacji produkcji (ang. manufacturing execution
system, w skrócie MES).
W systemie tym wykorzystywane są setki maszyn od szeregu dostaw-
ców. Każdy etap procesu ma też dokładnie dostosowane instrukcje. Utwo-
rzenie aplikacji MES, która mogłaby poradzić sobie z takimi złożonymi
kombinacjami, było czynnością zniechęcającą i zaporowo kosztowną.
W odpowiedzi konsorcjum przemysłowe SEMATECH utworzył szkie-
let CIM.
System ten jest rozległy i złożony, a także ma szereg różnych
aspektów, z których istotne w tym miejscu będą dwa. Po pierwsze, szkie-
let definiuje abstrakcyjne interfejsy dla podstawowych pojęć z dziedziny
realizacji produkcji półprzewodników, czyli DZIEDZINĘ GŁÓWNĄ
w postaci RDZENIA ABSTRAKCYJNEGO. Te definicje interfejsów
zawierają zarówno zachowanie, jak i semantykę.
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
699b15244ae879c728b313782703a4d7
6
sterujący urządzeniem powinien bez problemu podłączyć się do dowol-
nej aplikacji opartej na szkielecie CIM.
Po zdefiniowaniu tych interfejsów SEMATECH określił reguły
definiujące sposób interakcji z aplikacją. Każda aplikacja oparta na szkie-
lecie CIM powinna implementować protokół obejmujący obiekty zgodne
z pewnym podzbiorem tych interfejsów. Jeżeli protokół został zaimple-
mentowany, a aplikacja w ścisły sposób używała abstrakcyjnych inter-
fejsów, mogła liczyć na obiecane działanie tych interfejsów niezależnie
od implementacji. Połączenie interfejsów z protokołem służącym do ich
używania składa się na ściśle restrykcyjną strukturę dużej skali.
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.
***
699b15244ae879c728b313782703a4d7
6
W jaki sposób tysiące ludzi może niezależnie pracować w celu utworzenia
narzuty składającej się z ponad 40000 elementów?
Kilka prostych reguł złożyło się na strukturę dużej skali dla narzuty
stanowiącej pomnik dla chorych na AIDS. Szczegóły wykonania pozo-
stawiono już konkretnym wykonawcom. Zauważ, w jaki sposób te reguły
koncentrują się na ogólnej misji (upamiętnienie osób, które umarły na
AIDS). Spójrz również na cechy komponentu czyniące integrację prak-
tyczną oraz zdolność do obsługi narzuty w większych fragmentach (jak
na przykład możliwość jej zginania).
Oto, w jaki sposób utworzyć element narzuty
[Za stroną projektu www.aidsquilt.org]
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.
699b15244ae879c728b313782703a4d7
6
Szablon: Swój projekt naszkicuj ołówkiem na materiale, następnie
użyj pędzla do nałożenia farby do materiałów lub ołówka kopiowego.
Kolaż: Upewnij się, że materiały, które dodasz do elementu, nie
rozerwą materiału (na przykład unikaj szkła oraz cekinów). Unikaj
również nieporęcznych materiałów.
Fotografie: Najlepszym sposobem na dołączenie fotografii lub liter
jest skopiowanie ich na papier kopiujący, a następnie wprasowanie
na materiał złożony w 100% z bawełny i wszycie go do tworzonego
elementu. Możesz również umieścić zdjęcie na czystym fragmen-
cie winylu i wszyć go do materiału (poza środkiem, co zapobiegnie
jego zginaniu).
699b15244ae879c728b313782703a4d7
6
Zazwyczaj taka komunikacja zachodziłaby przy użyciu mechani-
zmu opartego na zdarzeniach. Obiekty warstwy operacyjnej w chwili
zmiany ich stanu tworzyłyby odpowiednie zdarzenia. Obiekty warstwy
polityki nasłuchiwałyby konkretnych zdarzeń z niższych warstw. W przy-
padku pojawienia się zdarzenia naruszającego regułę reguła ta wykony-
wałaby pewne działanie (będące częścią jej definicji), które przekazywałoby
właściwą odpowiedź lub też mogłoby utworzyć zdarzenie, z którego sko-
rzystałaby warstwa wyższa.
W przykładzie związanym z bankowością podczas zmiany wartości
aktywów (operacje) przenoszone są też wartości segmentów portfela.
W sytuacji, gdy wartości przekroczą limity dystrybucji portfela (polityka),
może zostać zaalarmowany makler, który z kolei może wtedy podjąć decy-
zję o zakupie lub sprzedaży aktywów w celu zrównoważenia bilansu.
O takim zachowaniu moglibyśmy zdecydować dla każdego przy-
padku osobna lub przyjąć spójny wzorzec, którego musieliby przestrze-
gać wszyscy uczestniczący w interakcjach z obiektami określonych warstw.
Bardziej restrykcyjna struktura zwiększa jednolitość, ułatwiając inter-
pretację projektu. Jeżeli tylko struktury są dobrze dopasowane, bardzo
prawdopodobne jest, że reguły skierują programistów ku dobremu pro-
jektowi. Różne elementy zdają się lepiej pasować do siebie nawzajem.
Z drugiej strony, ograniczenia mogą zniszczyć elastyczność, tak
potrzebną programiście. Niektóre ścieżki komunikacyjne pomiędzy KON-
TEKSTAMI ZWIĄZANYMI mogą stać się niepraktyczne, szczególnie
gdy rozciągają się one na różne obszary technologiczne w systemach
heterogenicznych.
Dlatego też będziesz zmuszony walczyć z pokusą utworzenia szkie-
letu ograniczającego implementację struktury dużej skali. Największym
wkładem, jaki ta ostatnia struktura wnosi do projektu, jest spójność poję-
ciowa, umożliwiająca lepsze wejrzenie do dziedziny. Każda reguła struk-
turalna powinna ułatwiać programowanie.
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
699b15244ae879c728b313782703a4d7
6
dogłębnego zrozumienia dziedziny oraz istoty problemu. A praktycznym
sposobem na takie zrozumienie jest iteracyjny proces tworzenia opro-
gramowania.
Zespół podejmujący się PORZĄDKU EWOLUCYJNEGO musi
odważnie analizować strukturę dużej skali w całym cyklu życia projektu.
Nie powinien on brać na barki struktury stworzonej na początku pro-
jektu, gdy nikt jeszcze nie zna dobrze dziedziny lub wymagań.
Niestety, taka ewolucja oznacza, że końcowa struktura nie będzie
dostępna od początku i konieczna będzie refaktoryzacja w trakcie trwa-
nia projektu. Wszystko to może być kosztowne i trudne do przeprowa-
dzenia, jednak może okazać się niezbędne. Istnieje kilka ogólnych sposo-
bów kontroli kosztów oraz maksymalizacji zysku.
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.
699b15244ae879c728b313782703a4d7
6
Dodatkowo struktura zazwyczaj jest abstrakcyjna, co sprawia, że spój-
ność aplikacji może być trudna do utrzymania przez duży zespół (lub
wiele zespołów).
Dialogi prowadzone w większości zespołów nie wystarczają do utrzy-
mania spójnej struktury dużej skali w systemie. Krytyczne zatem okazuje
się włączenie ich do JĘZYKA WSZECHOBECNEGO projektu, a także
stałe używanie tego języka przez wszystkie osoby w projekcie.
699b15244ae879c728b313782703a4d7
6
Destylacja zmniejsza obciążenie
Kolejną istotną siłą, która powinna zostać zastosowana do modelu, jest
ciągła destylacja. Zmniejsza ona trudność związaną ze zmianą struktury
na szereg różnych sposobów. Po pierwsze, poprzez usunięcie mechani-
zmów PODDZIEDZIN OGÓLNYCH oraz innej struktury pomocni-
czej z DZIEDZINY GŁÓWNEJ może najzwyczajniej pozostać mniej
rzeczy do zmiany.
Jeżeli jest to możliwe, te wspierające elementy powinny być zdefi-
niowane tak, aby pasowały w prosty sposób do struktury dużej skali. Na
przykład w systemie WARSTW ODPOWIEDZIALNOŚCI PODDZIE-
DZINA OGÓLNA mogłaby zostać zdefiniowana w taki sposób, aby
pasowała do pojedynczej warstwy. W przypadku KOMPONENTÓW
DOŁĄCZANYCH PODDZIEDZINA OGÓLNA mogłaby być całko-
wicie w posiadaniu pojedynczego komponentu lub też pomiędzy zestawem
powiązanych komponentów mogłoby istnieć JĄDRO WSPÓŁDZIE-
LONE. Tego rodzaju elementy pomocnicze mogą wymagać zrefaktory-
zowania w taki sposób, aby odnalazły swoje miejsce w strukturze. Jed-
nak przechodzą one niezależnie do DZIEDZINY GŁÓWNEJ i zdają się
zorientowane znacznie szerzej, co ułatwia cały proces. A co więcej, są one
znacznie mniej krytyczne, co sprawia, że ich ulepszanie jest mniej istotne.
Pryncypia destylacji oraz refaktoryzacji ku głębszemu zrozumieniu
stosują się nawet do samych struktur dużej skali. Na przykład na pod-
stawie pobieżnego zrozumienia dziedziny mogą być wybrane wstępne
warstwy, które będą stopniowo zastępowane przez głębsze abstrakcje
odzwierciedlające fundamentalne odpowiedzialności systemu. Duża przej-
rzystość umożliwia ludziom zajrzenie głębiej do projektu, co jest naszym
głównym celem. Jest to również jednym z narzędzi do osiągnięcia tego
celu, ponieważ umożliwia łatwiejszą i bezpieczniejszą manipulację sys-
temem dużej skali.
699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 17.
Łą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
699b15244ae879c728b313782703a4d7
6
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).
699b15244ae879c728b313782703a4d7
6
Rysunek 17.3.
Przypuśćmy, że chcesz zaadaptować WARSTWY ODPOWIE-
Struktura nałożona
DZIALNOŚCI, jednak masz stary system, którego organizacja jest nie- na relacje
spójna z pożądaną strukturą dużej skali. Czy w takiej sytuacji masz zre- komponentów
zygnować z WARSTW? Nie musisz, jednak powinieneś przyznać staremu różnych
systemowi rzeczywiste miejsce w strukturze. W rzeczywistości taka czyn- KONTEKSTÓW
ność może pomóc w scharakteryzowaniu starego systemu. USŁUGI ZWIĄZANYCH
udostępniane przez stary system mogą w rzeczywistości dotyczyć tylko
kilku WARSTW. Stwierdzenie, że stary system pasuje do określonych
WARSTW ODPOWIEDZIALNOŚCI, zwięźle opisuje kluczowy aspekt
jego zakresu i roli.
Jeżeli funkcjonalności starego podsystemu są używane poprzez
FASADĘ, wtedy możesz zaprojektować każdą z USŁUG oferowanych
przez FASADĘ w taki sposób, aby pasowała do jednej warstwy.
W tym przykładzie wnętrze aplikacji do koordynacji transportów,
będącej w rzeczywistości starym systemem, jest zaprezentowane jako nie-
rozróżnialna całość. Jednak zespół projektowy dysponujący dobrze ustano-
wioną strukturą dużej skali, rozciągającą się na MAPĘ KONTEKSTU,
mógłby w swoim KONTEKŚCIE uporządkować model za pomocą tych
samych znajomych WARSTW.
Oczywiście, ponieważ każdy KONTEKST ZWIĄZANY tworzy wła-
sną przestrzeń nazw, jedna struktura mogłaby zostać użyta do zorgani-
zowania modelu wewnątrz jednego KONTEKSTU, podczas gdy inna była
użyta w KONTEKŚCIE sąsiednim, a jeszcze inna organizowała MAPĘ
KONTEKSTU. Niemniej jednak podążanie zbyt daleko w tym kierunku
może ograniczyć wartość struktury dużej skali jako jednoczącego zestawu
pojęć dla projektu.
699b15244ae879c728b313782703a4d7
6
Rysunek 17.4.
Struktura
umożliwiająca
niektórym
komponentom
przekraczanie
warstw
Rysunek 17.5.
Ta sama struktura
zastosowana
w KONTEKŚCIE
oraz w całej MAPIE
KONTEKSTU
699b15244ae879c728b313782703a4d7
6
W tym samym czasie sama struktura dużej skali może być ważną częścią Rysunek 17.6.
DZIEDZINY GŁÓWNEJ. Na przykład rozróżnienie warstw potencjału, MODUŁY
DZIEDZINY
operacji, polityki oraz wsparcia decyzji destyluje spostrzeżenie podsta-
GŁÓWNEJ
wowe dla problemu biznesowego obsługiwanego przez dany program. To
(pogrubione) oraz
spostrzeżenie jest szczególnie przydatne w przypadku, gdy projekt jest PODDZIEDZINY
utworzony z wielu KONTEKSTÓW ZWIĄZANYCH, co sprawia, że OGÓLNE
obiekty modelu DZIEDZINY GŁÓWNEJ nie mają znaczenia w więk- są objaśniane
szej części projektu. przez warstwy
699b15244ae879c728b313782703a4d7
6
Najpierw oszacowanie
Kiedy zabierasz się za strategiczny projekt, musisz rozpocząć od jasnej
oceny bieżącej sytuacji:
1. Narysuj MAPĘ KONTEKSTU. Czy możesz narysować jedną spójną,
czy też istnieją sytuacje wieloznaczne?
2. Przymierz się do użycia języka w projekcie. Czy istnieje JĘZYK
WSZECHOBECNY? Czy jest na tyle bogaty, aby wspomagać projekt?
3. Zrozum, co jest istotne. Czy DZIEDZINA GŁÓWNA została ziden-
tyfikowana? Czy istnieje OPIS WIZJI DZIEDZINY? Czy możesz
jakiś opis utworzyć?
4. Czy technologia projektowa sprzyja, czy przeszkadza PROJEKTOWI
STEROWANEMU MODELEM?
5. Czy programiści w zespole mają wszystkie potrzebne umiejętności
techniczne?
6. Czy programiści mają wiedzę o dziedzinie? Czy są zainteresowani
dziedziną?
Oczywiście, nie znajdziesz perfekcyjnych odpowiedzi. W tej chwili
wiesz o projekcie znacznie mniej, niż będziesz wiedzieć o nim w przy-
szłości. Jednak te pytania definiują solidny punkt startowy. Zanim otrzy-
masz wstępne odpowiedzi na te pytania, zaczniesz już rozumieć, co musi
być zrobione najszybciej. W miarę upływu czasu będziesz mógł dopra-
cować odpowiedzi, w szczególności MAPĘ KONTEKSTU, OPIS WIZJI
DZIEDZINY i dowolny inny artefakt, który utworzysz, aby w ten spo-
sób odzwierciedlić zmianę sytuacji oraz nowe spostrzeżenia.
699b15244ae879c728b313782703a4d7
6
Na początku przyjrzyjmy się szybko dwóm stylom, które — jak
zauważyłem — prezentują w praktyce pewną wartość (w ten sposób
ignorując stary styl, polegający na „narzucaniu mądrości”).
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.
699b15244ae879c728b313782703a4d7
6
Zespół architektoniczny
skoncentrowany na kliencie
W przypadku, gdy strategia będzie używana wspólnie przez wiele zespo-
łów, centralizacja podejmowania decyzji może wydawać się atrakcyjna.
Niedziałający model architekta wieży z kości słoniowej nie jest jedyną
możliwością. Zespół architektoniczny może funkcjonować jako partner
dla szeregu zespołów aplikacyjnych, pomagając w koordynacji oraz har-
monizacji ich struktur dużej skali, a także granic KONTEKSTÓW
ZWIĄZANYCH, jak również innych problemów technicznych dotyka-
jących wielu zespołów. Aby ich działanie przynosiło korzyści, taki zespół
musi reprezentować podejście podkreślające wagę tworzenia aplikacji.
Na wykresie struktury organizacyjnej zespół ten może przypominać
tradycyjny zespół architektów, jednak w rzeczywistości znacznie się od
niego różni. Członkowie zespołu są prawdziwymi osobami z wiedzą
praktyczną, współdziałają z programistami i odkrywają z nimi wzorce,
eksperymentując z różnymi zespołami w procesie destylacji.
Kilkukrotnie zauważyłem scenariusz, w którym projekt kończył
z głównym architektem wykonującym większość czynności z poniższej listy.
699b15244ae879c728b313782703a4d7
6
Niezależnie od systemu, bądź mniej zainteresowany autorytetem
wynikającym z decyzji kierownictwa niż rzeczywistymi relacjami strategii
z programistami.
699b15244ae879c728b313782703a4d7
6
Zespoły architektów nie mogą wyciągać wszystkich najlepszych
i najbardziej błyskotliwych programistów.
Projektowanie na tym poziomie wymaga złożoności, która prawdopodobnie
nie jest zbyt łatwo osiągalna. Menedżerowie wydają się przenosić najbar-
dziej technicznie utalentowanych programistów do zespołów architektów
oraz zespołów infrastrukturalnych, ponieważ chcą skorzystać z zaawan-
sowanych umiejętności projektowych tych osób. Z ich punktu widzenia
programiści są kuszeni wizją dysponowania szerszym spojrzeniem lub
pracą nad „najbardziej interesującymi problemami”. Z przynależnością do
elitarnego zespołu wiąże się również prestiż.
Tego rodzaju siły bardzo często pozostawiają jedynie średnio uzdol-
nionych programistów, którzy mają w rzeczywistości tworzyć aplikację.
Jednak tworzenie dobrych aplikacji wymaga umiejętności projektowych,
więc taka konstrukcja projektu zmierza do porażki. Nawet jeżeli zespół
strategiczny stworzy dobry projekt, zespół aplikacyjny nie będzie aż tak
zaawansowany, aby go wdrożyć.
Przeciwnie, tego rodzaju zespoły prawie nigdy nie mają programisty,
który być może ma słabsze umiejętności tworzenia projektów, lecz dys-
ponuje największą wiedzą w danej dziedzinie. Projekt strategiczny nie jest
zadaniem czysto technicznym. Odcięcie się od programistów o głębokiej
wiedzy dziedzinowej jeszcze bardziej komplikuje wysiłki architektów.
Potrzebni są również eksperci dziedzinowi.
Konieczne jest dysponowanie silnymi projektantami we wszystkich
zespołach aplikacyjnych. Równie krytyczne jest posiadanie wiedzy dzie-
dzinowej w każdym zespole zajmującym się projektem strategicznym.
Być może wymagane będzie zatrudnienie większej liczby zaawansowanych
projektantów. Pomóc może również wykonywanie pracy przez zespoły
architektów jedynie na połowę etatu. Jestem przekonany, że istnieje wiele
różnych sposobów. Najważniejsze jest, aby wydajny zespół strategiczny
dysponował partnerem w postaci wydajnego zespołu aplikacyjnego.
699b15244ae879c728b313782703a4d7
6
do innego i w efekcie skończyliśmy z nadmiernie rozbudowaną archi-
tekturą, uniemożliwiającą pracę.
W zamian musieliśmy zdyscyplinować się do utworzenia pryncypiów
organizujących oraz głównych modeli, które nie mogą zawierać niczego,
co nie mogłoby w znacznym stopniu polepszyć przejrzystość projektu.
Mówiąc szczerze, wszystko wchodzi czemuś w drogę, dlatego lepiej, aby
każdy element miał swoje uzasadnienie. Uświadomienie sobie, że Twój
najlepszy pomysł może komuś przeszkadzać, wymaga pokory.
699b15244ae879c728b313782703a4d7
6
może wystąpić nawet wtedy, gdy projektanci szkieletu nie mają zamiaru
wchodzić w warstwy dziedziny lub aplikacji.
Te same siły, które ograniczają wady projektu strategicznego, mogą
pomóc w przypadku architektur technicznych. Ewolucja, minimalizm oraz
zaangażowanie w prace zespołu tworzącego aplikację mogą prowadzić do
stale ulepszanego zestawu usług oraz reguł, które pomagają zespołowi
tworzącemu aplikację, nie wchodząc mu w drogę. Architektury, które nie
podążają tą drogą, zmniejszą kreatywność zespołu tworzącego aplikację
albo będą marginalizowane lub obchodzone, co sprawi, że proces two-
rzenia aplikacji nie będzie dysponować żadną architekturą.
Istnieje szczególnie jedna przypadłość, która zdecydowanie znisz-
czy dowolny szkielet aplikacji.
699b15244ae879c728b313782703a4d7
6
Bez pewnego rodzaju procesu nie ma żadnej możliwości, aby uniwer-
sytet w Oregonie kiedykolwiek osiągnął tak zharmonizowane i głębokie
uporządkowanie, jak to leżące u podstaw uniwersytetu w Cambridge.
699b15244ae879c728b313782703a4d7
6
544 ROZDZIAŁ 17. ŁĄCZENIE STRATEGII
699b15244ae879c728b313782703a4d7
6
Zakończenie
699b15244ae879c728b313782703a4d7
6
Epilog
Praca przy nowatorskich projektach oraz eksperymentowanie z ciekawymi
pomysłami i narzędziami są bardzo satysfakcjonujące. Jednak są one dla
mnie nic niewarte, jeśli utworzone oprogramowanie nie jest później
wykorzystane w produkcji. Tak naprawdę prawdziwą miarą sukcesu jest to,
na ile dobrze oprogramowanie służy przez dłuższy czas. Osobiście miałem
możliwość śledzenia przez lata historii moich poprzednich projektów.
Przytoczę tutaj pięć takich projektów. W każdym z nich podjęta
została poważna próba zastosowania projektowania sterowanego dziedziną,
oczywiście nie w sposób tak usystematyzowany i bezpośrednio nazwany,
jak miało to miejsce w tej książce. Wszystkie te projekty skutkowały dostar-
czeniem oprogramowania: w niektórych udało się zrealizować i wykonać
projekt sterowany modelem, natomiast w innych nastąpiło odejście od tego
sposobu. Niektóre aplikacje stale się rozwijały i zmieniały przez wiele
lat, inne zatrzymały się w miejscu, a jeszcze inne umarły we wczesnym
stadium rozwoju.
Projekt mający na celu stworzenie oprogramowania do projektowania
obwodów drukowanych opisany w rozdziale 1. odniósł ogromny sukces
wśród testowych użytkowników dziedzinowych. Niestety, młoda firma,
która zainicjowała projekt, kompletnie nie doceniła kwestii reklamy
i w rezultacie została zamknięta. Oprogramowanie jest teraz wykorzy-
stywane przez garstkę inżynierów projektujących obwody, którzy posiadali
stare kopie wersji beta programu. Podobnie jak inne osierocone programy,
będzie on działać, dopóki w systemach, z którymi się integruje, nie zostaną
wprowadzone fatalne w skutkach zmiany.
546 ZAKOŃCZENIE
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
Ż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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
DODATEK
Wykorzystanie
szablonów
z tej książki
699b15244ae879c728b313782703a4d7
6
Bardzo lubiłem ten samochód, jednak nigdy więcej nie zdecyduję się
na posiadanie ekscentrycznego auta. Nadszedł w końcu dzień, w którym
w moim samochodzie zdiagnozowano szczególnie kosztowny problem,
i od tego czasu miałem dość samochodów marki Peugeot. Zawiozłem
moje auto do lokalnego oddziału firmy, która przyjmowała samochody
na cele dobroczynne. Następnie za kwotę równą prawie oszacowanej cenie
naprawy kupiłem starą hondę civic.
W przypadku rozwoju dziedziny brak jest standardowych elemen-
tów projektowych. Dlatego też każdy model dziedziny i odpowiadająca
mu implementacja są dziwne i trudne do zrozumienia. Co więcej, każdy
zespół musi poniekąd na nowo wynajdować koło (lub też skrzynię bie-
gów albo wycieraczkę). W świecie projektów obiektowych wszystko jest
obiektem, odwołaniem lub komunikatem, co oczywiście samo w sobie
stanowi przydatną abstrakcję. Nie ogranicza to jednak wystarczająco zakresu
wyborów dotyczących projektowania dziedziny i nie umożliwia ekono-
micznej dyskusji o modelu dziedziny.
Ograniczenie się do stwierdzenia, że „wszystko jest obiektem”, przy-
pominałoby cieślę lub architekta mówiącego, że w domu „wszystko jest
pokojem”. Przecież istniałby w nim duży pokój z gniazdkami o wysokim
napięciu oraz zlewem, w którym to pokoju mógłbyś gotować. Podobnie
byłby na górze mały pokój, w którym mógłbyś spać. Opis zwykłego
domu zająłby wiele stron. Osoby zajmujące się budową lub jedynie użyt-
kujące dom zauważają, że w przypadku pokojów istnieją zwykłe wzorce
o specjalnych nazwach, w rodzaju „kuchnia”. Taki język umożliwia ekono-
miczne dyskutowanie o projekcie domu.
Co więcej, nie wszystkie kombinacje funkcji wydają się praktyczne.
Dlaczego nie istnieje pokój, w którym można się kąpać i spać? Czy to
nie byłoby wygodne? Z długotrwałego doświadczenia wynika zwyczaj,
dzięki któremu oddzielamy „sypialnie” od „łazienek”. W końcu „łazienki”
wydaje się używać większa liczba osób niż sypialnie, a te ostatnie wymagają
maksymalnej prywatności nawet dla tych osób, które użytkują sypialnie
wspólnie. Natomiast łazienki mają specjalizowane oraz drogie wymaga-
nia co do infrastruktury. Wanny wraz z toaletami bardzo często umiesz-
czane są w tym samym pokoju, ponieważ obie te rzeczy wymagają tej
samej infrastruktury (wody oraz odpływu) i obie są używane prywatnie.
Kolejnym pokojem wymagającym specjalnej infrastruktury jest
pomieszczenie nazywane często „kuchnią”, w którym możesz przygoto-
wywać posiłki. W odróżnieniu od łazienki kuchnia nie posiada specjalnych
wymagań co do prywatności. Ponieważ jej wyposażenie jest kosztowne,
nawet zdecydowanie duży dom posiada zazwyczaj jedną kuchnię. Takie
699b15244ae879c728b313782703a4d7
6
podejście korzysta również z naszych przyzwyczajeń związanych ze wspól-
nym przygotowywaniem i spożywaniem posiłków.
Jeżeli stwierdzę, że chcę mieć dom z trzema sypialniami, dwiema
łazienkami i otwartą kuchnią, umieszczę w takim krótkim zdaniu olbrzymią
ilość informacji, a także uniknę wielu pomyłek, jak chociażby umieszczenia
toalety obok lodówki.
W każdym obszarze projektu — domach, samochodach, łodziach lub
oprogramowaniu — staramy się używać wzorców, które zostały spraw-
dzone w działaniu w przeszłości, improwizując jedynie w zdefiniowanych
profilach. Niekiedy musimy wynaleźć coś zupełnie nowego. Jednak opie-
rając standardowe elementy na wzorcach, unikamy marnowania energii
na problemy ze znanymi rozwiązaniami, dzięki czemu możemy skoncen-
trować się na nadzwyczajnych potrzebach. Budowanie przy użyciu stan-
dardowych wzorców pomaga nam uniknąć projektu tak indywidualnego,
że byłby bardzo trudny do zakomunikowania.
Chociaż projektowanie dziedziny oprogramowania nie jest tak doj-
rzałe, jak inne obszary projektowania (a niekiedy może być aż tak odmienne
od innych dziedzin, że nie będzie można w nim użyć wzorców tak spe-
cyficznych, jak te dla części samochodowych lub pokojów), to jednak
niezależnie istnieje potrzeba wykroczenia poza stwierdzenie, że „wszystko
jest obiektem”. Przynajmniej w takim stopniu, by odróżniać śruby od
sprężyn.
Pewna postać współdzielenia i standaryzacji spostrzeżeń projektowych
została zapoczątkowana w latach 70. przez grupę architektów kierowa-
nych przez Krzysztofa Alexandra (współautora książki wydanej w roku
1977). Ich „język wzorców” połączył wypróbowane i prawdziwe rozwią-
zania projektowe dla powszechnych problemów (zdecydowanie bardziej
subtelnie niż w moim przykładzie „kuchni”, który prawdopodobnie
przyprawił o dreszcze niektórych czytelników książek Alexandra). Celem
książki było, aby budowniczowie oraz użytkownicy mogli komunikować
się w tym języku, a także mogli być prowadzeni przez wzorce w celu
utworzenia pięknych budynków, które poprawnie funkcjonują i są lubiane
przez używających ich ludzi.
Obojętnie, o jakim pomyśle mogli myśleć architekci, ten język wzor-
ców miał duży wpływ na projektowanie oprogramowania. W latach 90.
wzorce programistyczne były stosowane z pewnym sukcesem na wiele
sposobów. Do najbardziej zauważalnych przykładów należy szczegółowe
projektowanie (książka Gammy i innych z roku 1995), a także architektury
techniczne (Buschman i inni z roku 1996). Ostatnio wzorce są używane
w celu dokumentowania podstawowych technik projektowania obiek-
towego (książka Larmana z roku 1998) oraz architektur przemysłowych
699b15244ae879c728b313782703a4d7
6
(książka Fowlera z roku 2003 oraz Alura i innych z roku 2001). Język
wzorców jest obecnie główną techniką porządkowania pomysłów w dzie-
dzinie projektowania oprogramowania.
699b15244ae879c728b313782703a4d7
6
NAZWA WZORCA
[Ilustracja pomysłu.
Niekiedy wizualna metafora lub sugestywny opis].
699b15244ae879c728b313782703a4d7
6
558 DODATEK WYKORZYSTANIE SZABLONÓW Z TEJ KSIĄŻKI
699b15244ae879c728b313782703a4d7
6
Słownik
Poniżej znajduje się krótkie podsumowanie wybranych definicji, stan-
dardów nazewnictwa oraz innych pojęć użytych w tej książce.
AGREGAT (ang. aggregate) — grupa skojarzonych ze sobą obiektów,
traktowanych podczas zmian danych jako pojedynczy element.
Zewnętrzne odwołania do tej grupy ograniczone są tylko do jednego
elementu AGREGATU, wskazanego jako korzeń1. Zestaw reguł spój-
ności ma zastosowanie w granicach AGREGATU.
ARCHITEKTURA WARSTWOWA (ang. layered architecture) — tech-
nika oddzielenia zagadnień systemu programistycznego, wyizolowa-
nia warstw dziedziny od innych rzeczy.
ASERCJA (ang. assertion) — określenie poprawnego stanu programu
w pewnym jego miejscu, niezależnie od jego rzeczywistego wyko-
nania. Asercja określa zazwyczaj wynik operacji lub też niezmiennik
elementu projektowego.
bezstanowość (ang. stateless) — właściwość elementu projektu pozwalająca
na użycie dowolnej jego funkcji bez związku ze stanem w przeszłości.
Element bezstanowy może wykorzystywać informacje dostępne glo-
balnie, a nawet je zmieniać (to znaczy może posiadać efekty uboczne),
jednak nie ma stanu prywatnego, który wpływałby na jego zachowanie.
cykl życia (ang. life cycle) — sekwencja stanów, jakie może przyjąć obiekt
pomiędzy jego utworzeniem a usunięciem. Zazwyczaj obejmuje
również ograniczenia, pozwalające zapewnić spójność podczas przejścia
z jednego stanu do drugiego. Może zawierać przeniesienie ENCJI
pomiędzy systemami oraz KONTEKSTAMI ZWIĄZANYMI.
destylacja (ang. distillation) — proces rozdzielania składników miesza-
niny w celu wydobycia z niej esencji w bardziej szlachetnej oraz
przydatnej postaci. W projektowaniu oprogramowania jest to
abstrakcja kluczowych aspektów modelu lub też partycjonowanie
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.
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
564 SŁOWNIK
699b15244ae879c728b313782703a4d7
6
BIBLIOGRAFIA
699b15244ae879c728b313782703a4d7
6
Fowler M., Patterns of Enterprise Application Architecture, Addison-Wesley,
2003 r. [Wydanie polskie: Architektura systemów zarządzania przedsię-
biorstwem: wzorce projektowe, Wydawnictwo Helion, 2005 r.]
Gamma E., R. Helm, R. Johnson oraz J. Vlissides, Design Patterns, Addison-
-Wesley, 1995 r. [Wydanie polskie: Wzorce projektowe: elementy opro-
gramowania obiektowego wielokrotnego użytku, WNT, 2005 r.]
Kerievsky J., rozdział „Continuous Learning” w książce Extreme Program-
ming Perspectives, M. Marchesi i inni, Addison-Wesley, 2003 r.
Kerievsky J., strona WWW: http://www.industriallogic.com/xp/refactoring, 2003 r.
Larman C., Applying UML and Patterns: An Introduction to Object-Oriented
Analysis and Design, Prentice Hall PTR, 1998 r. [Wydanie polskie: UML
i wzorce projektowe: analiza i projektowanie obiektowe oraz iteracyjny model
wytwarzania aplikacji, Wydawnictwo Helion, 2013 r.]
Merriam-Webster, Merriam-Webster’s Collegiate Dictionary, dziesiąte wydanie
Merriam-Webster, 1993 r.
Meyer B., Object-oriented Software Construction, Prentice Hall PTR, 1988 r.
[Wydanie polskie: Programowanie zorientowane obiektowo, Wydawnictwo
Helion, 2005 r.]
Murray-Rust P., H. Rzepa oraz C. Leach, Abstrakt nr 40, zaprezento-
wany w charakterze plakatu na 210. spotkaniu AC w Chicago 21
sierpnia 1995 r., http://www.ch.ic.ac.uk/cml/, 1995 r.
Pinker S., The Language Instinct: How the Mind Creates Language, Harper-
Collins, 1994 r.
Succi G. J., D. Wells, M. Marchesi oraz L. Williams, Extreme Programming
Perspectives, Pearson Education, 2002 r.
Warmer J. oraz A. Kleppe, The Object Constraint Language: Precise Modeling
with UML, Addison-Wesley, 1999 r.
Wirfs-Brock R., B. Wilkerson oraz L. Wiener, Designing Object-Oriented
Software, Prentice Hall PTR, 1990 r.
Wirfs-Brock R. oraz A. McKean, Object Design: Roles, Responsibilities, and
Collaborations, Addison-Wesley, 2003 r. [Wydanie polskie: Projekto-
wanie obiektowe: role, odpowiedzialność i współpraca, Wydawnictwo
Helion, 2006 r.]
566 BIBLIOGRAFIA
699b15244ae879c728b313782703a4d7
6
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)
699b15244ae879c728b313782703a4d7
6
568
699b15244ae879c728b313782703a4d7
6
SKOROWIDZ
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
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
699b15244ae879c728b313782703a4d7
6
699b15244ae879c728b313782703a4d7
6
699b15244ae879c728b313782703a4d7
6