Download as pdf or txt
Download as pdf or txt
You are on page 1of 578

Tytuł oryginału: Domain-Driven Design: Tackling Complexity in the Heart of Software

Tłumaczenie: Rafał Szpoton

ISBN: 978-83-283-0528-1

Authorized translation from the English language edition, entitled: DOMAIN-DRIVEN


DESIGN: TACKLING COMPLEXITY IN THE HEART OF SOFTWARE;
ISBN 0321125215; by Eric Evans; published by Pearson Education, Inc,
publishing as Addison Wesley.
Copyright © 2004 by Eric Evans.

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.

Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu


niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą
kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym
lub innym powoduje naruszenie praw autorskich niniejszej publikacji.

Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi


bądź towarowymi ich właścicieli.

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)

Pliki z przykładami omawianymi w książce można znaleźć pod adresem:


ftp://ftp.helion.pl/przyklady/domdri.zip

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

 Poleć książkę na Facebook.com  Księgarnia internetowa


 Kup w wersji papierowej  Lubię to! » Nasza społeczność
 Oceń książkę

699b15244ae879c728b313782703a4d7
6
Wyrazy uznania dla książki
„Ta książka powinna znajdować się na półce każdego wnikliwego
programisty”.
— Kent Beck

„Eric Evans napisał wspaniałą książkę traktującą o tym, w jaki sposób


można sprawić, aby projekt oprogramowania odpowiadał modelowi
pojęciowemu dziedziny problemu, który program rozwiązuje.
Jego książka jest zgodna z ideą programowania ekstremalnego. Nie
zajmuje się ona tworzeniem rysunków dziedziny, lecz mówi o tym, w jaki
sposób o niej myśleć, jakim językiem ją opisywać i w jaki sposób zorga-
nizować program, aby odzwierciedlał nabywane zrozumienie dziedziny.
Eric uważa, że nauka dziedziny może występować zarówno pod koniec
projektu, jak i na jego początku, dlatego refaktoryzacja stanowi zasad-
niczy element jego techniki.
Lektura książki sprawia przyjemność. Eric zna wiele interesujących
historii, o których potrafi ciekawie opowiadać. Uważam, że ta książka
jest zasadniczą lektura dla programistów — stanowi ona przyszły kanon
pozycji”.
— Ralph Johnson,
autor książki pt. Wzorce projektowe:
elementy oprogramowania obiektowego wielokrotnego użytku

„Jeżeli wciąż nie czujesz, abyś uzyskiwał korzyści ze swojej inwestycji


w programowanie obiektowe, z tej książki dowiesz się, co zapomniałeś
zrobić”.
— Ward Cunningham

„Ericowi udało się wychwycić tę część procesu projektowania, która była


od zawsze używana przez doświadczonych projektantów obiektowych,
a której jako grupa nie byliśmy jednak w stanie przekazać do reszty
społeczności. Przekazywaliśmy wprawdzie pewne elementy tej wiedzy,
jednak nigdy nie udało nam się uporządkować i usystematyzować pryncy-
piów tworzenia logiki dziedzinowej. Dlatego ta książka jest tak istotna”.
— Kyle Brown,
autor książki pt. Enterprise Java Programming with IBM Websphere

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

„Eric zebrał rzeczywiste doświadczenie w modelowaniu oraz tworzeniu


biznesowych aplikacji w postaci praktycznej i przydatnej książki. Stworzone
z perspektywy zaufanego praktyka oraz przedstawione przez Erica opisy
języka wszechobecnego, korzyści ze stosowania przez użytkowników
współdzielonych modeli, zarządzania cyklem życia obiektów, logicznego
i fizycznego formowania aplikacji czy też procesu oraz wyników głębokiej
refaktoryzacji stanowią razem duży wkład do naszej dziedziny”.
— Luke Hohmann,
autor książki pt. Więcej niż architektura oprogramowania

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

Rozdział 2. Komunikacja i użycie języka ......................................... 51


Język wszechobecny ........................................................................52
Modelowanie na głos .......................................................................58
Jeden zespół, jeden język .................................................................60
Dokumenty i diagramy ...................................................................63
Spisane dokumenty projektowe ....................................................65
Wykonywalna podstawa .............................................................68
Modele objaśniające .........................................................................68

Rozdział 3. Związanie modelu z implementacją ............................... 71


Projektowanie sterowane modelem ...............................................73
Paradygmaty modelowania i narzędzia wspierające ......................76
Projekt mechaniczny ...................................................................79
Projekt sterowany modelem .........................................................80
Odkrywanie szkieletu — dlaczego modele są ważne
dla użytkowników .........................................................................83
Modelowanie praktyczne ................................................................86

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

Rozdział 5. Wyrażenie modelu w programie ...................................107


Asocjacje ........................................................................................ 109
ENCJE (zwane również obiektami referencyjnymi) .................. 115
Modelowanie ENCJI .............................................................. 120
Projektowanie operacji na tożsamości ......................................... 121
WARTOŚCI .................................................................................. 125
Projektowanie OBIEKTÓW WARTOŚCI ............................ 128
Projektowanie asocjacji korzystających z WARTOŚCI .............. 131
USŁUGI ........................................................................................ 132
USŁUGI a wyizolowana warstwa dziedziny .......................... 134
Ziarnistość ............................................................................... 136
Dostęp do USŁUG ................................................................. 137
MODUŁY (zwane również PAKIETAMI) ................................ 138
MODUŁY zwinne (agile modules) ......................................... 140
Pułapki tworzenia pakietów na podstawie wymogów
infrastruktury ........................................................................ 142
Paradygmaty modelowania ........................................................... 146
Dlaczego dominuje paradygmat obiektowy? ............................... 146
Nieobiekty w świecie obiektowym .............................................. 149
Utrzymywanie PROJEKTU STEROWANEGO
MODELEM w przypadku łączenia paradygmatów .............. 150

Rozdział 6. Cykl życia obiektu dziedziny .......................................153


AGREGATY .................................................................................. 155
FABRYKI ....................................................................................... 166
Wybór FABRYK oraz ich miejsc .............................................. 169
Kiedy potrzebujesz jedynie konstruktora .................................... 171
Projektowanie interfejsu ............................................................ 173

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

Rozdział 7. Użycie języka — przykład rozszerzony ....................... 195


Prezentacja systemu logistycznego dla ładunku ..........................195
Izolowanie dziedziny — wprowadzenie aplikacji ........................198
Rozróżnianie ENCJI oraz WARTOŚCI ......................................199
Role (rola) oraz inne atrybuty ....................................................201
Projektowanie asocjacji w dziedzinie logistyki morskiej .............201
Granice AGREGATU ...................................................................203
Wybór REPOZYTORIÓW ..........................................................204
Przeglądanie scenariuszy ...............................................................206
Przykładowa funkcjonalność aplikacji — zmiana miejsca
przeznaczenia ładunku ..........................................................206
Przykładowa funkcjonalność aplikacji — powtórzenie operacji ....206
Tworzenie obiektów .....................................................................207
FABRYKI oraz konstruktory klasy Cargo .................................207
Dodanie operacji obsługi ............................................................208
Przerwa na refaktoring — projekt alternatywny
AGREGATU Cargo ...................................................................209
MODUŁY w modelu logistyki morskiej .....................................213
Nowa funkcjonalność — sprawdzanie przydziału ......................215
Łączenie dwóch systemów .........................................................216
Wzbogacanie modelu — segmentacja biznesu .............................217
Poprawa wydajności .................................................................219
Ostateczna wersja ..........................................................................220

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

Rozdział 9. Odkrywanie pojęć niejawnych ......................................241


Wyciąganie pojęć ........................................................................... 242
Nasłuchiwanie języka .............................................................. 242
Analiza dziwnej implementacji ................................................. 247
Rozmyślanie nad sprzecznościami ............................................. 252
Czytanie książki ...................................................................... 253
Wielokrotne powtarzanie prób .................................................. 255
W jaki sposób zamodelować mniej oczywiste pojęcia ................. 256
Procesy jako obiekty dziedziny .................................................. 259
SPECYFIKACJA .................................................................. 261
Zastosowanie SPECYFIKACJI w implementacji ..................... 264

Rozdział 10. Projekt elastyczny ......................................................279


INTERFEJSY UJAWNIAJĄCE ZAMIAR ................................. 283
FUNKCJE BEZ EFEKTÓW UBOCZNYCH ......................... 287
ASERCJE ....................................................................................... 293
Teraz widzimy lepiej ............................................................... 296
ZARYSY KONCEPCYJNE ......................................................... 298
Nieprzewidziana zmiana ......................................................... 301
KLASY SAMODZIELNE ............................................................ 304
ZAMKNIĘCIE OPERACJI ......................................................... 307
Projektowanie deklaratywne ......................................................... 310
Języki właściwe dziedzinie ....................................................... 311
Deklaratywny styl projektowania ................................................. 312
Rozszerzenie SPECYFIKACJI w stylu deklaratywnym ........... 313
Subsumpcja ............................................................................. 318

10 SPIS TREŚCI

699b15244ae879c728b313782703a4d7
6
Kierunki ataku ................................................................................321
Definiowanie poddziedzin ........................................................321
W miarę możliwości polegaj na ustalonym formalizmie ...............322

Rozdział 11. Stosowanie wzorców analitycznych ............................ 333

Rozdział 12. Powiązanie wzorców projektowych z modelem ........... 349


STRATEGIA (zwana również POLITYKĄ) ...............................351
KOMPOZYT ................................................................................356
Dlaczego nie wzorzec PYŁKU (FLYWEIGHT)? ........................361

Rozdział 13. Refaktoryzacja ku głębszemu zrozumieniu ................. 363


Początek .........................................................................................364
Zespoły poszukiwawcze ................................................................364
Wcześniejsze odkrycia ...................................................................365
Projekt dla programistów ..............................................................366
Wyczucie czasu ..............................................................................367
Kryzys jako źródło możliwości .....................................................368

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

Rozdział 15. Destylacja ..................................................................443


DZIEDZINA GŁÓWNA ............................................................ 446
Wybór RDZENIA dziedziny .................................................. 449
Kto wykonuje prace? ................................................................ 449
Zwiększanie destylacji ................................................................... 451
PODDZIEDZINY OGÓLNE .................................................... 452
Ogólny nie oznacza możliwy do ponownego wykorzystania ....... 459
Zarządzanie ryzykiem projektowym ......................................... 459
OPIS WIZJI DZIEDZINY .......................................................... 461
RDZEŃ WYRÓŻNIONY .......................................................... 464
Dokument destylacji ................................................................. 465
RDZEŃ oznaczony ................................................................ 466
Dokument destylacji jako narzędzie procesowe ........................... 467

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

Rozdział 16. Struktury dużej skali ................................................. 485


PORZĄDEK EWOLUCYJNY ....................................................490
METAFORA SYSTEMU .............................................................493
Dlaczego nie potrzebujemy metafory „naiwnej” ..........................495
WARSTWY ODPOWIEDZIALNOŚCI .....................................496
W jaki sposób struktura wpływa na bieżący projekt? ...................502
Wybór odpowiednich warstw .....................................................505
POZIOM WIEDZY ......................................................................511
SZKIELET KOMPONENTÓW DOŁĄCZANYCH ..............521
Jak ograniczająca powinna być struktura? ....................................526
Refaktoryzacja ku lepiej dopasowanej strukturze ........................527
Minimalizm ............................................................................528
Komunikacja oraz samodyscyplina .............................................528
Restrukturyzacja przyczynia się do projektu elastycznego .............529
Destylacja zmniejsza obciążenie ................................................530

Rozdział 17. Łączenie strategii ....................................................... 531


Łączenie struktur dużej skali
z KONTEKSTAMI ZWIĄZANYMI .......................................531
Łączenie struktur dużej skali oraz destylacji ................................534
Najpierw oszacowanie ...................................................................536
Kto określa strategię? .....................................................................536
Powstawanie struktury w trakcie tworzenia aplikacji ..................537
Zespół architektoniczny skoncentrowany na kliencie ....................538
Sześć podstawowych kryteriów dotyczących podejmowania
strategicznych decyzji projektowych .........................................538
To samo dotyczy szkieletów technicznych ...................................541
Wystrzegaj się planu głównego ...................................................542

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

Wiele rzeczy komplikuje proces tworzenia oprogramowania. Jednak zazwy-


czaj najważniejszą tego przyczyną jest sama złożoność dziedzin rozpatry-
wanych zagadnień. Jeżeli na przykład, drogi Czytelniku, pragnąłbyś dodać
automatyzację do złożonego ludzkiego przedsiębiorstwa, wtedy stworzone
oprogramowanie z pewnością nie mogłoby unikać jego złożoności —
mogłoby jedynie spróbować ją kontrolować.
Kluczem do zarządzania złożonością jest dobry model dziedziny,
to znaczy taki, który wykraczałby poza powierzchniową wizję dziedziny,
dotykając jej wewnętrznej struktury — taki, który mógłby zostać później
wykorzystany przez twórców oprogramowania. Dobry model dziedziny
może być niesamowicie cenny. Nie jest jednak łatwo go stworzyć. Doko-
nać tego w sposób prawidłowy może tylko niewielu ludzi i — co więcej —
sztuki tej bardzo trudno jest się nauczyć.
Eric Evans należy do nielicznego grona ludzi, którzy potrafią w prawi-
dłowy sposób tworzyć modele dziedziny. Odkryłem to w czasie, gdy
byłem jego współpracownikiem. Była to jedna z tych cudownych chwil,
w których udaje się znaleźć klienta bardziej utalentowanego od siebie.
Nasza współpraca nie trwała wprawdzie długo, lecz była niesamowicie
przyjemna. Od tego czasu pozostajemy w kontakcie i przez ten cały czas
obserwowałem proces powstawania tej książki.
Warto było na nią czekać.
Książka, którą trzymasz, rozrosła się do bardzo ambitnego poziomu —
stawia sobie ona za cel stworzenie oraz opis słownictwa definiującego sztukę
modelowania dziedziny. Ma to służyć stworzeniu ram odniesienia, przez
które będziemy mogli spoglądać na tę umiejętność, wyjaśniać ją, jak rów-
nież podejmować próbę jej nauki. W trakcie nadawania kształtu tej książce
przyszło mi do głowy wiele nowych pomysłów i naprawdę byłbym zdu-
miony, gdyby jej lektura nie zainspirowała nowymi pomysłami również
innych osób doświadczonych w dziedzinie modelowania koncepcyjnego.
Ericowi udało się także zespolić wiele wyuczonych przez lata rzeczy.
Po pierwsze, w modelowaniu nie należy oddzielać koncepcji od imple-
mentacji. Osoba modelująca dziedzinę w sposób efektywny nie tylko
potrafi na przykład użyć tablicy w trakcie rozmowy z księgowym, lecz

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

Od przynajmniej dwudziestu lat najlepsi projektanci oprogramowania uznają


modelowanie oraz projektowanie dziedzinowe za jeden z najważniejszych
tematów. Jednak wciąż jeszcze niewiele istnieje opisów tego, co należy
w tym celu zrobić i w jaki sposób. Chociaż nigdy nie zostało to w jasny
sposób sformułowane, w środowisku osób zajmujących się obiektowością
powstała filozofia, którą nazywam projektowaniem sterowanym dziedziną1.
Ostatnich dziesięć lat spędziłem na rozwoju złożonych systemów,
należących do kilku dziedzin technicznych oraz biznesowych. W swojej
pracy wypróbowywałem najlepsze praktyki w procesie projektowania oraz
rozwoju oprogramowania, gdy tylko pojawiały się one u liderów pro-
gramowania obiektowego. Niektóre z moich projektów zakończyły się
bardzo pomyślnie, nieliczne skończyły się porażką. Wspólną cechą tych
pierwszych był bogaty model dziedzinowy, powstały iteracyjnie w pro-
cesie projektowania, który stał się później elementem projektu.
W tej książce przedstawione zostaną podstawy podejmowania decyzji
projektowych, a także techniczne słownictwo używane do omawiania
projektu dziedziny. Stanowi ona syntezę powszechnie akceptowanych
praktyk projektowych z moimi własnymi przemyśleniami oraz doświad-
czeniami. Mogą one być następnie wykorzystane przez zespoły tworzące
oprogramowanie do systematycznego wdrażania projektowania sterowa-
nego dziedziną.

Trzy różne projekty


W mojej pamięci tkwią wciąż trzy projekty, będące żywymi przykładami
tego, jak bardzo praktyka projektowania dziedzinowego może wpłynąć na
rezultat programowania. Chociaż wszystkie te trzy projekty spowodowały
dostarczenie przydatnego oprogramowania, to jednak tylko jeden z nich
osiągnął swoje ambitne cele i wytworzył złożony program, który był dalej
stale rozwijany i dostosowywany do bieżących potrzeb organizacji.

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.

Proces projektowania a implementacja


Książki o projektowaniu lub książki o procesach — w każdej z nich bar-
dzo rzadko występują wzajemne odwołania do obu tych rodzajów publi-
kacji. Każdy temat na swój sposób jest złożony. Ta książka traktuje o pro-
jektowaniu, jednak sądzę, że projekt i proces są ze sobą nierozerwalnie
związane. Idee projektowe muszą zostać pomyślnie zaimplementowane
albo inaczej utkną w akademickich dysputach.
Gdy tylko ktoś nauczy się technik projektowania, czuje się pobudzony
odkrytymi możliwościami. Po chwili jednak spadają na niego brudne realia
prawdziwego projektu. I wtedy nie jest już w stanie połączyć nowych
pomysłów projektowych z technologią, której zmuszony jest używać, lub
też nie potrafi zdecydować, kiedy warto poświęcić dany aspekt projektu
za cenę czasu wdrożenia, a kiedy należy się uprzeć i znaleźć czyste roz-
wiązanie. Deweloperzy potrafią rozmawiać ze sobą nawzajem o stoso-
waniu zasad projektowych, jednak bardziej naturalna jest dla nich roz-
mowa o rzeczywistym postępie prac. Chociaż więc ta książka traktuje
o projektowaniu, gdy tylko zajdzie taka potrzeba, przejdę natychmiast przez
sztuczną granicę do procesu, co pomoże nam umieścić założenia projek-
towe we właściwym kontekście.
Niniejsza książka nie jest związana z żadną szczególną metodologią,
jednak skłania się ku nowej rodzinie „zwinnych procesów tworzenia opro-
gramowania2”. W szczególności zakładam, iż w projekcie zastosowanych
zostało już kilka praktyk. Dwie z nich są wymagane przed zastosowaniem
podejścia opisanego w tej książce:

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.

W ostatnich latach dało się odczuć bunt przeciwko stosowaniu tradycyjnych


metodologii programowania, które obciążały projekty koniecznością two-
rzenia bezużytecznej statycznej dokumentacji, a także wcześniejszym
planowaniem i projektowaniem. W przeciwieństwie do tych metodologii,
procesy zwinne, w rodzaju programowania ekstremalnego, kładą nacisk
na uwzględnianie zmian oraz niepewności w projekcie.
Programowanie ekstremalne uwzględnia ważność decyzji projek-
towych, lecz sprzeciwia się zdecydowanie wcześniejszemu projektowaniu.
W zamian dużo wysiłku kładzie się w nim na komunikację oraz zwięk-
szanie zdolności projektu do szybkiej zmiany kierunku. Dzięki takiej zdol-
ności do reagowania na sytuację deweloperzy mogą na każdym etapie pro-
jektu wybrać „najprostszą metodę, która może zadziałać”, a następnie,
poprzez wiele małych zmian do projektu, modyfikować kod w sposób
ciągły, aby w końcu osiągnąć projekt spełniający prawdziwe potrzeby
klienta.

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.

Dodatkowe materiały, włączając w to przykłady kodu lub dyskusje na


forum, można znaleźć na stronie pod adresem http://domaindrivendesign.org.

Kto powinien przeczytać tę książkę?


Książka została napisana w pierwszej kolejności dla programistów two-
rzących oprogramowanie obiektowe. Jednak większość członków zespołu
programistycznego może wynieść pożytek z lektury niektórych jej części.
Zapoznanie się z książką będzie mieć największe znaczenie dla pracowni-
ków włączonych obecnie w prace projektowe, którzy będą mogli wypró-
bować część opisanych metod bezpośrednio w praktyce, a także dla osób
mających już duże doświadczenie w takich projektach.
Do pełnego korzystania z tej książki konieczne może być posiadanie
pewnej wiedzy na temat modelowania obiektowego. Przykłady wykorzy-
stują diagramy UML, a także kod w języku Java. Z tego powodu istotne
jest posiadanie podstawowej umiejętności odczytywania tych języków.
Niemniej jednak nie jest konieczna ich mistrzowska znajomość. Znajo-
mość programowania ekstremalnego może nadać dyskusji o procesie
wytwarzania oprogramowania inną perspektywę. Niemniej jednak cały
materiał powinien być zrozumiały również dla osób bez tej wiedzy.
W przypadku średnio zaawansowanych programistów, którzy mieli
już szansę zapoznać się z kilkoma książkami traktującymi o projektowaniu

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

Zanim jednak mógłbym nawet pomyśleć o stworzeniu tej książki, musia-


łem wyrobić sobie pewien obraz oraz zrozumienie procesu projektowania
oprogramowania. W tym procesie wspierała mnie wspaniałomyślność kilku
genialnych ludzi, będących moimi nieformalnymi mentorami i przyjaciółmi.
David Siegel, Eric Gold oraz Iseult White — każdy z nich na swój sposób
pomógł mi wypracować moje spojrzenie na projektowanie oprogramowania.
Jednocześnie także Bruce Gordon, Richard Freyberg oraz dr Judith Segal
pomogli mi odnaleźć moją drogę w świecie pracy projektowej.
Oczywiście, moje naturalne przemyślenia ewoluowały wraz z ideami
pojawiającymi się równocześnie w społeczności. Niektóre z tych wpływów
staną się jasne w trakcie lektury głównego tekstu książki, a gdzie tylko to
możliwe, będą wzmiankowane w odwołaniach lub przypisach. Inne idee
były tak podstawowe, że nawet nie zauważyłem ich wpływu na mnie.
Promotor mojej pracy magisterskiej dr Bala Subramanium zachęcił
mnie do modelowania matematycznego, którego następnie użyliśmy do
modelowania kinetyki reakcji chemicznych. Modelowanie, jak każde inne,
doprowadziło mnie częściowo do napisania tej książki.
Cofając się jeszcze bardziej, mój sposób myślenia został ukształtowany
przez moich rodziców — Carol oraz Gary’ego Evansów. A moje zaintere-
sowania zostały rozbudzone przez kilku niesamowitych nauczycieli, szcze-
gólnie przez Dale Currier (moją nauczycielkę matematyki w szkole śred-
niej), Mary Brown (nauczycielkę lingwistyki angielskiej w szkole średniej)
oraz Josephine McGlamery (nauczycielkę nauk ścisłych w szóstej klasie).
Na koniec pragnę serdecznie podziękować moim przyjaciołom oraz
rodzinie, a także Fernandowi De Leon za ich wsparcie w każdej chwili.

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

ZASTOSOWANIE MODELU DZIEDZINY 31

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

W trakcie wywiadów w jednym z programów telewizyjnych komik John


Cleese opowiedział o pewnym zdarzeniu, jakie miało miejsce podczas
tworzenia filmu pt. Monty Python i święty Graal. Otóż ekipa powtarzała
wielokrotnie ujęcie jednej ze scen, jednak z jakiegoś powodu żadne z nich
nie było zabawne. Ostatecznie zrobiono przerwę i przedyskutowano pro-
blem ze znajomym komikiem Michaelem Palinem (który był drugim
aktorem w tej scenie). W efekcie wypracowano pewną drobną modyfi-
kację. Po kolejnym ujęciu scena okazała się śmieszna i na tym zakoń-
czono pracę.
Kolejnego ranka Cleese przeglądał wstępną wersję filmu, którą monta-
żysta zmontował poprzedniego dnia pracy. Gdy dotarł do sceny, która
sprawiała tyle kłopotów, okazało się, że nie jest ona wcale zabawna —
montażysta wykorzystał jedną z wcześniejszych jej wersji.
John zapytał go, dlaczego nie użył ostatniego ujęcia, tak jak było to
nakazane, na co ten odpowiedział: „Nie mogłem — ktoś wszedł w to
ujęcie”. John kilka razy ponownie przyjrzał się dokładnie ujęciu, lecz nie
mógł dojrzeć w nim niczego złego. Montażysta zatrzymał film i wskazał
na fragment rękawa palta, który przez ułamek chwili widoczny był na
klatce filmu.
Montażysta był całkowicie skoncentrowany na precyzyjnym wyko-
naniu swojego zadania. Obawiał się, że inni montażyści, który obejrzą ten
film, będą oceniać jego pracę jedynie przez pryzmat technicznej dosko-
nałości. W efekcie w tym procesie istota sceny została utracona.
Ostatecznie zabawne ujęcie zostało odtworzone przez reżysera filmu,
który miał wyczucie filmów komediowych. Dokładnie w ten sam sposób
liderzy zespołów rozumiejący ważność dziedziny mogą zainterweniować,
gdy proces tworzenia modelu reprezentującego jej głębokie zrozumienie
utknie w martwym punkcie.

ZASTOSOWANIE MODELU DZIEDZINY 33

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

P rzed kilkoma laty brałem udział w projektowaniu specjalistycznego


oprogramowania do projektowania obwodów drukowanych1. Tkwiła
w tym jedna pułapka — nie wiedziałem nic o sprzęcie elektronicznym.
Miałem wprawdzie dostęp do kilku projektantów obwodów PCB, jednak
zazwyczaj po trzech minutach rozmowy bolała mnie już głowa. W jaki więc
sposób mogłem zrozumieć wystarczająco wiele, aby stworzyć ten pro-
gram? Oczywiście, nie starałem się zostać inżynierem elektroniki przed
terminem ostatecznego oddania programu.
Rozpoczęliśmy od poproszenia projektantów obwodów drukowanych
o dokładny opis oczekiwanego działania programu. To był jednak zły
pomysł. Byli oni wprawdzie doskonałymi projektantami, lecz ich ocze-
kiwania zazwyczaj ograniczały się do odczytania pliku tekstowego, jego
uporządkowania i zapisu z pewnymi komentarzami, a następnie wypro-
dukowania raportu. To zdecydowanie nie było coś, co mogłoby dopro-
wadzić do milowego skoku w wydajności ich pracy, którego oczekiwali
po programie.
Kilka pierwszych spotkań było rozczarowujących, jednakże w rapor-
tach, o które poprosili, tliło się światełko nadziei. Raporty te zawierały
zawsze „sieci połączeń” (ang. nets) i różne ich szczegóły. W tej dziedzinie
połączenie jest najzwyczajniej przewodnikiem, który może połączyć ze sobą
dowolną liczbę elementów na płytce obwodu i przesłać sygnał elektryczny
do wszystkiego, co zostanie do niej podłączone. W ten sposób uzyskaliśmy
pierwszy element modelu dziedziny.

Rysunek 1.1.

1
ang. printed-circuit board, w skrócie PCB — przyp. tłum.

35

699b15244ae879c728b313782703a4d7
6
Rysunek 1.2.

W trakcie omawiania z ekspertami ich oczekiwań dotyczących funk-


cjonalności programu zacząłem rysować diagramy. W celu opisania różnych
scenariuszy użyłem nieformalnej odmiany diagramów interakcji obiektów.

Ekspert 1: Elementy nie muszą być układami elektronicznymi (ang. chips).


Projektant (ja): Czy w takim razie powinienem je nazywać „elementami”
(ang. components)?
Ekspert 1: Nazywamy je „elementami składowymi” (ang. component instan-
ces). Może wystąpić więcej niż jeden ten sam rodzaj elementu.
Ekspert 2: Kwadrat z napisem „sieć” przypomina już element składowy.
Ekspert 1: Ale on nie używa naszej notacji zapisu. Sądzę, że wszystko
jest dla nich takim kwadratem.
Projektant: Niestety, muszę przyznać, że masz rację. W takim razie uwa-
żam, że lepiej będzie nieco bardziej wyjaśnić tę notację.

W trakcie tej rozmowy eksperci stale mnie poprawiali, co sprawiło,


że zacząłem się uczyć. W ten sposób wygładzaliśmy niejasności oraz sprzecz-
ności w ich terminologii, a także różnice pomiędzy ich różnymi opiniami.
W tym czasie również oni podlegali procesowi uczenia się. W rezultacie
zaczęli opisywać rzeczy w sposób bardziej precyzyjny oraz spójny, co
pozwoliło nam razem rozpocząć tworzenie modelu.

Ekspert 1: Nie wystarczy powiedzieć, że sygnał dociera do doc-lok2,


musimy przecież jeszcze znać nóżkę.
Projektant: Doc-lok?
Ekspert 2: To znaczy to samo, co element składowy. Doc-lok to jego
nazwa w pewnym narzędziu, którego używamy.
Ekspert 1: Nieważne. Sieć połączeń łączy konkretną nóżkę (ang. pin)
elementu z inną nóżką innego elementu.
Rozdział 1. Przetwarzanie wiedzy

2
ang. ref-des — skrót od „reference destination” — przyp. tłum.

36 ROZDZIAŁ 1. PRZETWARZANIE WIEDZY

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.

Aby skoncentrować nasze wysiłki, ograniczyliśmy się na moment do


pojedynczej funkcjonalności. „Symulator sondy” miałby badać rozcho-
dzenie się sygnału w celu wykrycia przypuszczalnych obszarów projektu
mogących powodować pewnego rodzaju problemy.

Projektant: Rozumiem już, w jaki sposób sygnał jest przenoszony przez


sieć ścieżek (ang. net) do wszystkich połączonych z nią nóżek
(ang. pin), ale w jaki sposób idzie dalej? Czy topologia (ang. topology)
ma coś z tym wspólnego?
Ekspert 2: Nie. To element składowy (ang. component) przepuszcza sygnał.
Projektant: Z pewnością nie możemy zamodelować wewnętrznego dzia-
łania układu elektronicznego. To zbyt skomplikowane.
Ekspert 2: Wcale nie musimy tego robić. W tym celu używamy uprosz-
czenia. Posługujemy się jedynie listą przesyłu sygnału w układzie
z jednej nóżki do innej.
Projektant: Czy jest to coś w tym rodzaju?
[Po znacznej liczbie prób i błędów byliśmy już w stanie naszkicować
scenariusz].
Projektant: Co właściwie chcecie uzyskać z tych obliczeń?
Ekspert 2: Szukaliśmy znacznych opóźnień sygnałów — powiedzmy,
dowolnej ścieżki przesyłu sygnału, która miałaby więcej niż dwa lub
trzy skoki. To podstawowa zasada. Gdy ścieżka jest zbyt długa, sygnał
mógłby nie dotrzeć w jednym takcie zegara.

ROZDZIAŁ 1. PRZETWARZANIE WIEDZY 37

699b15244ae879c728b313782703a4d7
6
Rysunek 1.4.

Projektant: Więcej niż trzy skoki… To znaczy, że musimy obliczać dłu-


gość ścieżki. A co w takim razie jest skokiem?
Ekspert 2: W każdym momencie, gdy sygnał dociera do sieci ścieżek
(ang. net), jest to traktowane jako jeden skok.
Projektant: W takim razie moglibyśmy przekazywać liczbę skoków, a sieć
ścieżek mogłaby tę wartość zwiększać. O, w ten sposób:

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.

38 ROZDZIAŁ 1. PRZETWARZANIE WIEDZY

699b15244ae879c728b313782703a4d7
6
Rysunek 1.6.

Projektant: Przepraszam. Rzeczywiście zagłębiłem się zbytnio w szcze-


góły. Jednak zastanawiałem się w trakcie, gdzie teraz będzie pasować
do tego obrazka topologia.
Ekspert 1: Ale nie jest ona potrzebna do symulowania sondy.
Projektant: W takim razie teraz ją pominę, dobrze? Będziemy mogli do
niej powrócić, kiedy dojdziemy do tych funkcjonalności.

I w ten oto sposób pracowaliśmy (ze zdecydowanie większą liczbą wza-


jemnych nieporozumień niż w rozmowie przytoczonej powyżej).
Dyskutowanie oraz dopracowywanie, zadawanie pytań oraz znajdo-
wanie odpowiedzi. Model stworzony został na podstawie mojego zro-
zumienia dziedziny oraz ich zrozumienia, w jaki sposób będzie on użyty
w rozwiązaniu. Diagram klas reprezentujący wczesny model przypomina
nieco poniższy.

Rysunek 1.7.

Po upływie kilku kolejnych spędzonych w ten sposób dni uznałem,


że rozumiem wystarczająco dużo, aby spróbować napisać kod. W tym
celu, posługując się wzorcem do testów automatycznych, stworzyłem bar-
dzo prosty prototyp. Pominąłem przy tym wszystkie zagadnienia sprzętowe.
Nie było też obsługi przechowywania danych ani interfejsu użytkownika.
To umożliwiło mi skoncentrowanie się jedynie na jego działaniu. Po kilku

ROZDZIAŁ 1. PRZETWARZANIE WIEDZY 39

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.

Elementy wydajnego modelowania


Do opisanego wcześniej sukcesu przyczyniło się kilka rzeczy:
1. Powiązanie modelu z jego implementacją. Wstępne powiązanie zostało już
zdefiniowane przez zgrubny prototyp, a następnie zostało ono utrzy-
mane i rozbudowane w trakcie kolejnych iteracji.

40 ROZDZIAŁ 1. PRZETWARZANIE WIEDZY

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.

W używanej dawniej metodologii wodospadu eksperci biznesowi rozma-


wiali z analitykami, którzy pogłębiali wiedzę i opracowywali jej abstrak-
cyjną reprezentację, przekazywaną następnie do programistów, którzy two-
rzyli oprogramowanie. To podejście nie sprawdza się całkowicie, ponieważ
brak jest w nim komunikatu zwrotnego. To na analitykach spoczywa cał-
kowita odpowiedzialność za stworzenie modelu, wyłącznie na podstawie
informacji pochodzących od ekspertów biznesowych. Analitycy nie mają
wcale możliwości nauczyć się niczego od programistów lub nabyć doświad-
czenia, używając wczesnych wersji oprogramowania. Wiedza przepływa
tylko w jednym kierunku, lecz nigdzie się nie gromadzi.
Inne projekty mogą używać procesu iteracyjnego, lecz one także
mogą ponieść porażkę, ponieważ nie tworzą opisu abstrakcyjnego. Pro-
gramiści proszą ekspertów o opis oczekiwanej funkcjonalności, a potem
ją tworzą. Następnie pokazują efekt swojej pracy ekspertom i oczekują
dalszych wskazówek. Jeżeli programiści używają metod refaktoringu, są
w ten sposób w stanie utrzymać kod w na tyle czystym stanie, aby go
rozwijać. Jeżeli jednak nie są oni zainteresowani dziedziną, wtedy dowiedzą
się jedynie, jakie powinno być zachowanie aplikacji, a nie zasady, które
nią sterują. W ten sposób można wprawdzie stworzyć przydatne opro-

42 ROZDZIAŁ 1. PRZETWARZANIE WIEDZY

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.

W tym momencie, drogi Czytelniku, zatrzymaj się i zadaj sobie pytanie.


Czy w trakcie lektury nauczyłeś się czegoś o projektowaniu obwodów
elektronicznych? Pomimo że przykłady zakładały jedynie pobieżne użycie
wiedzy dziedzinowej, to jednak z omówienia modelu dziedziny powinny
wyniknąć pewne wnioski. Sam nauczyłem się bardzo wiele. Wprawdzie
nie stałem się inżynierem od projektowania obwodów, lecz nie było to
moim celem. Nauczyłem się za to, jak rozmawiać z ekspertami dziedzi-
nowymi, zrozumiałem najważniejsze pojęcia odnoszące się do aplikacji,
a także nauczyłem się, jak dopilnować poprawności modelu w trakcie jego
tworzenia.
W rzeczywistości naszemu zespołowi udało się w końcu odkryć, że
symulacja sondy miała niski priorytet, i ta funkcjonalność w końcu została

44 ROZDZIAŁ 1. PRZETWARZANIE WIEDZY

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

Projekt bogaty w wiedzę


Rodzaj wiedzy przechwytywanej w modelu, tak jak w przykładzie doty-
czącym projektowania obwodów, wykracza często poza zadanie „znajdź
rzeczowniki”. Czynności oraz reguły biznesowe są tak samo istotne dla
dziedziny, co używane encje. Każda z dziedzin będzie mieć różne kate-
gorie pojęć. Przetwarzanie wiedzy prowadzi do modeli odzwierciedlają-
cych tego rodzaju różne przemyślenia. Równolegle ze zmianami modelu
programiści dokonują refaktoringu implementacji w taki sposób, aby je
w niej odzwierciedlić, umożliwiając aplikacji wykorzystanie zebranej wiedzy.
To właśnie takie wyjście poza encje oraz wartości sprawia, że prze-
twarzanie wiedzy staje się bardziej intensywne, ponieważ pomiędzy regu-
łami biznesowymi mogą istnieć niespójności. Eksperci dziedzinowi nie są
zazwyczaj świadomi tego, jak złożone są ich procesy myślowe. W codzien-
nej pracy zawodowej używają wszystkich swoich reguł zgodnie z zasa-
dami zdrowego rozsądku, usuwając sprzeczności i niedomówienia. Jed-
nakże program nie może tego zrobić. To właśnie w wyniku bliskiej
współpracy z ekspertami w procesie przetwarzania wiedzy dochodzi do
wyjaśniania i zwiększania spójności reguł lub też ich wyłączania z użytku.

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.

PROJEKT BOGATY W WIEDZĘ 45

699b15244ae879c728b313782703a4d7
6
Rysunek 1.8.

Możemy założyć, że aplikacja do rezerwacji będzie odpowiedzialna


za skojarzenie każdego obiektu typu Cargo (ang. ładunek) z obiektem typu
Voyage (ang. rejs), zachowanie oraz śledzenie relacji pomiędzy nimi. Jak
dotąd bardzo dobrze. Gdzieś wewnątrz kodu mogłaby znajdować się
następująca metoda:
public int makeBooking(Cargo cargo, Voyage voyage) {
int confirmation = orderConfirmationSequence.next();
voyage.addCargo(cargo, confirmation);
return confirmation;
}

Ponieważ zawsze występuje możliwość odwołania podróży w ostat-


niej chwili, standardową praktyką w przewozach morskich jest przyjmo-
wanie większego ładunku, niż dany statek może zabrać w podróż. Taka
praktyka nazywana jest „nadkompletem”3.
Niekiedy dodawany jest jedynie pewien procent ładowności (ang.
capacity) (np. 110%). W innych przypadkach stosowane są skomplikowane
reguły, faworyzujące dużych klientów lub jakiś rodzaj ładunku.
To bardzo prosta strategia w dziedzinie transportu morskiego, która
powinna być znana każdej osobie z biznesu, jednak mogłaby nie być zro-
zumiała dla wszystkich technicznych członków zespołu programistycznego.
Dokument z wymaganiami zawiera następujący wiersz:
Dopuszczaj 10% nadkompletu

Diagram klas oraz kod mogłyby wyglądać jak poniżej:

Rysunek 1.9.

public int makeBooking(Cargo cargo, Voyage voyage) {


double maxBooking = voyage.capacity() * 1.1;
if ((voyage.bookedCargoSize() + cargo.size()) > maxBooking)
return –1;
int confirmation = orderConfirmationSequence.next();
voyage.addCargo(cargo, confirmation);
return confirmation;
}

3
ang. overbooking — przyp. tłum.

46 ROZDZIAŁ 1. PRZETWARZANIE WIEDZY

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.

Nasz kod mógłby teraz wyglądać w następujący sposób:


public int makeBooking(Cargo cargo, Voyage voyage) {
if (!overbookingPolicy.isAllowed(cargo, voyage)) return –1;
int confirmation = orderConfirmationSequence.next();
voyage.addCargo(cargo, confirmation);
return confirmation;
}

4
ang. policy — przyp. tłum.

PROJEKT BOGATY W WIEDZĘ 47

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);
}

Dla wszystkich będzie teraz oczywiste, że polityka nadkompletu jest


oddzielną zasadą, a jej implementacja będzie wyodrębniona i jawna.
W tym momencie nie nakłaniam nikogo do stosowania tak rozbudowanych
zasad projektowania do każdego szczegółu dziedziny. Dopiero w rozdziale 15.,
„Destylacja”, zastanowimy się dokładnie nad tym, w jaki sposób kon-
centrować się na istotnych rzeczach, a pomijać lub minimalizować wszyst-
kie pozostałe. Ten przykład miał na celu pokazanie, że model dziedziny
i odpowiadający mu projekt mogą być wykorzystane do zachowania i prze-
kazania wiedzy. Bardziej oczywisty projekt ma poniższe zalety:
1. W celu doprowadzenia projektu do tego etapu programiści i wszystkie
inne zaangażowane osoby będą musieli zrozumieć istotę zasady nad-
kompletu, jako jednej z samodzielnych i ważnych reguł biznesowych,
a nie tylko niejasnych obliczeń.
2. Programiści mogą pokazać ekspertom biznesowym swoje artefakty
techniczne, nawet kod, które w tym momencie powinny być bardziej
zrozumiałe dla ekspertów biznesowych (może z niewielką pomocą),
1
co ułatwi przepływ informacji.

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.

48 ROZDZIAŁ 1. PRZETWARZANIE WIEDZY

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

D la projektu programistycznego model dziedziny może być istotą


wspólnego języka. Jest on powstałym w głowach członków projektu zesta-
wem pojęć wraz z określeniami i związkami pomiędzy nimi, reprezentują-
cymi wiedzę dziedzinową. Określenia oraz ich wzajemne relacje stanowią
semantykę języka zaadaptowaną do dziedziny, jednak wciąż wystarczająco
precyzyjną do zastosowania w projekcie technicznym. Stanowić to będzie
niezbędne powiązanie modelu z kodem w zadaniach programistycznych.
Komunikacja oparta na modelu nie jest ograniczona jedynie do dia-
gramów zapisanych w notacji UML1. Najbardziej efektywne wykorzysta-
nie modelu w projekcie wymaga użycia każdego dostępnego medium
komunikacyjnego. Samo istnienie modelu zwiększa przydatność fizycznej
dokumentacji projektowej, nieformalnych diagramów, a także zwykłych
rozmów wewnątrz zespołu, usilnie zalecanych w projektach prowadzonych
przy użyciu metodyk zwinnych (ang. agile). Co więcej, model usprawnia
komunikację przy użyciu samego kodu programu, a także powiązanych
z nim testów.
Wykorzystanie języka w projekcie jest zadaniem subtelnym, lecz
wciąż ważnym…

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”

Utworzenie uniwersalnego, bogatego w wiedzę projektu wymagać będzie


języka wszechstronnego, używanego w całym zespole, a także pewnego
eksperymentowania z tym językiem, co w projektach informatycznych
zdarza się dość rzadko.
***
Eksperci dziedzinowi mają ograniczone zrozumienie technicznego
żargonu związanego z programowaniem, w zamian stosując ten istniejący
w ich dziedzinie. Ich własny żargon występuje prawdopodobnie w róż-
nych odmianach. Z drugiej strony, programiści mogą rozumieć i oma-
wiać system, używając w tym celu określeń opisowych i funkcjonalnych,
pozbawionych znaczeń języka wykorzystywanego przez ekspertów. Co wię-
cej, programiści mogą tworzyć określenia abstrakcyjne, używane w ich
projekcie, lecz zupełnie niezrozumiałe dla ekspertów dziedzinowych. Pro-
gramiści pracujący nad różnymi częściami danego zagadnienia mogą wypra-
cować swoje własne pojęcia projektowe, a także sposób, w jaki opisują tę
dziedzinę.
Pomijając tę barierę językową, eksperci dziedzinowi opisują swoje
oczekiwania w sposób ogólnikowy, co sprawia, że programiści mierzący
się z zadaniem zrozumienia nowej dla siebie dziedziny bardzo rzadko ją
rozumieją. Kilku członków zespołu może stać się tłumaczami pomiędzy
oboma językami, jednak ich niewielka liczba stanie się wąskim gardłem
w przepływie informacji, a tłumaczenia mogą utracić swoją precyzję.
W projektach pozbawionych wspólnego języka programiści muszą
pełnić rolę tłumaczy dla ekspertów dziedzinowych. Ci natomiast muszą
pełnić tę samą funkcję pomiędzy programistami a innymi ekspertami dzie-
dzinowymi. Co więcej, programiści często muszą tłumaczyć siebie nawza-
jem. Wszystkie pośrednie tłumaczenia komplikują pojęcia modelu, co
prowadzi do niebezpiecznego refaktoringu kodu. Brak bezpośredniej
komunikacji zakrywa niebezpieczeństwo tworzenia się różnych rozła-
mów — różni członkowie zespołu bezwiednie używają określeń w różny
sposób. To prowadzi do powstania niepewnych fragmentów kodu, które

52 ROZDZIAŁ 2. KOMUNIKACJA I UŻYCIE JĘZYKA

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.

54 ROZDZIAŁ 2. KOMUNIKACJA I UŻYCIE JĘZYKA

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.

56 ROZDZIAŁ 2. KOMUNIKACJA I UŻYCIE JĘZYKA

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

Scenariusz 2. Wzbogacony model dziedziny


do wsparcia dyskusji

Rysunek 2.2.

Użytkownik: Jeżeli zmienimy punkt odpraw celnych, będziemy musieli


zmienić cały plan trasy.
Programista: Zgadza się. W przypadku zmiany dowolnego atrybutu
obiektu Route Specification (ang. specyfikacja trasy) usuniemy stary
obiekt Itinerary (ang. plan podróży) i poprosimy usługę Routing
Service (ang. usługa trasowania) o utworzenie nowego planu na pod-
stawie nowego obiektu Route Specification.
Użytkownik: 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 czegokol-
wiek w obiekcie Route Specification wygenerujemy od nowa
obiekt Itinerary. Stanie się tak również w przypadku wprowadze-
nia jakiejkolwiek wartości po raz pierwszy.
Użytkownik: Oczywiście, jeżeli stare dane były poprawne, nie będziemy
chcieli tego robić.

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ą

58 ROZDZIAŁ 2. KOMUNIKACJA I UŻYCIE JĘZYKA

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.

Jeden zespół, jeden język


Osoby techniczne odczuwają często potrzebę „oddzielenia” ekspertów
biznesowych od modelu dziedziny. Często mówią:
„On jest zbyt abstrakcyjny dla nich”.
„Oni nie rozumieją obiektów”.
„Musimy zebrać wymagania, używając ich terminologii”.

60 ROZDZIAŁ 2. KOMUNIKACJA I UŻYCIE JĘZYKA

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.

JEDEN ZESPÓŁ, JEDEN JĘZYK 61

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

Dzięki użyciu JĘZYKA WSZECHOBECNEGO rozmowy pomiędzy


programistami lub ekspertami dziedzinowymi oraz same wyrażenia w kodzie
opierają się na tym samym języku, wywodzącym się ze współdzielonego
modelu dziedziny.

62 ROZDZIAŁ 2. KOMUNIKACJA I UŻYCIE JĘZYKA

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

64 ROZDZIAŁ 2. KOMUNIKACJA I UŻYCIE JĘZYKA

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.

Spisane dokumenty projektowe


Komunikacja językowa uzupełnia znaczeniowo rygor oraz szczegółowość
narzucone przez kod. Chociaż jednak do połączenia wszystkich obserwa-
torów z modelem niezbędna jest bezpośrednia rozmowa między nimi,
to jednak dowolnej wielkości grupa ludzi będzie prawdopodobnie potrze-
bować stabilności oraz możliwości współdzielenia informacji, oferowanej
przez spisane dokumenty.
Stworzenie dokumentów, które rzeczywiście wspomagałyby zespół
w tworzeniu dobrego oprogramowania, jest jednak wyzwaniem samo
w sobie. Gdy tylko dokument przybierze materialną postać, bardzo czę-
sto traci związek z projektem. Jest często pozostawiany sam sobie i nie
nadąża za rozwojem kodu lub języka projektowego.
Może istnieć wiele rozwiązań tego problemu. Kilka konkretnych
dokumentów zostanie zasugerowanych w części IV tej książki. Będą one
odpowiadać na różne potrzeby projektowe, jednak nie będę nawet próbować
rekomendować zestawu dokumentów, których projekt powinien używać.
W zamian zaoferuję dwie ogólne wskazówki dotyczące oceny dokumentu:

Dokumenty powinny uzupełniać kod oraz język mówiony.


Każda zwinna metodyka posiada własne podejście do kwestii dokumentów.
Programowanie ekstremalne zaleca, aby nie używać żadnych dodatkowych
dokumentów projektowych i ograniczyć się do kodu, który powinien mówić
sam za siebie. Uruchomiony kod nie kłamie, co nie musi być prawdą w przy-
padku każdego innego dokumentu. Działanie programu jest jednoznaczne.

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.

Dokumenty powinny na siebie pracować i pozostawać aktualne.


W trakcie dokumentowania modelu na piśmie tworzę małe, wybrane
z uwagą podzbiory modelu i otaczam je tekstem. Definiuję klasy oraz
opisuję ich odpowiedzialność w słowach, a następnie zanurzam je w kon-
tekście pojęciowym na tyle, na ile dobrze jest to w stanie oddać język
naturalny. Jednak diagramy przedstawiają niektóre wybory leżące u pod-
staw sformalizowania części pojęć w postaci modelu obiektowego. Te dia-
gramy mogą być zwyczajne — nawet narysowane odręcznie.
Oprócz zmniejszania ilości pracy niezbędnej do ich wykonania dia-
gramy odręczne mają dodatkową przewagę, sprawiając wrażenie zwyczaj-
nych i tymczasowych. To bardzo ważne cechy, ponieważ zasadniczo są
one również spójne z pomysłami leżącymi u podstaw modelu.

66 ROZDZIAŁ 2. KOMUNIKACJA I UŻYCIE JĘZYKA

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.

68 ROZDZIAŁ 2. KOMUNIKACJA I UŻYCIE JĘZYKA

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.

70 ROZDZIAŁ 2. KOMUNIKACJA I UŻYCIE JĘZYKA

699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 3.

Związanie modelu
z implementacją

P ierwszą rzeczą, którą ujrzałem po przekroczeniu progu pokoju, był


kompletny diagram klas wydrukowany na wielkich arkuszach papieru
pokrywających całą ścianę. To był mój pierwszy dzień pracy przy pro-
jekcie, nad którym różni mądrzy ludzie spędzili już miesiące, stopniowo
badając i opracowując szczegółowy model dziedziny. Typowy obiekt
w tym modelu miał złożone zależności z trzema lub czterema innymi
obiektami, a cała ta sieć połączeń miała kilka naturalnych granic. Pod
tym względem analitycy zachowali zgodność z naturą dziedziny.
Pomimo przytłaczającego rozmiaru diagramu na ścianie, w modelu
uchwycono pewną wiedzę. Po okresie analizy nauczyłem się z niego cał-
kiem sporo (chociaż w tym procesie trudno było mi określić kierunek,
w którym należało podążać — zupełnie jakbym w sposób losowy przeglądał
internet). Bardziej zmartwiło mnie odkrycie, że moja analiza nie przy-
niosła mi żadnej wiedzy o kodzie aplikacji i projekcie.
Gdy programiści rozpoczęli implementację aplikacji, bardzo szybko
przekonali się, że ten gąszcz powiązań, chociaż możliwy do wychwyce-
nia przez analityków, nie przekłada się na moduły, które mogłyby zostać
zachowane i odczytane, a tym samym zachowywałyby integralność trans-
akcyjną. Zwróćmy uwagę na to, że projekt używał obiektowej bazy danych,
dlatego programiści nie musieli nawet próbować zmierzyć się z wyzwa-
niem, jakim jest mapowanie obiektów na tabele relacyjne. Na poziomie
podstawowym model nie udostępniał żadnej wskazówki dotyczącej spo-
sobu implementacji.

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.

Modele mają wiele odmian i spełniają wiele ról, nawet te ograniczone


kontekstem wyznaczonym przez projekt programistyczny. Projekt stero-
wany dziedziną wymaga modelu, który będzie nie tylko wspierał począt-
kowy proces analizy, ale będzie stanowić podstawę całego procesu. Tego
rodzaju podejście ma kilka ważnych konsekwencji w kodzie. Mniej oczy-
wiste jest jednak, iż projektowanie sterowane dziedziną wymaga również
innego spojrzenia na modelowanie.

72 ROZDZIAŁ 3. ZWIĄZANIE MODELU Z IMPLEMENTACJĄ

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.

PROJEKTOWANIE STEROWANE MODELEM 73

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

74 ROZDZIAŁ 3. ZWIĄZANIE MODELU Z IMPLEMENTACJĄ

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.

PROJEKTOWANIE STEROWANE MODELEM 75

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-

76 ROZDZIAŁ 3. ZWIĄZANIE MODELU Z IMPLEMENTACJĄ

699b15244ae879c728b313782703a4d7
6
Rysunek 3.1.

stów korzysta jedynie z technicznych zdolności obiektów do organizo-


wania kodu programu, to jednak prawdziwy przełom projektowania obiek-
towego zachodzi, gdy kod wyraża pojęcia zawarte w modelu. Java oraz
wiele innych narzędzi umożliwiają tworzenie obiektów i relacji stano-
wiących bezpośrednią analogię do pojęciowych modeli obiektowych.
Prolog jest językiem programowania, który w sposób naturalny pasuje
do PROJEKTOWANIA STEROWANEGO MODELEM. Nie zdobył on
jednak takiej masowej popularności jak języki obiektowe. W tym przypadku
paradygmatem jest logika, a model jest zestawem logicznych reguł oraz
faktów, na których one działają.
PROJEKTOWANIE STEROWANE MODELEM ma ograniczone
zastosowanie w językach proceduralnych w rodzaju języka C, ponieważ
nie istnieje paradygmat modelowania odpowiadający językom czysto proce-
duralnym. Są one proceduralne w tym znaczeniu, że programista nakazuje
komputerowi wykonanie serii kroków. Chociaż programista mógłby roz-
ważać zagadnienia dziedziny, to sam program jest serią technicznych
operacji na danych. Rezultat może być wprawdzie przydatny, lecz sam
program nie wychwytuje zbyt wiele znaczeń dziedziny. Języki procedu-
ralne bardzo często wspierają złożone struktury danych, którym bliżej do
naturalnych pojęć dziedziny, jednak stanowią one jedynie uporządko-
wane dane i nie wychwytują żadnych aktywnych aspektów dziedziny.
W rezultacie programy utworzone w językach proceduralnych zawierają
złożone funkcje powiązane ze sobą wzajemnie poprzez przewidywane
ścieżki wykonania programu, a nie odzwierciedlają pojęciowych połączeń
wynikających z modelu dziedziny.
Zanim nawet jeszcze usłyszałem o programowaniu obiektowym,
w języku FORTRAN tworzyłem programy rozwiązujące modele mate-
matyczne. Jest to jedna z dziedzin, w których FORTRAN sprawuje się
po mistrzowsku. Głównym elementem pojęciowym takiego modelu są
funkcje matematyczne, które z kolei mogą być w przejrzysty sposób wyra-
żone w FORTRANIE. Niestety, nawet pomimo tego nie istnieje sposób
uchwycenia znaczeń wyższego poziomu niż tylko te funkcje. Większość

PARADYGMATY MODELOWANIA I NARZĘDZIA WSPIERAJĄCE 77

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

78 ROZDZIAŁ 3. ZWIĄZANIE MODELU Z IMPLEMENTACJĄ

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

Natomiast reguły dotyczące projektu płytki zapisywane są w nastę-


pującym pliku:
Nazwa ścieżki Rodzaj reguły Parametry
------------- ------------- ---------
Xyz1 min_szerokosc 5
Xyz1 max_opoznienie 15
Xyz2 min_szerokosc 5
Xyz2 max_opoznienie 15
...

Konwencja nazewnicza dla ścieżek dobierana jest przez inżynierów


w sposób bardzo staranny, ponieważ zależy im, aby alfabetyczne uporząd-
kowanie danych w pliku spowodowało umieszczenie ścieżek należących
do szyny obok siebie. Następnie skrypt może przetworzyć plik i zmody-
fikować każdą ze ścieżek na bazie szyny, do której należy. Ponieważ rze-
czywisty kod wykonujący powyższe czynności jest zbyt rozległy i opisowy,
aby służyć jako przykład, w tym miejscu wymienimy tylko poszczególne
kroki procedury:
1. Posortuj listę ścieżek po nazwie ścieżki.
2. Odczytaj każdy wiersz w pliku, poszukując pierwszego, którego
nazwa spełnia wzorzec dla szyny.
3. Przeanalizuj każdy wiersz z nazwą, aby uzyskać nazwę ścieżki.

PARADYGMATY MODELOWANIA I NARZĘDZIA WSPIERAJĄCE 79

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

skutkowałaby dodaniem do pliku następujących reguł dla ścieżek:


Nazwa ścieżki Rodzaj reguły Parametry
------------- ------------- ---------
...
Xyz0 max_vias 3
Xyz1 max_vias 3
Xyz2 max_vias 3
...

Mogę sobie wyobrazić, że osoba, która po raz pierwszy stworzyła


taki skrypt, miała tylko taką jedną prostą potrzebę. Jeżeli było to jedyne
wymaganie, wtedy taki skrypt miałby dużo sensu. Obecnie jednak w prak-
tyce występują dziesiątki skryptów. Oczywiście, można byłoby je zmo-
dyfikować w ten sposób, aby współdzieliły ze sobą funkcje do sortowa-
nia oraz dopasowywania łańcuchów znakowych. A w przypadku gdyby
użyty język obsługiwał wywołania funkcji grupujących w sobie szcze-
gółowe czynności, wtedy skrypty mogłyby zacząć prawie przypominać
algorytm przedstawiony wcześniej. Wciąż jednak będą to tylko operacje
na plikach. Nowy rodzaj pliku (a są ich dziesiątki) wymagałby rozpoczęcia
pracy od początku, pomimo iż podstawowa idea grupowania szyn i sto-
sowania do nich reguł byłaby identyczna. Gdybyś potrzebował bogatszej
funkcjonalności lub interaktywności, musiałbyś dodatkowo zapłacić za
każdy wiersz.
Autorzy skryptu chcieli rozszerzyć model dziedziny narzędzia ideą
„szyny”. Ich implementacja rozpoznawała szynę, wykonując operacje sor-
towania oraz dopasowywania nazw, jednak nie jest to całkowicie zgodne
z początkową ideą.

Projekt sterowany modelem


W poprzedniej dyskusji opisaliśmy już, w jaki sposób eksperci dziedzinowi
przywykli myśleć o swoich wyzwaniach. Teraz musimy jedynie uporząd-
kować ich przemyślenia bezpośrednio w postaci modelu, na którym można
byłoby oprzeć program.

80 ROZDZIAŁ 3. ZWIĄZANIE MODELU Z IMPLEMENTACJĄ

699b15244ae879c728b313782703a4d7
6
Rysunek 3.3.
Diagram klas
zoptymalizowany
pod kątem
efektywnego
przypisywania
reguł projektu

Jeżeli obiekty dziedziny zostaną zamodelowane w języku obiektowym,


podstawowa funkcjonalność programu stanie się niemal trywialna.
Metoda assignRule() (ang. przypisz regułę) może zostać zaimplemen-
towana w abstrakcyjnej klasie Net (ang. ścieżka). Z kolei metoda assigned
Rules() (ang. przypisane reguły) tej samej klasy będzie zwracać swoje
własne reguły oraz reguły szyny.
abstract class AbstractNet {
private Set rules;

void assignRule(LayoutRule rule) {


rules.add(rule);
}

Set assignedRules() {
return rules;
}
}

class Net extends AbstractNet {


private Bus bus;

Set assignedRules() {
Set result = new HashSet();
result.addAll(super.assignedRules());
result.addAll(bus.assignedRules());
return result;
}
}

Oczywiście, klasom tym towarzyszyłaby duża ilość kodu wspierają-


cego ich działanie, jednak w tej chwili chcemy odtworzyć jedynie pod-
stawową funkcjonalność skryptu.
Aplikacja wymagać będzie również logiki importu-eksportu, którą
w tym momencie umieścimy w prostych usługach:

PARADYGMATY MODELOWANIA I NARZĘDZIA WSPIERAJĄCE 81

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ł

Będziemy również potrzebować kilku narzędzi:

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)

W tym momencie uruchomienie aplikacji będzie polegać na zaini-


cjalizowaniu repozytoriów przy użyciu wczytanych danych.
Collection nets = NetListImportService.read(aFile);
NetRepository.addAll(nets);
Collection buses = InferredBusFactory.groupIntoBuses(nets);
BusRepository.addAll(buses);

Do każdej z usług oraz repozytoriów można napisać testy jednost-


kowe. Co więcej, można również przetestować główną logikę dziedziny.
Oto na przykład test jednostkowy dla głównej logiki (używamy szablo-
nów JUnit):
public void testBusRuleAssignment() {
Net a0 = new Net("a0");
Net a1 = new Net("a1");
Bus a = new Bus("a"); // Szyna nie zależy
a.addNet(a0); //od swojej nazwy,
a.addNet(a1); //podobnie jak jej test

NetRule minWidth4 = NetRule.create(MIN_WIDTH, 4);


a.assignRule(minWidth4);

assertTrue(a0.assignedRules().contains(minWidth4));
assertEquals(minWidth4, a0.getRule(MIN_WIDTH));
assertEquals(minWidth4, a1.getRule(MIN_WIDTH));
}

Interaktywny interfejs użytkownika mógłby wyświetlać listę szyn,


pozwalając użytkownikowi dopasować do każdej z nich regułę. W celu
zachowania zgodności z wcześniejszą wersją programu lista ta mogłaby

82 ROZDZIAŁ 3. ZWIĄZANIE MODELU Z IMPLEMENTACJĄ

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));
}

Kończąc nasz program:


NetRuleExport.write(aFileName, NetRepository.allNets());

usługa odpytuje każdy obiekt ścieżki (Net) o dopasowane reguły (assigned


Rules()), a następnie wyświetla je w postaci listy.
Oczywiście, gdyby występowała tylko jedna operacja, tak jak w tym
przykładzie, wtedy podejście wykorzystujące skrypt mogłoby być równie
praktyczne. W praktyce jednak operacji było ponad dwadzieścia. PROJEKT
STEROWANY MODELEM jest bardzo łatwy w skalowaniu i może zawie-
rać ograniczenia w łączeniu reguł oraz innych rozszerzeń.
W drugim projekcie umieściliśmy również testy, ponieważ wszyst-
kie elementy zawierają dobrze zdefiniowane interfejsy, do których łatwo
napisać testy jednostkowe. Jedynym sposobem przetestowania skryptu jest
porównanie plików wejściowych i wyjściowych.
Miej na uwadze, drogi Czytelniku, że tego rodzaju projekty nie
powstają w pojedynczym kroku. Wydobycie ważnych pojęć z domeny
do prostego, przenikliwego modelu wymagałoby kilku rund optymalizacji
kodu oraz przetwarzania wiedzy.

Odkrywanie szkieletu — dlaczego modele


są ważne dla użytkowników
W teorii moglibyśmy przedstawić użytkownikowi dowolny widok systemu,
niezależnie od tego, co będzie kryć się wewnątrz. W praktyce jednak nie-
zgodność może w najlepszym razie doprowadzić do zamieszania, a w naj-
gorszym do błędów. Weźmy na przykład bardzo prosty przypadek wpro-
wadzania w błąd użytkowników przez nakładające się zakładki stron
internetowych w obecnych wersjach przeglądarki Microsoft Internet
Explorer1.
Dlaczego modele są ważne dla użytkowników

1
Ten przykład podał mi Brian Maric.

DLACZEGO MODELE SĄ WAŻNE DLA UŻYTKOWNIKÓW 83

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

84 ROZDZIAŁ 3. ZWIĄZANIE MODELU Z IMPLEMENTACJĄ

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.

DLACZEGO MODELE SĄ WAŻNE DLA UŻYTKOWNIKÓW 85

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ą,

86 ROZDZIAŁ 3. ZWIĄZANIE MODELU Z IMPLEMENTACJĄ

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.

Projektowanie sterowane dziedziną używa modelu do rozwiązywania pro-


blemów aplikacji. W procesie przetwarzania wiedzy zespół projektowy fil-
truje potoki chaotycznych informacji, nadając im postać praktycznego
modelu. PROJEKTOWANIE STEROWANE MODELEM łączy bardzo
blisko ze sobą model z implementacją. JĘZYK WSZECHOBECNY jest
kanałem przepływu informacji pomiędzy programistami, ekspertami dzie-
dzinowymi a oprogramowaniem.
Rezultatem jest aplikacja udostępniająca bogatą funkcjonalność opartą
na fundamentalnym zrozumieniu podstawowej dziedziny.
Jak zostało to już wspomniane wcześniej, sukces PROJEKTOWANIA
STEROWANEGO MODELEM jest bardzo wrażliwy na szczegółowe
decyzje projektowe, które będą przedmiotem opisu w kilku kolejnych
rozdziałach.

88 ROZDZIAŁ 3. ZWIĄZANIE MODELU Z IMPLEMENTACJĄ

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

ELEMENTY SKŁADOWE PROJEKTU STEROWANEGO MODELEM 91

699b15244ae879c728b313782703a4d7
6
Mapa nawigacji po języku PROJEKTU STEROWANEGO MODELEM

92 CZĘŚĆ II

699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 4.

Wyizolowanie
dziedziny

F ragment oprogramowania, który adresuje główne problemy dzie-


dziny, stanowi najczęściej tylko małą część całego systemu informatycznego.
Niemniej jednak jego znaczenie jest nieproporcjonalne w stosunku do
rozmiaru. Aby móc zastosować nasze najlepsze pomysły, musimy być
w stanie dostrzec w poszczególnych elementach modelu cały system.
Nie możemy być zmuszeni do wybierania ich z większego zestawu obiek-
tów, tak jak gwiazdozbiorów na nocnym niebie. Musimy oddzielić obiekty
dziedziny od innych funkcji systemu, aby nie mieszać pojęć dziedzinowych
z innymi, związanymi jedynie z technologią oprogramowania. W gąszczu
całego systemu nie możemy też stracić z oczu samej dziedziny.
W tym celu powstały zaawansowane techniki służące do wyizolo-
wania dziedziny. To dobrze znany obszar, lecz jest on tak istotny w prawi-
dłowym zastosowaniu pryncypiów modelowania dziedzinowego, że musi
zostać w tym miejscu krótko omówiony z punktu widzenia projektowania
sterowanego dziedziną.

93

699b15244ae879c728b313782703a4d7
6
Architektura warstwowa

W przypadku prostej aplikacji wspierającej użytkownika w procesie wyboru


miejsca przeznaczenia dla ładunku z listy dostępnych miast musi istnieć
kod programu, który (1) rysuje element interfejsu użytkownika na ekranie,
(2) zadaje pytanie do bazy danych w celu uzyskania listy wszystkich moż-
liwych miast, (3) odczytuje wybór użytkownika i go sprawdza, (4) przy-
pisuje wybrane miasto do ładunku oraz (5) zapisuje wybór do bazy da-
nych. Cały ten kod należy do jednego programu, lecz tylko jego mała
część jest związana z dziedziną logistyki morskiej.
Programy wymagają od projektu oraz kodu, aby obsługiwały wiele
różnych rodzajów zadań. Muszą przecież pobierać dane od użytkownika,
wykonywać logikę biznesową, odwoływać się do bazy danych, komuni-
kować przez sieci komputerowe, wyświetlać informacje dla użytkownika
i tak dalej. Z tej przyczyny ilość kodu odpowiedzialnego za każdą funk-
cjonalność programu może być znaczna.
W programowaniu obiektowym kod obsługujący interfejs
użytkownika, bazę danych, a także wykonujący inne czynności
pomocnicze jest bardzo często umieszczany bezpośrednio w obiek-
tach biznesowych. Dodatkowa logika biznesowa jest również

94 ROZDZIAŁ 4. WYIZOLOWANIE DZIEDZINY

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:

Warstwa interfejsu Odpowiedzialna za wyświetlanie informacji


użytkownika dla użytkownika oraz interpretację jego poleceń.
(lub prezentacji) Zamiast użytkownika zewnętrzny aktor może być
niekiedy innym systemem komputerowym.
Warstwa aplikacji Definiuje zamierzone zadania programu i steruje
obiektami dziedziny w celu rozwiązania postawionych
przed nimi zadań. Zadania, za które jest odpowiedzialna
ta warstwa, są znaczące dla biznesu i wymagane w celu
poprawnego współdziałania z warstwami aplikacji
innych systemów. Warstwa jest utrzymywana
w zwięzłej postaci. Nie zawiera reguł ani wiedzy
biznesowej, a jedynie zarządza zadaniami i deleguje je
do rozwiązania przez obiekty dziedzinowe w kolejnej
warstwie niżej. Warstwa nie musi odzwierciedlać
konkretnej sytuacji biznesowej, lecz może posiadać
stan, ukazujący postęp zadania użytkownikowi
lub programowi.
Warstwa dziedziny Warstwa odpowiedzialna za reprezentację zagadnień
(lub modelu) biznesowych, informacji o sytuacji biznesowej oraz
reguł biznesowych. W tym miejscu jest przechowywany
oraz używany stan odzwierciedlający sytuację
biznesową, chociaż szczegóły techniczne związane
z przechowywaniem tego stanu są oddelegowane
do warstwy infrastruktury. Ta warstwa stanowi istotę
programu od strony biznesowej.
Warstwa Udostępnia podstawowe możliwości techniczne
infrastruktury wspierające warstwy wyższe, tj. komunikaty wysyłane
do aplikacji, zachowywanie dziedziny, rysowanie
elementów interfejsu użytkownika itp. Warstwa
infrastruktury może również poprzez szkielet
architektury wspierać wzorzec interakcji pomiędzy
wszystkimi czterema warstwami.

Niektóre projekty nie tworzą ostrego podziału pomiędzy warstwami


interfejsu użytkownika oraz aplikacji. Inne mają wiele warstw infrastruk-
tury. Jednak to właśnie wydzielenie warstwy dziedziny jest istotne w celu
umożliwienia stosowania PROJEKTU STEROWANEGO MODELEM.

96 ROZDZIAŁ 4. WYIZOLOWANIE DZIEDZINY

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.

98 ROZDZIAŁ 4. WYIZOLOWANIE DZIEDZINY

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

100 ROZDZIAŁ 4. WYIZOLOWANIE DZIEDZINY

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.

To w warstwie dziedziny żyje model


W większości dzisiejszych systemów stosowana jest ARCHITEKTURA
WARSTWOWA, która używa wielu odmian podejścia do podziału na war-
stwy. Również wiele różnych stylów programowania może skorzystać
z takiego podziału. Niemniej jednak projekt sterowany dziedziną wymaga
istnienia tylko jednej konkretnej warstwy.
Model dziedziny jest zestawem pojęć. Wyrazem modelu oraz wszyst-
kich związanych z nim bezpośrednio elementów dziedziny jest „warstwa
dziedziny”. Składają się na nią dziedzina oraz implementacja logiki bizne-
sowej. W PROJEKCIE STEROWANYM MODELEM struktury programu
uzyskane w warstwie dziedziny odzwierciedlają pojęcia tego modelu.

TO W WARSTWIE DZIEDZINY ŻYJE MODEL 101

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

102 ROZDZIAŁ 4. WYIZOLOWANIE DZIEDZINY

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

ANTYWZORZEC INTELIGENTNEGO INTERFEJSU UŻYTKOWNIKA 103

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.

104 ROZDZIAŁ 4. WYIZOLOWANIE DZIEDZINY

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.

ANTYWZORZEC INTELIGENTNEGO INTERFEJSU UŻYTKOWNIKA 105

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.

106 ROZDZIAŁ 4. WYIZOLOWANIE DZIEDZINY

699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 5.

Wyrażenie modelu
w programie

O siągnięcie kompromisu w programie bez utraty jego wyrazistości,


wynikającej z zastosowania PROJEKTU STEROWANEGO DZIEDZINĄ,
wymaga zmiany podstawowego podejścia. Połączenie modelu z imple-
mentacją musi być wykonane na bardzo wysokim poziomie szczegóło-
wości. W tym rozdziale skoncentrujemy się na właśnie tych indywidual-
nych i szczegółowych elementach modelu, nadając im kształt możliwy
do wykorzystania w dalszych czynnościach, opisanych w kolejnych roz-
działach.
Rozpoczniemy od dyskusji na temat problemów z projektowaniem
oraz upraszczaniem asocjacji pomiędzy obiektami. Wprawdzie łatwo jest
takie asocjacje wymyślić i narysować, jednak ich zaimplementowanie może
wpędzić w potencjalne tarapaty. To właśnie asocjacje obrazują, jak dla wyko-
nalności PROJEKTU STEROWANEGO MODELEM istotne są nawet
tak szczegółowe decyzje implementacyjne.
Skupiając się na obiektach, będziemy kontynuować analizę związków
pomiędzy szczegółowymi decyzjami podejmowanymi podczas modelowania
a późniejszymi problemami implementacyjnymi. Skoncentrujemy się na
różnicach pomiędzy trzema wzorcami elementów modelu: ENCJAMI (ang.
entity), WARTOŚCIAMI (ang. value-object) oraz USŁUGAMI (ang. service).
Proces definiowania obiektów reprezentujących zagadnienia dziedziny
na pierwszy rzut oka wydaje się bardzo intuicyjny, jednak poważne wyzwa-
nia czają się w szczegółach. Dlatego też zostało zdefiniowanych kilka
wyróżników, zakwalifikowanych do kanonu praktyk projektowych,
mających na celu wyjaśnienie znaczenia poszczególnych elementów modelu
i wpłynięcie na stworzenie określonych typów obiektów.

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.

108 ROZDZIAŁ 5. WYRAŻENIE MODELU W PROGRAMIE

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

Bardzo często głębsze zrozumienie dziedziny prowadzi do relacji


„kwalifikowanych”. Gdybyśmy przyjrzeli się bliżej prezydentom, odkryli-
byśmy fakt, że kraj miał w danym okresie (ang. period) tylko jednego
prezydenta (no, może poza okresem wojny secesyjnej). Ten kwalifikator
zmniejsza wielorakość relacji do postaci jeden do jednego, co w sposób
widoczny ustanawia tę ważną regułę bezpośrednio w modelu. Kto zatem
był prezydentem Stanów Zjednoczonych w roku 1790? Oczywiście Jerzy
Waszyngton!

Rysunek 5.2.
Ograniczenie
asocjacji pozwala
przekazać większą
liczbę informacji
i przyczynia
się do tworzenia
bardziej
praktycznych
projektów

110 ROZDZIAŁ 5. WYRAŻENIE MODELU W PROGRAMIE

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.

Jedna z implementacji obiektu Brokerage Account (ang. konto


inwestycyjne) zdefiniowanego w powyższym modelu mogłaby w języku
Java wyglądać następująco:
public class BrokerageAccount {
String accountNumber;
Customer customer;
Set investments;
// pominęliśmy konstruktory itp.

public Customer getCustomer() {


return customer;
}

public Set getInvestments() {

ASOCJACJE 111

699b15244ae879c728b313782703a4d7
6
return investments;
}
}

Jeżeli jednak musielibyśmy pobrać dane z relacyjnej bazy danych,


wtedy inna implementacja (również tak samo zgodna z modelem) mogłaby
wyglądać w taki sposób:

Tabela: BROKERAGE_ACCOUNT

ACCOUNT_NUMBER CUSTOMER_SS_NUMBER

Tabela: CUSTOMER

SS_NUMBER NAME

Tabela: INVESTMENT

ACCOUNT_NUMBER STOCK_SYMBOL AMOUNT

public class BrokerageAccount {


String accountNumber;
String customerSocialSecurityNumber;

// pominęliśmy konstruktory itp.

public Customer getCustomer() {


String sqlQuery =
"SELECT * FROM CUSTOMER WHERE" +
"SS_NUMBER='"+customerSocialSecurityNumber+"'";
return QueryService.findSingleCustomerFor(sqlQuery);
}
public Set getInvestments() {
String sqlQuery =
"SELECT * FROM INVESTMENT WHERE" +
"BROKERAGE_ACCOUNT='"+accountNumber+"'";
return QueryService.findInvestmentsFor(sqlQuery);
}
}

Uwaga: Serwis o nazwie QueryService, stanowiący narzędzie do odczy-


tywania wierszy z bazy danych i tworzenia obiektów, jest przydatny do
wyjaśniania przykładów, jednak niekoniecznie jest dobrym projektem do
wykorzystania w prawdziwej aplikacji.

112 ROZDZIAŁ 5. WYRAŻENIE MODELU W PROGRAMIE

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.

Takie założenie nie byłoby prawdziwe we wszystkich sytuacjach biz-


nesowych (gdyby na przykład wymagane było śledzenie pozycji zakupo-
wych). Niemniej jednak wszędzie tam, gdzie odkrywane są dodatkowe
reguły narzucające ograniczenia asocjacji, powinny one być dołączane do
modelu oraz jego implementacji. W ten sposób model stanie się bardziej
precyzyjny, a sama implementacja prostsza w utrzymaniu. W takiej sytu-
acji implementacja powyższego modelu w języku Java mogłaby wyglądać
następująco:
public class BrokerageAccount {
String accountNumber;
Customer customer;
Map investments;

// pominęliśmy konstruktory itp.

public Customer getCustomer() {


return customer;
}
public Investment getInvestment(String stockSymbol) {
return (Investment)investments.get(stockSymbol);
}
}

Natomiast implementacja odczytująca dane z bazy danych przy użyciu


SQL miałaby wtedy następującą postać:
public class BrokerageAccount {
String accountNumber;
String customerSocialSecurityNumber;

// pominęliśmy konstruktory itp.


public Customer getCustomer() {
String sqlQuery = "SELECT * FROM CUSTOMER WHERE SS_NUMBER='"

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);
}
}

Ostrożna destylacja oraz ograniczanie asocjacji występujących w modelu


zbliżą nas na długiej drodze do PROJEKTU STEROWANEGO MODE-
LEM. A teraz zajmijmy się samymi obiektami. Kilka specjalnych wyróż-
ników modelu ułatwia jego praktyczną implementację.

114 ROZDZIAŁ 5. WYRAŻENIE MODELU W PROGRAMIE

699b15244ae879c728b313782703a4d7
6
ENCJE (zwane również
obiektami referencyjnymi)

Wiele obiektów nie jest zdefiniowanych bezpośrednio przez swoje atry-


buty, lecz raczej ich właściwości są określane przez zdolność do utrzy-
mywania pewnej ciągłości (ang. continuity) oraz posiadanie pewnej
tożsamości (ang. identity).

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

ENCJE (ZWANE RÓWNIEŻ OBIEKTAMI REFERENCYJNYMI) 115

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

116 ROZDZIAŁ 5. WYRAŻENIE MODELU W PROGRAMIE

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.

ENCJE (ZWANE RÓWNIEŻ OBIEKTAMI REFERENCYJNYMI) 117

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.

118 ROZDZIAŁ 5. WYRAŻENIE MODELU W PROGRAMIE

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

ENCJE (ZWANE RÓWNIEŻ OBIEKTAMI REFERENCYJNYMI) 119

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

120 ROZDZIAŁ 5. WYRAŻENIE MODELU W PROGRAMIE

699b15244ae879c728b313782703a4d7
6
Rysunek 5.5.
Atrybuty związane
z tożsamością pozostają
w ENCJI

Projektowanie operacji na tożsamości


Każda ENCJA musi posiadać jakiś sposób ustanowienia jej tożsamości przy
użyciu innego obiektu — rozróżnialnej nawet od innego obiektu z tymi
samymi atrybutami opisowymi. Atrybut identyfikujący musi mieć gwaran-
cję niepowtarzalności w systemie niezależnie od tego, jak ten system jest
zdefiniowany — nawet gdy jest rozproszony lub gdy obiekty są archiwi-
zowane.
Jak wspomnieliśmy wcześniej, języki obiektowe posiadają już ope-
racje „na tożsamości”, określające, czy dwie referencje wskazują na ten
sam obiekt. Dokonują tego poprzez porównanie położenia obiektów
w pamięci. Niestety, ten rodzaj śledzenia tożsamości jest do naszych celów
zbyt delikatny. W większości technologii dysponujących funkcją zachowy-
wania obiektów przy każdym odczytaniu takiego obiektu z bazy danych
tworzona jest też jego nowa instancja, co sprawia, że początkowa tożsamość
jest tracona. Podobnie w przypadku, gdy obiekt jest przesyłany przez
sieć — jego nowa instancja tworzona jest w miejscu docelowym, co rów-
nież powoduje utratę tożsamości. Sytuacja może być nawet gorsza, jeżeli
w systemie istnieje wiele różnych wersji tego samego obiektu, na przykład
gdy w rozproszonej bazie danych rozsyłane są modyfikacje stanu obiektu.
Podstawowy problem istnieje nawet w przypadku szkieletów uprasz-
czających te wszystkie techniczne problemy. W jaki więc sposób stwierdzić,
czy dwa obiekty reprezentują tę samą pojęciową ENCJĘ? Definicja toż-
samości wynika z modelu. Zdefiniowanie jej wymaga więc zrozumienia
dziedziny.
Niekiedy istnieje sytuacja, w której pewne atrybuty lub ich kombi-
nacje mogą gwarantować niepowtarzalność w systemie lub zostać w ten
sposób ograniczone. Takie podejście definiuje unikalny klucz dla ENCJI.

ENCJE (ZWANE RÓWNIEŻ OBIEKTAMI REFERENCYJNYMI) 121

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.

122 ROZDZIAŁ 5. WYRAŻENIE MODELU W PROGRAMIE

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.

ENCJE (ZWANE RÓWNIEŻ OBIEKTAMI REFERENCYJNYMI) 123

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

124 ROZDZIAŁ 5. WYRAŻENIE MODELU W PROGRAMIE

699b15244ae879c728b313782703a4d7
6
WARTOŚCI

Wiele obiektów nie ma pojęciowej tożsamości. Mogą one opisywać jedynie


pewną charakterystykę rzeczy.
***
Gdy dziecko rysuje obrazek, martwi się o wybór odpowiedniego koloru
mazaka, może też sprawdzić stan jego końcówki. Jeżeli jednak dwa mazaki
są tego samego koloru i kształtu, to prawdopodobnie nie będzie się przej-
mować, którego z nich użyć. Gdy zgubi mazak i zastąpi go nowym o tym
samym kolorze, wziętym z nowego opakowania, prawdopodobnie będzie
mogło wznowić swoją pracę bez zwracania uwagi na tę zmianę.
Spytaj swoje dziecko o różne rysunki wiszące na lodówce, a będzie
w stanie bardzo szybko odróżnić swoje od tych zrobionych przez siostrę.
Dziecko oraz jego siostra mają przydatne tożsamości, podobnie jak ich
gotowe rysunki. Wyobraźmy sobie jednak, jak skomplikowanym zada-
niem byłaby konieczność śledzenia tego, którym mazakiem była wyko-
nana każda z linii. Rysowanie z pewnością przestałoby sprawiać przy-
jemność.
Ponieważ w modelu najbardziej widoczne są ENCJE, a śledzenie
tożsamości każdej z nich jest tak istotne, naturalne wydaje się rozważe-
nie możliwości przypisania tożsamości do wszystkich obiektów dziedziny.
Rzeczywiście, w niektórych szkieletach aplikacji unikalny atrybut ID przy-
pisywany jest do każdego obiektu.
System musi dać sobie radę z ich śledzeniem, co sprawia, że wyklu-
czona staje się możliwość wielu optymalizacji wydajnościowych. Wysiłek

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

126 ROZDZIAŁ 5. WYRAŻENIE MODELU W PROGRAMIE

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

128 ROZDZIAŁ 5. WYRAŻENIE MODELU W PROGRAMIE

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.

130 ROZDZIAŁ 5. WYRAŻENIE MODELU W PROGRAMIE

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

ENCJE oraz WARTOŚCI są głównymi elementami tradycyjnych modeli


obiektowych. Jednak pragmatyczni projektanci doszli do wniosku, iż
potrzebny jest jeszcze inny element: USŁUGI...

WARTOŚCI 131

699b15244ae879c728b313782703a4d7
6
USŁUGI

W niektórych sytuacjach nie będzie to tylko czcza przechwałka.


Niekiedy najbardziej przejrzysty i pragmatyczny projekt obejmuje
operacje, które konceptualnie nie należą do żadnego obiektu. Zamiast na
siłę definiować wyjątek, możemy wspólnie zastanowić się teraz nad natu-
ralnymi granicami tego problemu i w sposób bezpośredni dołączyć do
modelu USŁUGI (ang. service).
***
Istnieją ważne operacje w dziedzinie, które nie mogą znaleźć swojej
naturalnej przynależności do ENCJI lub WARTOŚCI. Niektóre z nich są
z natury rzeczy aktywnościami lub działaniami, a nie rzeczami. Ponieważ
jednak nasz paradygmat modelowania jest obiektowy, to postaramy się je
tak czy inaczej dopasować do obiektów.
W tym momencie najbardziej powszechną pomyłką jest zbyt wczesne
zaprzestanie prób dopasowania danego zachowania do konkretnego obiektu,
co powoduje stopniowe zdążanie w kierunku programowania procedu-
ralnego. Jeżeli jednak wymusimy daną operację na obiekcie i nie będzie
ona pasować do jego definicji, wtedy obiekt straci swoją przejrzystość
pojęciową i stanie się trudny do zrozumienia i modyfikacji. Skompliko-
wane operacje mogą łatwo zamglić prosty obiekt, przesłaniając jego rolę.
A ponieważ operacje tego typu bardzo często działają na wielu obiektach
dziedziny, wykorzystując i koordynując ich wspólne działania, dodatkowa
odpowiedzialność może stworzyć między nimi zależności, powodujące
zagmatwanie różnych pojęć, które mogą być zrozumiane jedynie nieza-
leżnie od siebie.

132 ROZDZIAŁ 5. WYRAŻENIE MODELU W PROGRAMIE

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

USŁUGI a wyizolowana warstwa dziedziny


W tym wzorcu skoncentrujemy się na tych USŁUGACH, które mają ważne
znaczenie dla dziedziny i do niej przynależą, pamiętając przy tym, że
USŁUGI nie są stosowane jedynie w tej warstwie. Odróżnienie USŁUG
należących do warstwy dziedziny od tych z innych warstw, a także takie
zdefiniowanie ich odpowiedzialności, aby ten podział był wyraźny, wyma-
gać będzie wiele uwagi.
Większość USŁUG omawianych w literaturze jest typowo techniczna
i przynależy do warstwy infrastruktury. Oczywiście, USŁUGI z warstw
dziedziny oraz aplikacji współpracują z tymi z warstwy infrastruktury.
Na przykład bank mógłby dysponować aplikacją wysyłającą wiadomości
e-mail do klientów, gdy stan ich konta spadnie poniżej określonego
poziomu. Interfejs hermetyzujący system pocztowy, a być może rów-
nież inne alternatywne środki komunikacji, to USŁUGA w warstwie
infrastruktury.
Odróżnienie USŁUG z warstwy aplikacji od tych z warstwy dzie-
dziny może być już trudniejszym zadaniem. Warstwa aplikacji odpowie-
dzialna jest za zamówienie powiadomienia. Warstwa dziedziny natomiast
będzie odpowiedzialna za stwierdzenie, czy poziom graniczny salda został

134 ROZDZIAŁ 5. WYRAŻENIE MODELU W PROGRAMIE

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

Aplikacja Usługa aplikacyjna do przelewu środków


• Pobiera dane wejściowe (np. żądanie w formacie XML)
• Wysyła komunikat do usługi dziedziny w celu wykonania
zadania
• Nasłuchuje odpowiedzi
• Podejmuje decyzję, czy wysłać powiadomienie za pomocą
usługi w warstwie infrastruktury
Dziedzina Usługa dziedzinowa do przelewu środków
• Współpracuje z niezbędnymi obiektami Account (konto)
oraz Ledger (księga rachunkowa) w celu wykonania
odpowiednich uznań i obciążeń
• Zwraca potwierdzenie lub rezultat operacji (przelew
dozwolony lub nie itd.)
Infrastruktura Usługa infrastrukturalna do przelewu środków
• Wysyła wiadomości e-mail, listy lub inne wiadomości
w sposób nakazany przez aplikację

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-

136 ROZDZIAŁ 5. WYRAŻENIE MODELU W PROGRAMIE

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.

138 ROZDZIAŁ 5. WYRAŻENIE MODELU W PROGRAMIE

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

MODUŁY (ZWANE RÓWNIEŻ PAKIETAMI) 139

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

MODUŁY zwinne (agile modules)


MODUŁY muszą nadążać za rozwojem pozostałych części modelu. Ozna-
cza to konieczność refaktorowania MODUŁÓW równocześnie z modelem
i kodem programu. Jednak tego rodzaju modyfikacje MODUŁÓW nie
wydarzają się zbyt często. Ich zmiana zdaje się wymagać uaktualnienia
szeregu fragmentów kodu. Takie zmiany mogą działać destrukcyjnie na
komunikację w zespole, a nawet namieszać w środowisku programistycz-
nym, jak chociażby w systemie kontroli wersji kodu. W rezultacie nazwy
oraz struktury MODUŁÓW odzwierciedlają zazwyczaj zdecydowanie
wcześniejszą postać modelu niż klasy.
Nieuniknione pomyłki we wczesnych decyzjach odnośnie podziału
na MODUŁY będą prowadzić do mocnego związania kodu, co z kolei
utrudni późniejszy refaktoring. Brak zmian będzie jedynie zwiększał tę
inercję. To błędne koło może zostać przerwane tylko przez pogodzenie
się z faktami i reorganizację MODUŁÓW na podstawie zgromadzonej
wiedzy na temat problemu.

140 ROZDZIAŁ 5. WYRAŻENIE MODELU W PROGRAMIE

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

Niestety, w Javie nie ma ucieczki przed koniecznością importowa-


nia bezpośrednio do konkretnej klasy, jednak przynajmniej można za
jednym zamachem importować całe pakiety. Będzie to odzwierciedlać
pierwotny zamiar zamodelowania pakietów w postaci bardzo spójnych
jednostek, a jednocześnie zmniejszy konieczność przeróbek w przypadku
zmiany ich nazw.
ClassA1
import packageB.*;
import packageC.*;
. . .

Tak, to prawda — taka technika oznacza wymieszanie ze sobą jed-


nostek dwóch różnych rozmiarów (klasy zależą od pakietów). Jednak prze-
kazuje ona zdecydowanie więcej informacji niż importowanie poszcze-
gólnych klas, przedstawione w poprzednim przykładzie. W ten sposób
informuje o zamiarze utworzenia zależności z danym MODUŁEM.
Jeżeli dana klasa naprawdę zależy od konkretnej klasy w innym
pakiecie, a MODUŁ, do której należy, wydaje się nie mieć zależności
z tamtym innym MODUŁEM, wtedy może lepiej byłoby taką klasę
przenieść w inne miejsce lub ponownie zastanowić się nad podziałem na
MODUŁY.

MODUŁY (ZWANE RÓWNIEŻ PAKIETAMI) 141

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

142 ROZDZIAŁ 5. WYRAŻENIE MODELU W PROGRAMIE

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

MODUŁY (ZWANE RÓWNIEŻ PAKIETAMI) 143

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.

144 ROZDZIAŁ 5. WYRAŻENIE MODELU W PROGRAMIE

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.

Znaczenie modularności zwiększa się wraz z rozrastaniem się kodu i zwięk-


szaniem jego złożoności. W tym rozdziale zaprezentujemy jedynie pod-
stawową analizę. Natomiast większy fragment części IV, „Projekt strate-
giczny”, zajmuje się sposobami opakowywania i podziału dużych modeli
i projektów, a także metodami pozwalającymi zdefiniować punkty klu-
czowe pomagające ludziom w zrozumieniu projektu.
Każde pojęcie z modelu dziedziny powinno być odzwierciedlone
w pewnym elemencie implementacji. ENCJE, WARTOŚCI oraz asocjacje
wraz z kilkoma USŁUGAMI dziedzinowymi i porządkującymi je
MODUŁAMI stanowią miejsca połączeń implementacji z modelem.
Oczywiście, obiekty, wskaźniki oraz funkcje odczytu obecne w imple-
mentacji również muszą mapować się bezpośrednio do elementów modelu.
W przeciwnym razie należy wyczyścić kod lub cofnąć się i zmienić model,
lub wykonać obie te rzeczy jednocześnie.
Należy opierać się pokusie dodawania do obiektów dziedziny czego-
kolwiek, co nie jest blisko związane z pojęciami przez nie reprezentowa-
nymi. Te elementy projektu mają do wykonania swoje własne zadanie,
jakim jest wyrażenie modelu. Oczywiście, istnieją też inne odpowiedzial-
ności związane z dziedziną, które muszą zostać obsłużone, oraz inne dane,
którymi należy się zająć, aby system zaczął funkcjonować poprawnie.
Niemniej jednak nie należą one do obiektów dziedziny. W rozdziale 6.
omówimy kilka obiektów pomocniczych, pomagających w wypełnieniu
technicznych odpowiedzialności warstwy dziedziny, w rodzaju definio-
wania odwołań do bazy danych lub też hermetyzacji tworzenia złożonych
obiektów.
Wszystkie cztery wzorce opisane w tym rozdziale stanowią elementy
składowe modelu obiektowego. Jednak PROJEKT STEROWANY
MODELEM nie oznacza konieczności używania pojęć obiektowych do
wszystkich zagadnień. Istnieją również inne paradygmaty modelowania
wspierane przez obecnie istniejące narzędzia, jak chociażby silniki reguł
(ang. rules engines). To projekty muszą zdecydować, w jaki sposób uzyskać
kompromis pomiędzy tymi podejściami. Te wszystkie pozostałe narzę-
dzia oraz techniki mają na celu pomoc w dokończeniu PROJEKTU
STEROWANEGO MODELEM, a nie jego zastąpienie.

MODUŁY (ZWANE RÓWNIEŻ PAKIETAMI) 145

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.

Dlaczego dominuje paradygmat obiektowy?


Liczne powody, dla których zespoły wybierają paradygmat obiektowy,
nie są wcale techniczne, lub nawet nie są związane z właściwościami
obiektów. Od samego początku jednak modelowanie obiektowe znalazło
dobrą równowagę pomiędzy prostotą a skomplikowaniem.
Jeżeli paradygmat modelowania jest zbyt osobliwy, wtedy niewielu
programistów go opanuje i będą go używać w niepoprawny sposób. Jeżeli
nietechniczni członkowie zespołu nie będą w stanie przyswoić sobie chociaż
podstaw paradygmatu, nie zrozumieją modelu, a JĘZYK WSZECH-
OBECNY zostanie utracony na zawsze. Podstawowe założenia projek-
towania obiektowego wydają się naturalne dla większości osób. Chociaż
niektórzy programiści nie opanują subtelności programowania, to nawet
osoby bez wiedzy technicznej będą w stanie prześledzić diagram modelu
obiektowego.
Chociaż założenia modelowania obiektowego są proste, to jednak
to podejście okazało się na tyle bogate, aby wychwycić ważną wiedzę
dziedzinową. I od samego początku było wspierane przez narzędzia pro-
gramistyczne umożliwiające wyrażenie modelu w programie.
W chwili obecnej paradygmat obiektowy ma również dodatkowe
zalety, wynikające z jego dojrzałości oraz powszechnego użycia. Bez doj-
rzałej infrastruktury oraz wsparcia po stronie narzędzi projekt może być
zepchnięty w ramy technicznego projektu R&D, odwracającego uwagę
członków zespołu od programowania aplikacji i wprowadzającego dodat-
kowe ryzyko techniczne. Niektóre technologie nie działają dobrze z innymi,
a co więcej, może nie być możliwe zintegrowanie ich ze standardowymi
rozwiązaniami przemysłowymi, co będzie zmuszało zespół do ponownego
utworzenia powszechnie stosowanych narzędzi. Wiele z tych problemów

146 ROZDZIAŁ 5. WYRAŻENIE MODELU W PROGRAMIE

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

PARADYGMATY MODELOWANIA 147

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-

148 ROZDZIAŁ 5. WYRAŻENIE MODELU W PROGRAMIE

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.

Nieobiekty w świecie obiektowym


Model dziedziny nie musi być modelem obiektowym. Na przykład ist-
nieją PROJEKTY STEROWANE MODELEM zaimplementowane
w Prologu, z modelem utworzonym z reguł logicznych oraz faktów.
Paradygmaty modelowania starają się odpowiadać na sposoby postrzegania
przez ludzi konkretnych dziedzin. Dopiero wtedy ich modele kształto-
wane są przez paradygmat. Rezultatem takiego procesu jest model zgodny
z paradygmatem, który może zostać skutecznie zaimplementowany przy
użyciu narzędzi wspierających dany styl modelowania.
Niezależnie od dominującego paradygmatu modelowania występują-
cego w projekcie, będą istnieć elementy dziedziny, które będzie można
znacznie łatwiej wyrazić w innym paradygmacie. Jeżeli będzie to doty-
czyć tylko kilku elementów dziedziny, a reszta będzie działać poprawnie
z wcześniej wybranym paradygmatem, wtedy programiści prawdopo-
dobnie będą mogli żyć z kilkoma dziwnymi obiektami w spójnym modelu
(z drugiej strony, jeżeli większa część dziedziny problemu daje się bardziej
naturalnie wyrazić w innym konkretnym paradygmacie, wtedy może bar-
dziej sensowna będzie całkowita jego zamiana i wybór innej platformy
implementacyjnej). Gdy główne elementy dziedziny wydają się przyna-
leżeć do innych paradygmatów, wtedy zamodelowanie ich w tych, do któ-
rych pasują, będzie stanowić całkiem spore wyzwanie intelektualne. Będzie
trzeba też użyć zestawu różnych narzędzi wspierających implementację.
Jeżeli wzajemna zależność tych części jest mała, podsystem w innym
paradygmacie może być zahermetyzowany niezależnie, jak chociażby zło-
żony moduł obliczeń matematycznych, który może zostać najzwyczajniej
wywołany z wnętrza obiektu. Kiedy indziej różne aspekty mogą być bardziej
ze sobą splecione, jak w przypadku, gdy interakcja pomiędzy obiektami
zależy od relacji matematycznych.

PARADYGMATY MODELOWANIA 149

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.

150 ROZDZIAŁ 5. WYRAŻENIE MODELU W PROGRAMIE

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.

Chociaż PROJEKT STEROWANY MODELEM nie musi być obiektowy,


to jednak polega on na ekspresywnej implementacji elementów modelu,
niezależnie od tego, czy będą to obiekty, reguły, czy przepływy zadań.
Jeżeli dostępne narzędzia nie umożliwiają takiej implementacji, należy
rozważyć ich zmianę. Brak możliwości wyrażenia obiektów modelu
w implementacji neguje zalety tego paradygmatu.
Wymieńmy zatem cztery podstawowe reguły łączenia nieobiekto-
wych elementów z systemem obiektowym:
 Nie walcz z paradygmatem implementacji. Zawsze istnieje inny sposób
myślenia o dziedzinie. Spróbuj znaleźć pojęcia modelu pasujące do
tego paradygmatu.
 Pochyl się nad językiem wszechobecnym. Nawet w sytuacji, gdy nie istnieje
ścisłe połączenie pomiędzy narzędziami, spójne użycie języka może
zapobiec rozejściu się elementów projektu.
 Nie polegaj jedynie na języku UML. Niekiedy przywiązanie do jednego
narzędzia, jak na przykład diagramy UML, skłania ludzi do zepsucia
modelu tylko po to, aby pasował do postaci łatwej do narysowania.

PARADYGMATY MODELOWANIA 151

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.

Zanim podejmiesz się wyzwań, jakie stawia łączenie paradygmatów, powi-


nieneś wyczerpać wszystkie możliwości oferowane przez paradygmat domi-
nujący w projekcie. Nawet jeżeli niektóre pojęcia z dziedziny nie wydają
się oczywistymi obiektami, to często wciąż mogą być zamodelowane
w zgodzie z tym paradygmatem. W rozdziale 9. zajmiemy się mode-
lowaniem niekonwencjonalnych typów pojęć przy użyciu technologii
obiektowych.
Paradygmat relacyjny jest specjalnym rodzajem łączenia paradyg-
matów. Będąc najpowszechniejszą nieobiektową technologią, relacyjne
bazy danych są również bardzo blisko związane z modelem obiektowym,
ponieważ funkcjonują jako miejsce, w którym przechowywane są dane
tworzące same obiekty. Zachowywanie danych obiektowych w relacyj-
nych bazach danych zostanie omówione w rozdziale 6., wraz z wieloma
innymi wyzwaniami pojawiającymi się w cyklu życia obiektu.

152 ROZDZIAŁ 5. WYRAŻENIE MODELU W PROGRAMIE

699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 6.

Cykl życia
obiektu dziedziny

K ażdy obiekt posiada cykl życia, ponieważ musi się narodzić,


następnie przechodzi przez różne stany i ostatecznie umiera — będąc
zarchiwizowanym lub usuniętym. Oczywiście, wiele z tych obiektów jest
nieskomplikowanych i tymczasowych, tworzonych w prosty sposób, poprzez
zwykłe wywołanie ich konstruktora. Obiekty te mogą następnie zostać
wykorzystane w obliczeniach, a na koniec usunięte w procesie czyszcze-
nia pamięci (ang. garbage collection). Nie ma potrzeby, aby tego rodzaju
obiekty nadmiernie komplikować. Jednak inne obiekty mogą mieć dłuższe
życie, którego nie spędzają jedynie w aktywnej pamięci procesu. Mogą
one mieć również złożone zależności z innymi obiektami. Co więcej,
mogą napotykać różne zmiany ich stanów, do których będą mieć zastoso-
wanie niezmienniki. Zarządzanie takimi obiektami często stanowi wyzwa-
nie, które w prosty sposób może wykoleić próby użycia PROJEKTU STE-
ROWANEGO MODELEM.
Wyzwania związane z zarządzaniem obiektami można zaliczyć do
dwóch kategorii:
1. Utrzymywanie integralności w całym cyklu życia.
2. Zapobieganie zagmatwaniu modelu przez zbyt skomplikowane zarzą-
dzanie cyklem życia obiektów.
W tym rozdziale zajmiemy się tymi problemami dzięki użyciu trzech
wzorców. Na początku AGREGATY wzmocnią sam model, poprzez
zdefiniowanie jasnych zasad dotyczących jego własności oraz granic, co

153

699b15244ae879c728b313782703a4d7
6
Rysunek 6.1.
Cykl życia
obiektu dziedziny

pozwoli uniknąć powstania chaotycznej, pogmatwanej sieci obiektów. Ten


wzorzec jest istotny dla zachowania integralności we wszystkich fazach
cyklu życia.
Następnie spojrzymy na początek cyklu życia, wykorzystując FABRYKI
w celu utworzenia oraz rekonstrukcji złożonych obiektów oraz AGRE-
GATÓW, przy jednoczesnym zachowaniu hermetyzacji ich wewnętrznej
struktury. Na koniec REPOZYTORIA pozwolą nam zająć się środkową
oraz końcową fazą cyklu życia, udostępniając metody wyszukiwania oraz
odczytywania zachowanych obiektów oraz hermetyzując dużą część zwią-
zanej z tym infrastruktury.
Chociaż REPOZYTORIA oraz FABRYKI nie pochodzą bezpośred-
nio z dziedziny, to jednak odgrywają w jej projekcie istotną rolę. Te struk-
tury dopełniają PROJEKT STEROWANY MODELEM, umożliwiając nam
radzenie sobie z obiektami modelu.
Modelowanie AGREGATÓW oraz dodawanie do projektu FABRYK
i REPOZYTORIÓW dają możliwość zajmowania się obiektami modelu
w sposób uporządkowany oraz przy użyciu znaczących jednostek zdefi-
niowanych w ich cyklu życia. AGREGATY zaznaczają zakres, w którym
niezmienniki muszą być utrzymywane na każdym etapie cyklu życia.
FABRYKI oraz REPOZYTORIA działają na AGREGATACH, hermety-
zując złożoność konkretnych przejść pomiędzy stanami cyklu życia.

154 ROZDZIAŁ 6. CYKL ŻYCIA OBIEKTU DZIEDZINY

699b15244ae879c728b313782703a4d7
6
AGREGATY

Minimalistyczny projekt asocjacji pomaga w uproszczeniu połączeń i ogra-


nicza eksplozję relacji pomiędzy obiektami. Jednak większość dziedzin
biznesowych jest tak wewnętrznie zagmatwana, że ostatecznie i tak pew-
nie skończymy, śledząc długie i zawiłe ścieżki pomiędzy referencjami do
różnych obiektów. W pewien sposób ta gęstwina obrazuje realia świata,
który rzadko kiedy obliguje nas do zachowania ostrych granic. To jest ogólny
problem w projektowaniu oprogramowania.
Powiedzmy, że chcemy usunąć z bazy danych obiekt Person. Razem
z daną osobą znikną jej nazwisko, data urodzenia i opis stanowiska pracy.
Co stanie się jednak z jej adresem? Przecież mogłyby istnieć inne osoby
zamieszkujące w tym samym miejscu. Jeżeli usuniemy adres, wtedy takie
obiekty klasy Person będą mieć referencję do usuniętego obiektu. Jeżeli
jednak go pozostawimy, to w bazie danych będziemy gromadzić niepo-
trzebne adresy. Automatyczny proces czyszczenia pamięci mógłby usunąć
niepotrzebne adresy, jednak takie techniczne usprawnienie (nawet jeżeli jest
dostępne w bazie danych) ignoruje podstawowy problem modelowania.
Nawet jeżeli będziemy rozpatrywać wyizolowaną transakcję, sieć
powiązań w typowym modelu obiektowym nie będzie umożliwiać łatwej

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

156 ROZDZIAŁ 6. CYKL ŻYCIA OBIEKTU DZIEDZINY

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ń

158 ROZDZIAŁ 6. CYKL ŻYCIA OBIEKTU DZIEDZINY

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.

Bardzo pomocne może być używanie technicznego szkieletu aplikacji,


pozwalającego na deklarowanie AGREGATÓW, a następnie automatycznie
wykonującego blokowanie schematu itp. Bez takiej asysty to zespół będzie
musiał wykazać się zdyscyplinowaniem i stale umawiać się między sobą
co do AGREGATÓW i obsługującego je kodu.

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

Powyższy diagram przedstawia jedynie całkiem tradycyjny obraz


zamówienia zakupowego (ang. Purchase Order), rozłożonego na poszcze-
gólne składniki (ang. Purchase Order Line Item), z regułą niezmiennika
wymuszającą, aby suma składników zamówienia nie przekraczała limitu
autoryzacji dla całego zamówienia (ang. approved limit). Z tą implementacją
związane są trzy problemy:

160 ROZDZIAŁ 6. CYKL ŻYCIA OBIEKTU DZIEDZINY

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

Obiekty będą odczytywane z bazy danych i tworzone w przestrzeni


adresowej pamięci systemu każdego z użytkowników. Następnie będą
mogły być przeglądane i edytowane. Blokady w bazie danych będą zakła-
dane jedynie w czasie rozpoczynania edycji. Dlatego też zarówno Jurek,
jak i Amanda będą mogli pracować równocześnie, dopóki nie będą uży-
wać tych samych składników. Wszystko w porządku... do czasu gdy Jurek
i Amanda nie będą pracować na oddzielnych składnikach tego samego
zamówienia.

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)

Gdy tylko oboje użytkownicy zapisali swoje zmiany, zamówienie


zostało zachowane w bazie danych, co naruszyło niezmiennik zdefinio-
wany w modelu dziedziny. Została tym samym złamana ważna reguła
biznesowa i nawet nikt o tym nie został poinformowany. Widać od razu,
że zablokowanie pojedynczego wiersza nie jest wystarczającym środkiem
bezpieczeństwa. Gdybyśmy w zamian zablokowali całe zamówienie,
moglibyśmy uniknąć tego problemu.
Program nie pozwoli na zachowanie transakcji do chwili, gdy Amanda
nie rozwiąże problemu, być może zwiększając w tym celu swój limit lub
usuwając jedną z gitar. Tego rodzaju mechanizm zapobiega wystąpieniu
problemów i mógłby być nawet dobrym rozwiązaniem w sytuacji, gdyby
większość zmian była rozproszona pomiędzy wiele zamówień. Jeżeli
jednak zazwyczaj wiele osób pracuje jednocześnie nad różnymi składni-
kami tego samego dużego zamówienia, wtedy takie blokowanie mogłoby
być kłopotliwe.

162 ROZDZIAŁ 6. CYKL ŻYCIA OBIEKTU DZIEDZINY

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

W rezultacie wszyscy troje chwilę sobie poczekają.


W tym momencie możemy rozpocząć usprawnianie modelu poprzez
dołączenie następującej wiedzy biznesowej:
1. Artykuły są używane w wielu zamówieniach (duże współzawod-
nictwo).
2. Zmian do artykułów jest mniej niż do zamówień.
3. Zmiany do cen artykułów niekoniecznie przenoszone są na istniejące
zamówienia. Wszystko zależy od czasu zmiany ceny w stosunku do
stanu tego zamówienia.
Punkt 3. staje się szczególnie zrozumiały w chwili, gdy weźmiemy
pod uwagę zarchiwizowane zamówienia, które już zostały dostarczone.
Powinny one oczywiście wskazywać raczej na ceny z chwili ich wysta-
wienia, a nie obecne.

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

164 ROZDZIAŁ 6. CYKL ŻYCIA OBIEKTU DZIEDZINY

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,

166 ROZDZIAŁ 6. CYKL ŻYCIA OBIEKTU DZIEDZINY

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)

168 ROZDZIAŁ 6. CYKL ŻYCIA OBIEKTU DZIEDZINY

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.

Wybór FABRYK oraz ich miejsc


Ogólnie mówiąc, fabryki tworzymy po to, aby wyprodukowały nam coś,
czego szczegóły chcielibyśmy ukryć i umieszczamy je tam, skąd chcieli-
byśmy ten proces kontrolować. Te decyzje obracają się zazwyczaj wokół
AGREGATÓW.
Jeżeli na przykład chciałbyś dodać element do istniejącego AGRE-
GATU, mógłbyś w tym celu stworzyć METODĘ WYTWÓRCZĄ w jego
korzeniu. W taki sposób wnętrze AGREGATU zostałoby ukryte przed
zewnętrznym klientem, nadając jednocześnie korzeniowi odpowiedzial-
ność za zapewnienie integralności AGREGATU w trakcie dodawania do
niego elementów. Ten proces przedstawiliśmy na rysunku 6.13.
Innym przykładem byłoby umieszczenie METODY WYTWÓRCZEJ
w obiekcie, który byłby zaangażowany w tworzenie innego obiektu, chociaż
po utworzeniu tego produktu sam nie posiadałby go dłużej. W przypadku,

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.

170 ROZDZIAŁ 6. CYKL ŻYCIA OBIEKTU DZIEDZINY

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ę

172 ROZDZIAŁ 6. CYKL ŻYCIA OBIEKTU DZIEDZINY

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.

Gdzie mieści się logika niezmienników?


FABRYKA jest odpowiedzialna za upewnienie się, czy dla obiektu lub
AGREGATU, który tworzy, spełnione są wszystkie niezmienniki. Wciąż
jednak powinniśmy dwa razy się zastanowić, zanim reguły mające zastoso-
wanie do danego obiektu wyciągniemy poza ten obiekt. FABRYKA może
oddelegować sprawdzanie niezmienników do produktu i to bardzo często
jest najlepsze wyjście. Jednak FABRYKI mają specjalną relację ze swoimi
produktami.
Od początku znają bowiem wewnętrzną strukturę tworzonego pro-
duktu, a ich cały sens istnienia sprowadza się do implementacji swoich
produktów. W pewnych okolicznościach istnieją korzyści z umieszczenia
logiki niezmienników w FABRYCE i zmniejszenia złożoności produk-
tów. Jest to szczególnie interesujące w przypadku reguł AGREGATÓW
(które mogą dotyczyć wielu obiektów). Do takiej praktyki może natomiast
bardzo zniechęcić METODA WYTWÓRCZA dołączona do innego
obiektu dziedziny.
Chociaż dla zasady niezmienniki stosowane są pod koniec każdej
operacji, to jednak niekiedy przekształcenia dozwolone w obiekcie mogą
nigdy nie dopuścić ich do głosu. Na przykład może istnieć reguła mająca
zastosowanie do operacji nadania wartości atrybutom określającym toż-
samość ENCJI. Jednak po utworzeniu tożsamość będzie już niezmienna.
Również WARTOŚCI nie mogą być zmieniane. W takim przypadku obiekt
nie musi zawierać logiki, która nigdy w trakcie jego całego aktywnego życia
nie będzie mogła zostać zaimplementowana. W takich przypadkach
FABRYKA będzie stanowić dobre logiczne miejsce do umieszczenia nie-
zbędników, co uprości tworzony produkt.

FABRYKI ENCJI a FABRYKI WARTOŚCI


FABRYKI ENCJI różnią się od FABRYK WARTOŚCI pod dwoma wzglę-
dami. WARTOŚCI są niezmienne, a tworzony produkt jest już kompletny
w swojej ostatecznej postaci. Dlatego operacje w FABRYCE muszą mieć
możliwość pełnego opisu produktu. Natomiast FABRYKI ENCJI inicjali-
zują raczej tylko niezbędne atrybuty wymagane do utworzenia popraw-
nego AGREGATU. Jeżeli tylko te szczegółowe informacje nie są wyma-
gane przez niezmienniki, wtedy mogą być one dodane później.

174 ROZDZIAŁ 6. CYKL ŻYCIA OBIEKTU DZIEDZINY

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

Odtwarzanie zachowanych obiektów


Do tej chwili FABRYKA odgrywała rolę w bardzo początkowym etapie
cyklu życia obiektu. Prędzej czy później jednak większość obiektów jest
zachowywana w bazie danych lub przesyłana przez sieć. Większość metod
transmisji musi spłaszczyć obiekt do bardziej ograniczonej postaci; jedynie
kilka obecnie dostępnych systemów bazodanowych podczas tych operacji
zachowuje obiektowy charakter jego zawartości. Dlatego też proces odtwa-
rzania obiektu może wymagać potencjalnie złożonych czynności przywró-
cenia jego części do postaci aktywnego obiektu.
FABRYKA używana do odtwarzania bardzo przypomina tę wykorzy-
stywaną do tworzenia obiektów, za wyjątkiem dwóch głównych różnic:
1. FABRYKA ENCJI używana do jej odtwarzania nie przypisuje nowego
ID używanego do śledzenia tej ENCJI. W ten sposób mogłaby utracić
ciągłość związku z poprzednią postacią obiektu. Dlatego też parametry
wejściowe FABRYKI odtwarzającej zachowany obiekt muszą zawierać
atrybuty używane do jego identyfikacji.
2. FABRYKA odtwarzająca obiekt będzie inaczej obsługiwać sytuacje naruszenia
jego niezmienników. W przypadku niespełnienia niezmienników nowego
obiektu podczas jego tworzenia FABRYKA mogłaby po prostu odmó-
wić dalszej pracy, natomiast w sytuacji jego odtwarzania może być
wymagana bardziej elastyczna odpowiedź. Jeżeli obiekt już istnieje
gdzieś w systemie (na przykład w bazie danych), ten fakt nie może
zostać zignorowany, podobnie jak nie można zignorować naruszenia
reguły. Musi istnieć jakaś strategia naprawiania tego rodzaju niespój-
ności, co może sprawić, iż odtwarzanie obiektów będzie większym
wyzwaniem niż tworzenie nowych.

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

Podsumowując — musi zostać zidentyfikowany punkt, w którym


będą tworzone instancje obiektów, a ich zakres musi zostać wyraźnie
określony. Mogą to być zwyczajne konstruktory, lecz bardzo często
potrzebny jest bardziej abstrakcyjny lub rozbudowany mechanizm tworze-

176 ROZDZIAŁ 6. CYKL ŻYCIA OBIEKTU DZIEDZINY

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

Asocjacje umożliwiają nam odszukanie obiektu na podstawie jego relacji


z innymi. Jednak przeszukiwanie musimy zacząć od jakiejś ENCJI lub
WARTOŚCI w środkowym etapie jej cyklu życia.
***
Aby wykonać jakąkolwiek czynność z obiektem, należy posiadać do
niego referencję. A w jaki sposób ją uzyskać? Jedną z metod jest utwo-
rzenie obiektu, ponieważ w tym procesie zostanie zwrócona referencja do
nowego obiektu. Innym sposobem jest podążanie w kierunku wyznaczo-
nym przez asocjację. Można rozpocząć od aktualnie posiadanego obiektu
i odpytać go o skojarzony z nim inny obiekt. Programy obiektowe wyko-
nują wiele tego rodzaju operacji, a te połączenia dają modelom wiele wyra-
zistości. Niemniej jednak zawsze należy dysponować tym pierwszym
obiektem.
Kiedyś rzeczywiście napotkałem na projekt, w którym zespół bar-
dzo entuzjastycznie przyjął koncepcję PROJEKTU STEROWANEGO
MODELEM i wszystkie obiekty tworzył lub odszukiwał poprzez ich aso-
cjacje! Obiekty były przechowywane w obiektowej bazie danych, a sami
członkowie projektu twierdzili, iż istniejące związki pojęciowe dostarczą

178 ROZDZIAŁ 6. CYKL ŻYCIA OBIEKTU DZIEDZINY

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

180 ROZDZIAŁ 6. CYKL ŻYCIA OBIEKTU DZIEDZINY

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

182 ROZDZIAŁ 6. CYKL ŻYCIA OBIEKTU DZIEDZINY

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.

184 ROZDZIAŁ 6. CYKL ŻYCIA OBIEKTU DZIEDZINY

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.

Kod klienta, w przeciwieństwie


do programistów, ignoruje implementację
REPOZYTORIUM
Hermetyzacja technologii umożliwiających utrwalanie danych pozwala na
uproszczenie klienta oraz oddzielenie go do implementacji REPOZYTO-
RIUM. Jednak jak to zwykle bywa w przypadku hermetyzacji, programista
również musi zrozumieć, co dzieje się wewnątrz. Jeżeli REPOZYTORIA
są używane na różne sposoby lub też różnie działają, wtedy ich wpływ na
wydajność może być olbrzymi.
Kyle Brown opowiedział mi kiedyś historię o tym, jak został wezwany
do aplikacji przemysłowej opartej na serwerze Websphere, która właśnie
była przekazywana do produkcji. Z nieznanych przyczyn już po kilku
godzinach działania systemowi zabrakło pamięci. Kyle przejrzał szybko kod

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ć

186 ROZDZIAŁ 6. CYKL ŻYCIA OBIEKTU DZIEDZINY

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

Praca ze szkieletami architektury


Przed implementacją czegoś w rodzaju REPOZYTORIUM należy dokład-
nie przyjrzeć się stosowanej infrastrukturze, a w szczególności szkieletowi
architektury. Może okazać się, że udostępnia on usługi, które mogą być
w prosty sposób użyte do utworzenia REPOZYTORIUM, lub też że cały
czas będzie tę czynność utrudniał. Co więcej, możesz odkryć, że szkielet
architektury już ma zdefiniowany równoważny wzorzec zachowywania
obiektów albo że ten wzorzec nie jest wcale REPOZYTORIUM.
Na przykład projekt może używać J2EE. Spoglądając na znaczeniowe
podobieństwo pomiędzy tym szkieletem architektury a wzorcami PRO-
JEKTOWANIA STEROWANEGO MODELEM (oraz mając na uwadze,
że ziarno encyjne nie jest tym samym, co ENCJA), mógłbyś zamodelować
korzeń AGREGATU w postaci tego ziarna. W J2EE konstrukcją umoż-
liwiającą dostęp do tego rodzaju obiektów jest obiekt EJB Home. Jednak
używanie tego obiektu w sposób przypominający REPOZYTORIUM może
prowadzić do innych problemów.
Ogólnie rzecz biorąc, nie należy walczyć ze szkieletem architektury.
Spróbuj odnaleźć sposób na zachowanie podstaw projektu sterowanego
dziedziną, lecz nie upieraj się przy szczegółach w przypadku, gdy szkielet
architektury im nie sprzyja. Postaraj się odszukać podobieństwa pomię-
dzy znaczeniami projektu sterowanego dziedziną a pojęciami w szkielecie
architektury. Oczywiście, wszystko przy założeniu, że nie ma innego wyboru
niż jego stosowanie. Wiele projektów J2EE nie używa wcale takich obiektów

188 ROZDZIAŁ 6. CYKL ŻYCIA OBIEKTU DZIEDZINY

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

Inną przyczyną skłaniającą ludzi do łączenia ze sobą FABRYKI oraz


REPOZYTORIUM jest chęć posiadania logiki typu „znajdź lub utwórz”,
w której klient może opisać, jakiego obiektu potrzebuje, a jeżeli taki obiekt
nie zostanie odnaleziony, to mechanizm utworzy nowy. Tworzenia tego
rodzaju funkcjonalności powinno się unikać. W najlepszym wypadku będzie
z niej niewielki pożytek. Wiele sytuacji, w których wydawałaby się przydatna,
zostanie wyeliminowanych poprzez rozróżnienie ENCJI oraz WARTOŚCI.
Klient, który chciałby otrzymać WARTOŚĆ, mógłby pójść prosto do
FARBYKI i poprosić o nową. Zazwyczaj rozróżnienie pomiędzy obiektem
nowym a istniejącym jest ważne w dziedzinie, a szkielet architektury,
który w sposób przejrzysty łączy tę logikę, w rzeczywistości może jedynie
zaciemnić cały obraz.

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

190 ROZDZIAŁ 6. CYKL ŻYCIA OBIEKTU DZIEDZINY

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

PROJEKTOWANIE OBIEKTÓW DLA RELACYJNYCH BAZ DANYCH 191

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,

192 ROZDZIAŁ 6. CYKL ŻYCIA OBIEKTU DZIEDZINY

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.

PROJEKTOWANIE OBIEKTÓW DLA RELACYJNYCH BAZ DANYCH 193

699b15244ae879c728b313782703a4d7
6
194 ROZDZIAŁ 6. CYKL ŻYCIA OBIEKTU DZIEDZINY

699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 7.

Użycie języka
— przykład
rozszerzony

W poprzednich trzech rozdziałach wprowadzaliśmy język wzor-


ców służących udoskonaleniu modelu szczegółowego oraz utrzymywa-
niu PROJEKTU STEROWANEGO MODELEM. W przedstawionych
wcześniej przykładach wzorce stosowane były zazwyczaj oddzielnie, jednak
w prawdziwych projektach konieczne będzie ich łączenie. Dlatego w tym
rozdziale przedstawimy przykład rozszerzony (wciąż jednak zdecydowa-
nie prostszy od rzeczywistego projektu). W trakcie tworzenia tego pro-
jektu odziedziczymy wcześniejszy model i przejdziemy przez proces jego
udoskonalenia, wcielając się w hipotetyczny zespół zmagający się z pro-
blemami wynikającymi z wymagań oraz implementacji. Będziemy roz-
wijać PROJEKT STEROWANY MODELEM, przedstawiając wszystkie
okoliczności mające na niego wpływ, a także sposób, w jaki wzorce
z części II pomogą nam się z nimi zmierzyć.

Prezentacja systemu logistycznego


dla ładunku
Będziemy rozwijać nowe oprogramowanie dla firmy z obszaru logistyki
morskiej. Początkowe wymagania można zdefiniować w trzech prostych
funkcjonalnościach:

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

Dostępny model organizuje wiedzę dziedzinową oraz udostępnia


język dla zespołu. Możemy na przykład w następujący sposób zdefinio-
wać wspierające wymagania:
„Wielu klientów (Customers) zaangażowanych jest w transport ła-
dunku (Cargo) i każdy z nich odgrywa inną rolę”.

„Określony jest główny cel, jakim jest dostarczenie ładunku (Cargo)”.

196 ROZDZIAŁ 7. UŻYCIE JĘZYKA — PRZYKŁAD ROZSZERZONY

699b15244ae879c728b313782703a4d7
6
„Cel zostanie osiągnięty dzięki serii operacji spedycyjnych (Carrier
Movements), zgodnie ze specyfikacją podróży (Specification)”.

Każdy obiekt w modelu ma jasno określone zdarzenie.

Operacja obsługi (Handling Event) będzie pojedynczą akcją na


ładunku (Cargo), na przykład załadunkiem na statek lub odprawą celną.
Ta klasa byłaby prawdopodobnie włączona w hierarchię różnych rodza-
jów obsługi (np. załadunek, rozładunek, odbiór przez adresata itp.).
Klasa Delivery Specification (specyfikacja podróży) określa cel ope-
racji logistycznej, który w najprostszym przypadku zdefiniowany byłby
przez miejsce przeznaczenia oraz czas dostarczenia, jednak mogłaby być
również bardziej złożona. Implementuje ona wzorzec SPECYFIKACJI
(patrz rozdział 9.).
Oczywiście, ta odpowiedzialność mogłaby zostać włączona do obiektu
Cargo (ładunek), jednak stworzenie abstrakcji operacji logistycznej w ten
sposób daje przynajmniej trzy korzyści:
1. Bez Delivery Specification obiekt Cargo byłby odpowiedzialny za
szczegółowe znaczenie wszystkich atrybutów oraz asocjacji okre-
ślających cel operacji logistycznej. To strasznie zaciemniłoby obraz
obiektu Cargo i utrudniło jego zrozumienie oraz zmianę.
2. Taka konstrukcja ułatwia pominięcie szczegółów w trakcie wyjaśnia-
nia całego ogólnego modelu. Na przykład w obiekcie Delivery Spe-
cification mogłyby być określone inne kryteria, których jednak dia-
gram na tym poziomie szczegółowości jeszcze nie musiałby odkrywać.
Diagram mówi czytelnikowi, że istnieje SPECYFIKACJA operacji
logistycznej, której szczegóły są jeszcze nieistotne (i w rzeczywistości
mogłyby zostać później w prosty sposób zmienione).
3. Ten model jest bardziej wymowny. Dodanie Delivery Specification
przekazuje jednoznacznie, że dokładne znaczenie transportu ładunku
(Cargo) nie jest jeszcze określone, jednak w tym celu musi zostać
osiągnięty cel określony w specyfikacji podróży (Delivery Specifi-
cation).
Rola (role) rozróżnia różne funkcje pełnione przez klienta w operacji
logistycznej. Jeden z nich może być „nadawcą”, inny „odbiorcą”, a jeszcze
inny „płatnikiem” itd. Ponieważ dla danego ładunku tylko jeden klient
(Customer) może pełnić daną rolę, ta asocjacja nabiera kwalifikacji wiele
do jednego zamiast wiele do wielu. Rola mogłaby zostać zaimplemento-
wana jako prosty łańcuch znakowy lub też mogłaby być klasą, gdyby ocze-
kiwane było inne jej zachowanie.

PREZENTACJA SYSTEMU LOGISTYCZNEGO DLA ŁADUNKU 197

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.

198 ROZDZIAŁ 7. UŻYCIE JĘZYKA — PRZYKŁAD ROZSZERZONY

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.

Rozróżnianie ENCJI oraz WARTOŚCI


Omawiając po kolei każdy obiekt, poszukamy tożsamości, która musi być
śledzona, oraz prostej wartości ją reprezentującej. Rozpocznijmy od oczy-
wistych klas, a następnie zajmijmy się tymi bardziej wieloznacznymi.
Customer (ang. klient)
Rozpocznijmy od prostej klasy. Obiekt Customer reprezentuje osobę lub
firmę, czyli encję w zwyczajowym tego słowa znaczeniu. Obiekt ten zde-
cydowanie ma tożsamość, która ma znaczenie dla użytkownika, dlatego
w modelu będzie ENCJĄ. W jaki sposób ją śledzić? W niektórych przy-
padkach właściwy mógłby być numer identyfikacji podatkowej (TaxID),
jednak firma międzynarodowa mogłaby go nie używać. To pytanie będzie
wymagać konsultacji z ekspertem dziedzinowym. Omówiliśmy ten problem
z osobą z firmy logistycznej i odkryliśmy, że firma posiada już bazę danych
klientów, w której każdy z nich ma identyfikator nadany podczas pierwszego
kontaktu. Ten identyfikator jest już używany w firmie. Zastosowanie go
w naszym programie wymusi więc ciągłość tożsamości pomiędzy oboma
systemami. Początkowo identyfikator będzie wprowadzany ręcznie.
Cargo (ang. ładunek)
Ponieważ musi istnieć możliwość rozróżnienia dwóch paczek, obiekty
Cargo będą ENCJAMI. W praktyce wszystkie firmy logistyczne do każdej
części ładunku przypisują identyfikator używany do śledzenia. Będzie

ROZRÓŻNIANIE ENCJI ORAZ WARTOŚCI 199

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

200 ROZDZIAŁ 7. UŻYCIE JĘZYKA — PRZYKŁAD ROZSZERZONY

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

Role (rola) oraz inne atrybuty


Rola mówi coś o asocjacji, której dotyczy, jednak nie ma historii ani cią-
głości. Jest to WARTOŚĆ i mogłaby być współdzielona przez różne
asocjacje Ładunku (Cargo)/Klienta (Customer).
Inne atrybuty, w rodzaju dat lub nazw, są również WARTOŚCIAMI.

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

PROJEKTOWANIE ASOCJACJI W DZIEDZINIE LOGISTYKI MORSKIEJ 201

699b15244ae879c728b313782703a4d7
6
Rysunek 7.2.
Niektóre asocjacje
mają ograniczoną
kierunkowość

W naszym modelu istnieje tylko jedna cykliczna referencja. Obiekt


Cargo zna obiekt Delivery History, który zawiera serię obiektów Han-
dling Events, a te z kolei wskazują z powrotem na Cargo. Tego rodzaju
cykliczne referencje istnieją w wielu dziedzinach i są niekiedy konieczne
również w projekcie. Jednak ich utrzymanie jest skomplikowane. Wybory
dokonane podczas implementacji mogą pozwolić uniknąć sytuacji, w której
ta sama informacja jest przechowywana w dwóch różnych miejscach,
wymuszających konieczność ich synchronizacji. W tym przypadku możemy
jednak zrobić prostą implementację początkowego prototypu (w Javie),
umieszczając w klasie Delivery History obiekt klasy List, zawierający
obiekty klasy Handling Events. W pewnym momencie jednak z pew-
nością będziemy chcieli porzucić kolekcję na rzecz zapytania do bazy
danych z identyfikatorem obiektu Cargo użytym w charakterze klucza.
Powrócimy do tego tematu w trakcie wyboru REPOZYTORIUM. Jeżeli
zapytanie do odczytania historii jest stosunkowo rzadko stosowane, takie
rozwiązanie powinno skutkować dość dobrą wydajnością, uproszczonym

202 ROZDZIAŁ 7. UŻYCIE JĘZYKA — PRZYKŁAD ROZSZERZONY

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.

GRANICE AGREGATU 203

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-

204 ROZDZIAŁ 7. UŻYCIE JĘZYKA — PRZYKŁAD ROZSZERZONY

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.

WYBÓR REPOZYTORIÓW 205

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.

Przykładowa funkcjonalność aplikacji


— zmiana miejsca przeznaczenia ładunku
Od czasu do czasu klient (Customer) dzwoni i mówi: „Och nie! Kaza-
liśmy wysłać nasz ładunek do Szczecina, jednak tak naprawdę potrzebu-
jemy go w Gdańsku”. Oczywiście, jesteśmy od tego, aby służyć klientowi,
więc system też powinien udostępniać taką zmianę.
Klasa Delivery Specification jest WARTOŚCIĄ, dlatego najpro-
ściej byłoby ją wyrzucić i wziąć nową, a następnie użyć odpowiedniej
metody w obiekcie Cargo, aby zastąpić starą wartość nową.

Przykładowa funkcjonalność aplikacji


— powtórzenie operacji
Użytkownicy twierdzą, że powtarzające się rezerwacje od tego samego
klienta (Customer) wydają się podobne, dlatego chcieliby użyć istnieją-
cych ładunków (Cargo) jako prototypów nowych obiektów. Aplikacja
umożliwi użytkownikom odnalezienie ładunku (Cargo) w REPOZYTO-
RIUM, a następnie użycie polecenia do utworzenia nowego obiektu Cargo
na podstawie wybranego obiektu. W tym celu użyjemy wzorca PROTOTYP
(książka Gammy z roku 1995).
Klasa Cargo jest ENCJĄ, a jednocześnie korzeniem AGREGATU.
Dlatego jej obiekty musimy kopiować bardzo ostrożnie. Musimy zastano-
wić się, co powinno się stać z każdym obiektem lub atrybutem umieszczo-
nym w granicach AGREGATU.
Zajmijmy się każdym z nich oddzielnie:
 Historia podróży (Delivery History). Powinniśmy utworzyć nowy,
pusty obiekt, ponieważ poprzednia historia podróży nie ma zasto-
sowania. To bardzo częsty przypadek dla ENCJI w granicach
AGREGATU.
 Role klientów (Customer roles). Powinniśmy skopiować mapę (lub
inną kolekcję), która przechowuje klucze odwołań do obiektów Cus-
tomer, zachowując te klucze, ponieważ bardzo prawdopodobne jest,

206 ROZDZIAŁ 7. UŻYCIE JĘZYKA — PRZYKŁAD ROZSZERZONY

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)

Moglibyśmy też chcieć utworzyć metodę w samodzielnej FABRYCE,


jak na przykład:
public Cargo newCargo(Cargo prototype, String newTrackingID)

Samodzielna FABRYKA mogłaby również hermetyzować proces two-


rzenia nowego ID (generowanego automatycznie) dla nowego obiektu
klasy Cargo, do czego potrzebowałaby tylko jednego argumentu:
public Cargo newCargo(Cargo prototype)

Wynik zwracany przez dowolną z tych FABRYK byłby identyczny:


obiekt klasy Cargo z pustym atrybutem Delivery History oraz Delivery
Specification o wartości null.
Dwukierunkowa asocjacja pomiędzy klasami Cargo oraz Delivery
History oznacza, iż żaden z obiektów tych klas nie będzie kompletny bez
wskazywania na swój odpowiednik, dlatego też muszą zostać utworzone
jednocześnie. Pamiętaj, że obiekt klasy Cargo jest korzeniem AGREGATU,

TWORZENIE OBIEKTÓW 207

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();
}

Efektem działania powyższego kodu mógłby być nowy obiekt Cargo


z nowym obiektem Delivery History, wskazującym ponownie na ten sam
obiekt Cargo. Konstruktor klasy Delivery History jest używany wyłącznie
przez korzeń AGREGATU, czyli klasę Cargo, dlatego złożenie ładunku
jest zahermetyzowane.

Dodanie operacji obsługi


Za każdym razem, gdy ładunek jest obsługiwany w rzeczywistym otocze-
niu, jakiś użytkownik dodawałby obiekt reprezentujący operację obsługi
(Handling Event), używając w tym celu aplikacji do rejestrowania incy-
dentów. Każda klasa musi posiadać proste konstruktory. Ponieważ klasa
Handling Event jest ENCJĄ, wszystkie jej atrybuty określające jej tożsa-
mość muszą być przekazywane do konstruktora. Jak już poprzednio to
omówiliśmy, klasa Handling Event jest w sposób jednoznaczny okre-
ślona przez połączenie identyfikatora jej ładunku (Cargo), czasu operacji
(ang. completion time) oraz jej rodzaju (ang. event type). Jedynym innym atry-
butem klasy Handling Event jest asocjacja z obiektem operacji spedy-
cyjnej (Carrier Movement), którego niektóre rodzaje operacji obsługi
nawet nie posiadają. Podstawowy konstruktor tworzący poprawne operacje
obsługi mógłby wyglądać następująco:
public HandlingEvent(Cargo c, String eventType, Date timeStamp) {
handled = c;
type = eventType;
completionTime = timeStamp;
}

Atrybuty nieidentyfikujące ENCJI mogą zazwyczaj być dodawane


później. W tym przypadku wszystkie atrybuty klasy Handling Event będą
ustawiane w początkowej transakcji i nigdy nie będą podlegać zmianie
(za wyjątkiem przypadku ich korygowania z powodu błędu podczas wpro-
wadzania danych). Dlatego też wygodnym rozwiązaniem zwiększającym
wymowę klienta byłoby dodanie do klasy Handling Event prostej

208 ROZDZIAŁ 7. UŻYCIE JĘZYKA — PRZYKŁAD ROZSZERZONY

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;
}

Klasa Handling Event w modelu jest abstrakcją, która mogłaby


hermetyzować szereg specjalistycznych klas tego typu, począwszy od rozła-
dunku/załadunku, poprzez zapieczętowanie ładunku, jego przechowywanie
i wiele innych niezwiązanych ze spedytorem. Taka właściwość mogłaby
zostać zaimplementowana w postaci wielu podklas lub skomplikowanej
inicjalizacji, lub też obu tych sposobów jednocześnie. Dodając do klasy
bazowej (Handling Event) METODY FABRYCZNE dla każdego rodzaju
operacji, definiujemy abstrakcję dla operacji tworzenia instancji obiektu,
uwalniając klienta od wiedzy o właściwej implementacji. To FABRYKA
jest odpowiedzialna za wiedzę o rodzaju tworzonej klasy oraz sposobie jej
inicjalizacji.
Niestety, ten przypadek nie jest aż tak prosty. Cykliczność odwołań
od klasy Cargo do Delivery History, następnie Handling Event
i ponownie do klasy Cargo bardzo utrudnia tworzenie instancji. Klasa
Delivery History zawiera kolekcję obiektów Handling Events właści-
wych danemu ładunkowi (Cargo), co sprawia, że w ramach transakcji musi
do tej kolekcji zostać dodany nowy obiekt. W przypadku pominięcia tego
wskaźnika obiekt mógłby być niespójny.
Tworzenie zwrotnego wskaźnika mogłoby zostać zahermetyzowane
w FABRYCE (i zachowane w warstwie dziedziny, do której ona przy-
należy), jednak przyjrzyjmy się teraz alternatywnemu projektowi, który
będzie eliminował konieczność tej dziwnej interakcji między obiektami.

Przerwa na refaktoring — projekt


alternatywny AGREGATU Cargo
Modelowanie oraz projektowanie nie jest procesem jednokierunkowym.
Cały wysiłek zmierzałby donikąd, gdyby nie było częstego refaktoringu,
umożliwiającego wykorzystanie nowych odkryć w dziedzinie, mogących
ulepszyć zarówno model, jak i projekt.

PRZERWA NA REFAKTORING — PROJEKT ALTERNATYWNY AGREGATU CARGO 209

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

210 ROZDZIAŁ 7. UŻYCIE JĘZYKA — PRZYKŁAD ROZSZERZONY

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.

PRZERWA NA REFAKTORING — PROJEKT ALTERNATYWNY AGREGATU CARGO 211

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

Pamiętajmy, że ta nowa funkcjonalność („Co jest w tej operacji spedycyj-


nej?”) nie została jeszcze zdefiniowana i może się nawet nigdy nie poja-
wić, dlatego też nie powinniśmy poświęcać jej jeszcze zbyt wiele uwagi.
Tego rodzaju alternatywne wybory oraz kompromisy projektowe
występują wszędzie i tylko w tym małym uproszczonym systemie mogłyby
tworzyć wiele przykładów. Należy jednak pamiętać, że w samym modelu
istnieje pewna granica swobody. Poprzez świadome zamodelowanie
WARTOŚCI, ENCJI oraz AGREGATÓW zmniejszyliśmy wpływ tego
rodzaju zmian projektowych. Dajmy na to, w tym przykładzie wszystkie
zmiany są zahermetyzowane wewnątrz granic AGREGATU obiektu Cargo.
Co więcej, wymagane jest tylko dodanie repozytorium obiektów Handling
Event (Handling Event Repository), a nie zmiana ich samych (chociaż
w zależności od szczegółów szkieletu danego REPOZYTORIUM pewne
zmiany implementacyjne mogą być wymagane).

212 ROZDZIAŁ 7. UŻYCIE JĘZYKA — PRZYKŁAD ROZSZERZONY

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.

MODUŁY W MODELU LOGISTYKI MORSKIEJ 213

699b15244ae879c728b313782703a4d7
6
Rysunek 7.7.
Te moduły
nie przekazują
wiedzy dziedzinowej

214 ROZDZIAŁ 7. UŻYCIE JĘZYKA — PRZYKŁAD ROZSZERZONY

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

NOWA FUNKCJONALNOŚĆ — SPRAWDZANIE PRZYDZIAŁU 215

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

Łączenie dwóch systemów


System zarządzania sprzedażą (Sales Management System) nie został
jednak stworzony przy użyciu tego samego modelu, z którym pracujemy
obecnie. Gdyby nasza aplikacja do rezerwacji (Booking Application)
miała współpracować z nim bezpośrednio, wtedy musiałaby zawierać
projekt innego systemu, co utrudniałoby utrzymanie przejrzystego PRO-
JEKTU STEROWANEGO MODELEM i zagmatwałoby JĘZYK
WSZECHOBECNY. W zamian pozwólmy utworzyć jeszcze jedną klasę,
której zadaniem będzie dokonywanie tłumaczenia pomiędzy naszym
modelem a językiem systemu do zarządzania sprzedażą. Nie będzie to
jednak mechanizm ogólnego tłumaczenia. Będzie on jedynie wystawiać
funkcjonalności potrzebne przez naszą aplikację i tworzyć ich abstrakcję
w pojęciach naszego modelu dziedziny. Ta klasa będzie funkcjonować jako
WARSTWA ZAPOBIEGAJĄCA USZKODZENIU (ang. ANTICOR-
RUPTION LAYER), która zostanie dokładniej opisana w rozdziale 14.
Ponieważ jest to interfejs do systemu zarządzania sprzedażą, mogli-
byśmy najpierw myśleć o nim jako o „interfejsie do systemu zarządzania

216 ROZDZIAŁ 7. UŻYCIE JĘZYKA — PRZYKŁAD ROZSZERZONY

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.

Wzbogacanie modelu — segmentacja biznesu


Teraz, kiedy już pobieżnie opisaliśmy współpracę dwóch systemów,
powstaje pytanie, jakiego rodzaju interfejs moglibyśmy dostarczyć, aby
odpowiadał na pytanie, ile ładunku danego rodzaju może zostać zarezer-
wowane. Największym problemem wydaje się zdefiniowanie, co ozna-
cza „rodzaj” ładunku, ponieważ nasz model dziedziny nie wprowadził
jeszcze żadnej ich kategoryzacji. W systemie zarządzania sprzedażą rodzaje
ładunku są najzwyczajniej zestawem słów kluczowych dla kategorii i do
tej listy moglibyśmy dostosować nasze rodzaje ładunków. Moglibyśmy
na przykład jako argument przekazać kolekcję łańcuchów znakowych.
Niemniej jednak w tym momencie wykorzystamy inną możliwość: odtwo-
rzymy abstrakcję dziedziny innego systemu. W tym celu musimy wzbo-
gacić nasz model dziedziny, aby pomieścił wiedzę o kategoriach ładunku.
Aby tego dokonać, musimy przedyskutować temat z ekspertem dzie-
dzinowym.
Niekiedy (jak omówimy to w rozdziale 11.) wzorzec analityczny może
dać nam pomysł na rozwiązanie zagadnienia modelowania. Książka pt.
Analysis Patterns (Fowler 1996 r.) omawia wzorzec, który zajmuje się tym
rodzajem problemu, tj. SEGMENT PRZEDSIĘBIORSTWA (ang.
ENTERPRISE SEGMENT). Wzorzec ten jest zestawem wymiarów okre-
ślających sposób podziału biznesu. Mogłyby do nich należeć wszystkie

NOWA FUNKCJONALNOŚĆ — SPRAWDZANIE PRZYDZIAŁU 217

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

Klasa Allocation Checker będzie dokonywać tłumaczenia pomiędzy


tymi nowymi segmentami przedsiębiorstwa a nazwami kategorii w drugim
systemie. Klasa Cargo Repository będzie musiała również udostępniać
zapytanie na podstawie segmentu przedsiębiorstwa. W obu przypadkach
obiekt Enterprise Segment mógłby zostać wykorzystany do wykonania
operacji bez niszczenia hermetyzacji segmentu i komplikowania własnych
implementacji klas. (Zauważ, że klasa Cargo Repository odpowiada na
zapytanie, zwracając liczbę, a nie kolekcję instancji).
Z powyższym projektem związanych jest wciąż kilka problemów:
1. Aplikacji do rezerwacji przydzieliliśmy nowe zadanie, dodając nastę-
pującą regułę: „Ładunek jest akceptowany, jeżeli przestrzeń przy-
pisana do jego segmentu przedsiębiorstwa jest większa niż ilość
obecnie zarezerwowana oraz rozmiar nowego ładunku”. Wymu-
szanie reguły biznesowej jest odpowiedzialnością dziedziny i nie
powinno być wykonywane w warstwie aplikacji.

218 ROZDZIAŁ 7. UŻYCIE JĘZYKA — PRZYKŁAD ROZSZERZONY

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

Jedynym poważnym ograniczeniem wprowadzanym przez tego


rodzaju integrację będzie fakt, iż system zarządzania sprzedażą nie będzie
mógł wykorzystać wymiarów, których klasa Allocation Checker nie
będzie w stanie zamienić na segmenty przedsiębiorstwa. (Bez zastoso-
wania wzorca SEGMENT PRZEDSIĘBIORSTWA to samo ograniczenie
zmusiłoby system sprzedaży do stosowania jedynie tych wymiarów, które
mogłyby działać w zapytaniu umieszczonym w klasie Cargo Repository.
Takie podejście byłoby możliwe, jednak wtedy system sprzedaży miałby
wpływ na inne części dziedziny. W naszym rozwiązaniu klasa Cargo
Repository musi zostać zaprojektowana jedynie w taki sposób, aby obsłu-
giwać segment przedsiębiorstwa, natomiast jakiekolwiek zmiany w sys-
temie sprzedaży sięgać będą jedynie do klasy Allocation Checker, która
na pierwszym miejscu była stworzona jako FASADA).

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

NOWA FUNKCJONALNOŚĆ — SPRAWDZANIE PRZYDZIAŁU 219

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

Ostatnie ważne pytanie projektowe: Dlaczego nie przypisać klasie Cargo


odpowiedzialności za zdefiniowanie segmentów przedsiębiorstwa?
Na pierwszy rzut oka rozwiązanie, w którym wszystkie potrzebne do
tego dane znajdowałyby się w Cargo, jako jeden z jej atrybutów, mogłoby
wydawać się eleganckie. Niestety, nie jest to takie proste. Segmenty przed-
siębiorstwa są określane dowolnie, zgodnie z podziałami przydatnymi dla
strategii biznesowej. Do różnych celów te same ENCJE mogłyby zostać
podzielone na segmenty w różny sposób. Tworzymy segment dla kon-
kretnego ładunku na potrzeby przydzielania rezerwacji, jednak segment
przedsiębiorstwa mógłby mieć zupełnie inną postać na przykład na potrzeby
obliczania podatków. Przypisanie segmentów przedsiębiorstwa mogłoby
nawet ulec zmianie w przypadku, gdyby z powodu nowej strategii sprzeda-
żowej została zmieniona konfiguracja segmentu zarządzania sprzedażą.
Z tego powodu klasa Cargo musiałaby wiedzieć o klasie Allocation
Checker, która jest zdecydowanie poza pojęciową odpowiedzialnością

220 ROZDZIAŁ 7. UŻYCIE JĘZYKA — PRZYKŁAD ROZSZERZONY

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.

OSTATECZNA WERSJA 221

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

224 CZĘŚĆ III

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

REFAKTORYZACJA KU GŁĘBSZEMU ZROZUMIENIU 225

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.

226 CZĘŚĆ III

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.

Model dogłębny/projekt elastyczny


Wobec potrzeby stałej refaktoryzacji także sam model musi wspierać zmiany.
W rozdziale 10. przyjrzymy się sposobom umożliwiającym stworzenie pro-
jektu, z którym łatwo będzie się pracować nie tylko tym, którzy będą do
niego wprowadzać zmiany, lecz również tym, którzy będą go integrować
z innymi częściami systemu.
Pewne cechy charakterystyczne projektu czynią go łatwiejszym do
zmiany oraz fizycznego zastosowania. Nie są one skomplikowane, lecz
często stanowią wyzwanie. Projekt elastyczny oraz sposoby na jego osią-
gnięcie będą stanowić temat rozdziału 10.
Na szczęście, wielokrotne przekształcanie modelu oraz kodu (pod
warunkiem że każda zmiana odzwierciedla nowe zrozumienie dziedziny)
prowadzi do zwiększenia elastyczności tych miejsc, w których zmiany są
najbardziej wymagane, oraz ułatwienia najczęściej wykonywanych czyn-
ności. Na przykład dobrze dopasowana rękawiczka staje się elastyczna
w miejscach zginania się palców, podczas gdy w innych zachowuje swoją
odporność i ochronę. Dlatego też pomimo iż w ten rodzaj podejścia do
modelowania oraz projektowania wpisanych jest wiele prób i błędów, to
jednak zmiany stają się coraz prostsze do wprowadzenia i zbliżają nas do
osiągnięcia projektu elastycznego.
Oprócz ułatwiania samych zmian projekt elastyczny przyczynia się
również do ulepszenia modelu. PROJEKT STEROWANY MODELEM
opiera się na dwóch podporach. Model dogłębny umożliwia utworzenie
wymownego projektu. Jednocześnie ten sam projekt może w rzeczywi-
stości wspomagać sam proces odkrywania modelu, pod warunkiem że jest
na tyle elastyczny, aby umożliwiać programiście eksperymentowanie oraz
ukazywać mu rezultaty tych eksperymentów. Ten rodzaj sprzężenia zwrot-
nego jest niezbędny, ponieważ model, którego poszukujemy, nie jest tylko
ładnym zestawem pomysłów, lecz stanowi podstawę systemu.

REFAKTORYZACJA KU GŁĘBSZEMU ZROZUMIENIU 227

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.

228 CZĘŚĆ III

699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 8.

Moment
przełomowy

Z wrot z refaktoryzacji nie jest liniowy. Najczęściej w przypadku


niewielkiego wysiłku zwrot jest marginalny, jedynie z niewieloma udo-
skonaleniami. Stanowią one tylko powierzchowną ochronę przed skost-
niałą starzyzną wewnątrz. Jednak najczęściej najważniejsze odkrycia nastę-
pują niespodziewanie i szokują cały projekt.
Powoli i konsekwentnie zespół przyswaja wiedzę i przetwarza ją,
uwzględniając w modelu. Modele dogłębne mogą powstawać stopniowo
poprzez sekwencję wielu małych refaktoryzacji — tu jeden obiekt, tam
zmieniona asocjacja lub przeniesiona odpowiedzialność.
Bardzo często stałe wysiłki w stronę refaktoryzacji przygotowują
podłoże pod coś znacznie mniej zwyczajnego. Każde udoskonalenie kodu

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

230 ROZDZIAŁ 8. MOMENT PRZEŁOMOWY

699b15244ae879c728b313782703a4d7
6
Rysunek 8.1.
Model zakładający
stały udział
pożyczkodawców

Zauważyliśmy jednak pewne niepokojące oznaki. Musieliśmy sobie


radzić z nieoczekiwanymi wymaganiami komplikującymi projekt. Jednym
z przykładów było rodzące się zrozumienie, że udziały w „linii kredyto-
wej” były jedynie wskazówką do partycypacji w danej pożyczce. Kiedy
pożyczkobiorca potrzebuje swoich pieniędzy, wtedy lider syndykatu wzywa
wszystkich członków do udostępnienia ich udziałów.
Po wezwaniu inwestorzy zazwyczaj sięgają do swoich portfeli do
wysokości udziału, jednak bardzo często negocjują również z innymi
członkami syndykatu i inwestują mniej (lub więcej). Tę regułę udało nam
się wychwycić w modelu, dodając do niego obiekt reprezentujący korekty
pożyczki — Loan Adjustment.
Wraz z odkrywaniem reguł różnych transakcji tego rodzaju udosko-
nalenia modelu umożliwiają nam nadążanie z modelem za wiedzą. Nie-
mniej jednak stale zwiększała się złożoność, a my nie czuliśmy, abyśmy
szybko zmierzali do naprawdę porządnej funkcjonalności.
Jeszcze więcej problemów sprawiały nam subtelne niespójności
w zaokrągleniach, których nie byliśmy w stanie wytropić wraz ze stale
zwiększającą się złożonością algorytmów. Oczywiście, w przypadku kon-
traktów o wielkości 100 milionów lub większej nikt nie przejmuje się gro-
szami, jednak bankierzy zazwyczaj nie ufają oprogramowaniu, które nie
jest w stanie skrupulatnie się ich doliczyć. Zaczęliśmy podejrzewać, że
nasze trudności spowodowane były podstawowymi problemami z pro-
jektem.

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

HISTORIA PEWNEGO PRZEŁOMU 231

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

232 ROZDZIAŁ 8. MOMENT PRZEŁOMOWY

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.

HISTORIA PEWNEGO PRZEŁOMU 233

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

Naszkicowałem również nowy model pożyczki.


W modelu tym nie występowały już dłużej żadne obiekty wyspe-
cjalizowane w obsłudze udziałów w linii kredytowej (Facility) lub po-
życzce (Loan). Oba te udziały zostały podzielone na bardziej intuicyjne
rozkłady udziałów (Share Pie). Tego rodzaju uogólnienie umożliwiło
nam wprowadzenie „matematyki udziałów”, zdecydowanie upraszczającej
obliczenia udziałów w dowolnej transakcji, a także czyniącej je bardziej
wyrazistymi, precyzyjnymi, jak również możliwymi do połączenia.

234 ROZDZIAŁ 8. MOMENT PRZEŁOMOWY

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!

HISTORIA PEWNEGO PRZEŁOMU 235

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

236 ROZDZIAŁ 8. MOMENT PRZEŁOMOWY

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.

Epilog — potok nowych spostrzeżeń


Wystąpienie w projekcie momentu przełomowego ukazało nam światełko
nadziei, jednak nie był to koniec historii. Model pogłębiony stworzył
niespodziewaną możliwość wzbogacenia aplikacji oraz uproszczenia jej
projektu.
Już po kilku tygodniach od wprowadzenia wersji programu korzy-
stającej z modelu z rozkładem udziałów zauważyliśmy pewien jego dodat-
kowy aspekt, komplikujący cały projekt. Brakowało ważnej ENCJI. Jej
brak spowodował konieczność przejęcia na siebie jej odpowiedzialności
przez inne obiekty. W szczególności w dziedzinie występowały istotne
reguły dotyczące sposobu wypłat pożyczki, wprowadzania opłat itp. Cała
ta logika była wciśnięta w obiekty Facility (linia kredytowa) oraz Loan
(pożyczka). Te problemy projektowe były ledwo widoczne przed wprowa-
dzeniem nowego podejścia z obiektem Share Pie (rozkład udziałów),
jednak stały się oczywiste po stworzeniu bardziej przejrzystego modelu.
Zaczęliśmy dostrzegać różne pojęcia pojawiające się w dyskusji, nie-
obecne w modelu — na przykład „transakcja” (oznaczająca transakcję
finansową). Zrozumieliśmy, że wszystkie one były wymuszane przez te
wszystkie skomplikowane metody.
Podejmując się jeszcze raz procesu podobnego do opisanego wcze-
śniej (chociaż tym razem szczęśliwie bez tak dużej presji czasowej), odkry-
liśmy jeszcze więcej spostrzeżeń na temat dziedziny i jeszcze bardziej
pogłębiliśmy jej model. Ten nowy model odkrył ukryte dotychczas pojęcia,
w rodzaju transakcji (Transaction), a jednocześnie uprościł pojęcie pozycji
(Position), która stanowiła abstrakcję obejmującą linię kredytową oraz
pożyczkę. Zdefiniowanie różnorodnych transakcji wraz z ich regułami,
procedurami negocjacyjnymi oraz procesami zatwierdzania stało się już
banalnie proste, a kod był relatywnie przejrzysty i mówił sam za siebie.

238 ROZDZIAŁ 8. MOMENT PRZEŁOMOWY

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

EPILOG — POTOK NOWYCH SPOSTRZEŻEŃ 239

699b15244ae879c728b313782703a4d7
6
240 ROZDZIAŁ 8. MOMENT PRZEŁOMOWY

699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 9.

Odkrywanie
pojęć niejawnych

M odelowanie dogłębne brzmi wspaniale, lecz w jaki sposób


w rzeczywistości go użyć? Model dogłębny jest tak potężny, ponieważ
zawiera istotne pojęcia oraz abstrakcje, przedstawiające w zwięzły i ela-
styczny sposób zasadniczą wiedzę o działaniach użytkowników, proble-
mach oraz ich rozwiązaniach. Pierwszą czynnością będzie zaprezentowanie
w modelu podstawowych pojęć dziedziny. Czas na jego udoskonalenie
przyjdzie później, wraz ze stopniowym cyklicznym przetwarzaniem wiedzy
oraz refaktoryzacją. Jednak sam proces przyspieszy naprawdę dopiero
w chwili rozpoznania ważnego zagadnienia oraz jego bezpośredniego
wyrażenia w modelu oraz projekcie.
Wiele modyfikacji modeli dziedziny wraz z towarzyszącym
im kodem następuje w chwili, gdy programiści rozpoznają pewne
pojęcie dotychczas ukryte w dyskusji lub obecne w projekcie w spo-
sób niejawny, a następnie zaprezentują je w modelu w sposób bez-
pośredni przy użyciu jednego lub więcej obiektów albo relacji.
Od czasu do czasu tego rodzaju przekształcenie niejawnego pojęcia
do jego postaci bezpośredniej stanowi przełom prowadzący do modelu
dogłębnego. Częściej jednak moment przełomowy następuje w chwili
późniejszej, po ujawnieniu w modelu szeregu ważnych pojęć, po wielo-
krotnych operacjach refaktoryzacji modyfikujących ich odpowiedzialności,
zmieniających ich relacje z innymi obiektami, a nawet niekiedy ich na-
zwy. Ostatecznie wszystko ma znaczenie, lecz sam proces rozpoczyna
się od rozpoznania ukrytych w pewnej postaci nowych pojęć, jakkolwiek
postać ta byłaby niedoskonała.

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

242 ROZDZIAŁ 9. ODKRYWANIE POJĘĆ NIEJAWNYCH

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?

WYCIĄGANIE POJĘĆ 243

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

244 ROZDZIAŁ 9. ODKRYWANIE POJĘĆ NIEJAWNYCH

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.

Ekspert: Dokładnie. To wygląda bardzo dobrze. Dla każdego etapu podróży


(Leg) chciałbym zobaczyć identyfikator podróży, miejsce załadunku
(from) i rozładunku (to), a także czas.
Programista: W takim razie gdy tylko utworzymy obiekt etapu (Leg),
będę mógł pobrać czasy z rozkładu podróży. Możemy też uczynić
obiekt planu podróży (Itinerary) naszym głównym obiektem dla
aplikacji do wsparcia operacji. Co więcej, będziemy mogli zmody-
fikować raport planu podróży w taki sposób, aby też z tego korzy-
stał, co sprawi, że logika dziedzinowa powróci znów do warstwy
dziedziny.
Ekspert: Nie nadążam za tym wszystkim, lecz masz rację, mówiąc, że
dwa główne przypadki użycia planu podróży to raport w aplikacji
do rezerwacji oraz aplikacja do wsparcia operacji.
Programista: Hej! Możemy przecież sprawić, aby interfejs usługi trasowa-
nia (Routing Service) zwrócił nam obiekt planu podróży (Itinerary),
zamiast umieszczać te dane w tabeli bazodanowej. W ten sposób nasz
silnik trasowania nie będzie musiał wiedzieć nic o naszych tabelach.
Ekspert: Nie rozumiem...
Programista: To znaczy zmienię program w ten sposób, aby silnik tra-
sowania zwracał plan podróży. Następnie będzie on mógł być zapi-
sany w bazie danych przez aplikację do rezerwacji, dokładnie w tym
samym miejscu, co reszta danych o rezerwacji.
Ekspert: To znaczy, że teraz nie jest właśnie w ten sposób?
Programista wyszedł, aby porozmawiać z innymi członkami zespołu
zaangażowanymi w proces trasowania. Następnie zajęli się zmianami do
modelu oraz ich wpływem na projekt, prosząc ekspertów dziedzinowych
o poradę, gdy tylko było to potrzebne. Ostatecznie wrócili z diagramem
z rysunku 9.3.

WYCIĄGANIE POJĘĆ 245

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.

246 ROZDZIAŁ 9. ODKRYWANIE POJĘĆ NIEJAWNYCH

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.

Analiza dziwnej implementacji


Pojęcie, którego będziesz potrzebować, nie będzie zawsze unosić się
swobodnie na powierzchni, wyłaniając się z przeprowadzonych kon-
wersacji lub dostępnych dokumentów. Być może będziesz zmuszony,
aby nieco więcej pogrzebać i coś odkryć samemu. Dobrymi kandydatami
do tego rodzaju zabiegów są wszystkie niezgrabnie zaprojektowane miejsca
w projekcie — miejsca, w których procedury wykonują skomplikowane,
trudne do wyjaśnienia czynności, a także miejsca, w których nowe wy-
magania wydają się zwiększać ich złożoność.
Niekiedy trudno może być nawet rozpoznać, że brakuje jakiegoś
pojęcia. Możesz dysponować wszystkimi obiektami wykonującymi całe
zadanie, jednak niektóre z ich odpowiedzialności mogą wydawać się
dziwne. A nawet jeżeli uświadomisz sobie, że czegoś brakuje, umknąć może
Ci modelowe rozwiązanie.
Dlatego do poszukiwań będziesz musiał aktywnie włączyć eksper-
tów dziedzinowych. Jeżeli będziesz mieć szczęście, mogą oni polubić
zabawę z pomysłami oraz eksperymentowanie z modelem. W przeciw-
nym razie może Twoi znajomi programiści będą mieć pewne pomysły,
a ekspertów użyjesz tylko do sprawdzenia nowych rozwiązań, bacznie
obserwując dyskomfort lub podziw pojawiający się na ich twarzach.

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

WYCIĄGANIE POJĘĆ 247

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

248 ROZDZIAŁ 9. ODKRYWANIE POJĘĆ NIEJAWNYCH

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.

Ekspert: Sadzę, że ma to sens. Jednak właściwie przesunęłaś to z jedne-


go miejsca na inne.
Programistka: Za wyjątkiem tego, że teraz kalkulator odsetkowy śledzi
jedynie zarobione odsetki, a obiekt płatności (Payment) trzyma swoje
liczby oddzielnie. Wprawdzie nie uprościło to znacznie całego modelu,
lecz czyż nie tak właśnie działa w praktyce wasz biznes?
Ekspert: Rozumiem. Czy moglibyśmy dysponować również historią
odsetek, podobnie jak historią płatności (Payment History)?
Programistka: Tak, to zostało właśnie zażądane jako nowa funkcjonalność.
Jednak mogłoby to zostać dodane do oryginalnego projektu.

WYCIĄGANIE POJĘĆ 249

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,

250 ROZDZIAŁ 9. ODKRYWANIE POJĘĆ NIEJAWNYCH

699b15244ae879c728b313782703a4d7
6
Rysunek 9.7.

jak wiele duplikacji występuje w kalkulatorze opłat (Fee Calculator).


Co więcej, teraz inne rodzaje opłat mogą być dodane w prosty sposób.
Ekspert: Tak, wcześniejsze obliczenia były poprawne, lecz teraz rozu-
miem wszystko lepiej.
Ponieważ klasy kalkulatora (Calculator) nie były bezpośrednio zwią-
zane z innymi częściami projektu, ich refaktoryzacja była dość prostą
czynnością. Programistka była w stanie w kilka godzin napisać od nowa
testy jednostkowe dla nowego języka, a nowy projekt działał już pod koniec
następnego dnia. Ostatecznie model wyglądał następująco:

Rysunek 9.8.
Głębszy model
po refaktoryzacji

W zrefaktoryzowanej aplikacji nocny skrypt wsadowy prosi każdy


obiekt reprezentujący aktywa (Asset) o wywołanie funkcji calculateAc
crualsThroughDate(). Zwracana wartość jest kolekcją obiektów repre-
zentujących sumy przyrostowe (Accruals), których każda wartość jest
dodawana do wskazanej księgi.

WYCIĄGANIE POJĘĆ 251

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.

Rozmyślanie nad sprzecznościami


Różni eksperci dziedzinowi na podstawie swojego doświadczenia oraz
potrzeb widzą różne rzeczy w odmienny sposób. Nawet ta sama osoba
może dostarczyć informacje, które po uważnej analizie będą ze sobą
logicznie niespójne. Tego rodzaju nieznośne sprzeczności, które cały czas
napotykamy w procesie analizy wymagań, mogą być w przypadku modeli
poważniejszym problemem. Niektóre z nich wynikają najzwyczajniej
z odmian w terminologii, a inne z nieporozumień. Jednak wciąż pozo-
staje przypadek, w którym dwa stwierdzenia ekspertów mogą być ze sobą
logicznie sprzeczne.
Astronom Galileo podał kiedyś przykład paradoksu. Dowód przepro-
wadzony ze zmysłów jasno wskazuje, że Ziemia jest nieruchoma —
w końcu ludzie nie są wyrzucani w powietrze i nie spadają. Jednak Koper-
nik sformułował istotną tezę, że Ziemia porusza się dość szybko wokół
Słońca. Gdyby teza ta okazała się prawdziwa, mogłaby odkryć bardzo ważne
zasady działania przyrody.

252 ROZDZIAŁ 9. ODKRYWANIE POJĘĆ NIEJAWNYCH

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.

WYCIĄGANIE POJĘĆ 253

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.

— Finance and Accounting: How to Keep Your Books and Manage


Your Finances Without an MBA, a CPA or a Ph.D. autorstwa
Suzanne Caplan, wydana nakładem Adams Media w 2000 r.

W tym momencie programistka nie była zmuszona na nowo odkrywać


księgowości. Po krótkiej dyskusji z innym programistą mogła już przyjść
z gotowym modelem.

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.

254 ROZDZIAŁ 9. ODKRYWANIE POJĘĆ NIEJAWNYCH

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.

Oczywiście, nie jest to scenariusz alternatywny ani propozycja. Nawet


z drobiazgowym wsparciem ze strony ekspertów dziedzinowych opłaca
się sięgnąć do literatury, aby zdobyć choć część wiedzy teoretycznej z danej
dziedziny. Większość biznesów nie dysponuje modelami tak dopraco-
wanymi jak księgowość lub finanse, jednak w wielu z nich istnieją osoby
z wiedzą ekspercką, które już zorganizowały lub stworzyły abstrakcję
podstawowych praktyk w tym biznesie.
Jeszcze inną możliwością, która otworzyła się przed programistką, było
przeczytanie jakiejś książki, napisanej przez innego specjalistę w zakresie
tworzeniu oprogramowania z doświadczeniem w tej dziedzinie. Na przy-
kład zapoznanie się z rozdziałem 6. książki pt. Analysis Patterns: Reusable
Object Models (Fowler 1997) skierowałoby ją w całkiem innym kierunku,
niekoniecznie lepszym lub gorszym. Oczywiście, taka lektura nie przyniesie
gotowego rozwiązania. Mogłaby jednak odkryć kilka nowych punktów,
od których można by rozpocząć własne eksperymenty, a także przekazać
doświadczenia ludzi, którzy już przemierzali terytorium danej dziedziny.
Na pewno zaoszczędziłaby programistce konieczności odkrywania na nowo
koła. Więcej na ten temat opowiemy w rozdziale 11., „Stosowanie wzorców
analitycznych”.

Wielokrotne powtarzanie prób


Podane powyżej przykłady nie odzwierciedlają liczby prób i błędów zwią-
zanych z modelowaniem. Zanim znalazłbym w rozmowie przejrzysty
i w miarę przydatny wątek wart wypróbowania, mógłbym podążać za
pół tuzinem innych. Ostatecznie ten jeden udało mi się zastąpić później
przynajmniej jeszcze jeden raz, gdy na podstawie własnego doświadcze-
nia oraz przetwarzania wiedzy odkryłem inny, w lepszy sposób odwzoro-
wujący daną ideę. Osoba zajmująca się modelowaniem nie może sobie po-
zwolić na przywiązanie do swoich własnych pomysłów.

WYCIĄGANIE POJĘĆ 255

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.

W jaki sposób zamodelować


mniej oczywiste pojęcia
Paradygmat projektowania obiektowego zmusza nas do poszukiwania
i wynajdywania określonych pojęć. Nawet tak abstrakcyjne rzeczy jak
„przyrosty” stanowią wraz z wykonywanymi na nich akcjami istotę
większości modeli obiektowych. To właśnie są „podmioty i orzeczenia”,
o których mówi większość podstawowych książek na temat projektowa-
nia obiektowego. Jednak w modelu mogą zostać wyrażone bezpośrednio
również inne ważne rodzaje pojęć.
Wspomnę tu o trzech tego rodzaju kategoriach, które w chwili, gdy
zacząłem pracować z obiektami, nie były dla mnie oczywiste. Za każdym
razem, gdy nauczyłem się którejś z nich, moje projekty stawały się bar-
dziej wyraziste.

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

256 ROZDZIAŁ 9. ODKRYWANIE POJĘĆ NIEJAWNYCH

699b15244ae879c728b313782703a4d7
6
Rysunek 9.10.

if (contents + addedVolume > capacity) {


contents = capacity;
} else {
contents = contents + addedVolume;
}
}
}

Ta logika jest tak prosta, że wyrażenie reguły staje się oczywiste.


Jednak możemy sobie również w prosty sposób wyobrazić, że to ograni-
czenie gubi się w bardziej skomplikowanej klasie. Dlatego spróbujmy
wyrazić je w oddzielnej metodzie, o nazwie jasno i przejrzyście wyraża-
jącej znaczenie tego ograniczenia.
class Bucket {
private float capacity;
private float contents;

public void pourIn(float addedVolume) {


float volumePresent = contents + addedVolume;
contents = constrainedToCapacity(volumePresent);
}

private float constrainedToCapacity(float volumePlacedIn) {


if (volumePlacedIn > capacity) return capacity;
return volumePlacedIn;
}
}

Obie wersje tego kodu wymuszają wspomniane ograniczenie, jednak


druga ma zdecydowanie bardziej oczywistą relację z modelem (podsta-
wowe wymaganie PROJEKTU STEROWANEGO MODELEM). Ta bar-
dzo prosta reguła nawet w swojej oryginalnej postaci była zrozumiała, jed-
nak gdy wymagane reguły są bardziej złożone, zaczynają one przeważać
w obiekcie lub operacji, do której mają zastosowanie, tak jak każde nie-
jawne pojęcie. Refaktoryzacja ograniczenia w sposób umieszczający je
w jego własnej metodzie pozwala nam na nadanie mu pewnej celowości,
odkrywając jego nazwę, ujawniającą je w sposób bezpośredni w projekcie.
Będzie ono od tej pory nazwaną rzeczą, o której można dyskutować.
Takie podejście daje również ograniczeniu pewną przestrzeń do
wzrostu. Reguła bardziej złożona od tej przedstawionej powyżej mogłaby

W JAKI SPOSÓB ZAMODELOWAĆ MNIEJ OCZYWISTE POJĘCIA 257

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.

258 ROZDZIAŁ 9. ODKRYWANIE POJĘĆ NIEJAWNYCH

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

Zgódźmy się od razu na początku, że nie chcemy, aby procedury stano-


wiły dominujący aspekt naszego modelu. Obiekty mają na celu herme-
tyzację procedur i zmuszenie nas raczej do myślenia o ich celach oraz
intencjach.
Chciałbym teraz porozmawiać o procesach istniejących w dziedzinie,
które musimy odzwierciedlić w modelu. W chwili, gdy się uwidocznią,
zazwyczaj prowadzą do dziwnych projektów obiektowych.
Pierwszy przykład w tym rozdziale opisywał system firmy spedycyjnej,
który tworzył trasy dla ładunku. Cały proces trasowania miał znaczenie
biznesowe. Jednym ze sposobów umożliwiających wyrażenie takich pro-
cesów w sposób bezpośredni, wciąż jeszcze przy zachowaniu hermety-
zacji bardzo złożonych algorytmów, jest USŁUGA.
Gdy istnieje więcej niż jeden sposób na wyrażenie procesu, innym
podejściem będzie umieszczenie samego algorytmu lub jego kluczowej
części w samodzielnym obiekcie. Wybór pomiędzy procesami staje się
wyborem pomiędzy tymi obiektami, z których każdy reprezentuje inną
STRATEGIĘ (na zastosowanie STRATEGII w dziedzinie spojrzymy sze-
rzej w rozdziale 12.).
Sposób na odróżnienie procesu, który powinien być jawny, od tego,
który powinien być ukryty, jest bardzo prosty: Czy jest to coś, o czym
eksperci dziedzinowi wciąż dyskutują, czy jest to po prostu część mecha-
nizmu programu komputerowego?

Ograniczenia oraz procesy są dwiema szerokimi kategoriami pojęć mode-


lowych, które nie przychodzą szybko na myśl, kiedy programujemy, uży-
wając języka obiektowego. Jednak kiedy zaczniemy myśleć o nich jako
o elementach modelu, zaczną one znacznie wyostrzać cały projekt.

W JAKI SPOSÓB ZAMODELOWAĆ MNIEJ OCZYWISTE POJĘCIA 259

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.

260 ROZDZIAŁ 9. ODKRYWANIE POJĘĆ NIEJAWNYCH

699b15244ae879c728b313782703a4d7
6
SPECYFIKACJA

We wszystkich rodzajach aplikacji pojawiają się metody zawierające porów-


nania logiczne, stanowiące część reguł. Dopóki są one proste, możemy
obsłużyć je za pomocą prostych metod logicznych typu anIterator.has
Next() lub anInvoice.isOverdue(). W kodzie klasy Invoice w metodzie
isOverdue() zawarty jest algorytm obsługujący daną regułę, na przykład:
public boolean isOverdue() {
Date currentDate = new Date();
return currentDate.after(dueDate);
}

Jednak nie wszystkie reguły są aż tak proste. W tej samej klasie


Invoice inna reguła, anInvoice.isDelinquent(), prawdopodobnie zaczę-
łaby działanie od sprawdzenia, czy metoda anInvoice.isOverdue() zwraca
wartość „prawda”, jednak byłaby to jedynie pierwsza czynność z wielu
innych. Polityka obsługująca prolongatę terminu płatności mogłaby zależeć
od stanu rachunku klienta. Niektóre zaległe faktury mogłyby być gotowe
na drugie ponaglenie, podczas gdy inne mogłyby już zostać wysłane do
agencji windykacyjnej. Historia płatności klienta, polityka firmy doty-
cząca różnych linii produktowych — przejrzystość obiektu Invoice jako
żądania płatności mogłaby zostać za chwilę zagubiona w masie innych
reguł umieszczonych w kodzie. Z obiektem Invoice będzie również zwią-
zanych dużo innych relacji do klas i podsystemów dziedziny, niewspierają-
cych bezpośrednio tego podstawowego znaczenia.
W tej sytuacji programista, chcąc zachować klasę Invoice, mógłby
bardzo często decydować się na refaktoryzację kodu sprawdzającego regułę
i umieszczenie go w warstwie aplikacji (w tym przypadku w aplikacji zbie-
rającej rachunki). Spowoduje to oddzielenie reguł od warstwy dziedziny,
pozostawiając obiekty danych, pozbawione reguł istotnych w modelu biz-
nesowym. Dlatego też reguły powinny pozostać w warstwie dziedzinowej,
niemniej jednak nie mieszczą się one w zakresie testowanego obiektu
(w tym przypadku klasy Invoice). Również metody testujące wykorzy-
stywane przez same reguły mogą opierać się na wielu operatorach logicz-
nych, co utrudni ich zrozumienie.
Programiści używający paradygmatu programowania logicznego obsłu-
żyliby tę sytuację w nieco inny sposób. Tego rodzaju reguły wyrażone
zostałyby w postaci predykatów. Predykaty są funkcjami zwracającymi

W JAKI SPOSÓB ZAMODELOWAĆ MNIEJ OCZYWISTE POJĘCIA 261

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.

262 ROZDZIAŁ 9. ODKRYWANIE POJĘĆ NIEJAWNYCH

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

W JAKI SPOSÓB ZAMODELOWAĆ MNIEJ OCZYWISTE POJĘCIA 263

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.

Zastosowanie SPECYFIKACJI w implementacji


Istotną wartością SPECYFIKACJI jest fakt, iż unifikuje ona funkcjonal-
ność aplikacji, która może wydawać się całkiem odmienna. Możemy napo-
tkać na konieczność określenia stanu obiektu w przypadku jednego lub
więcej z tych trzech zastosowań:
1. W celu sprawdzenia, czy obiekt czegoś potrzebuje lub jest gotów na
pewne zastosowanie.

264 ROZDZIAŁ 9. ODKRYWANIE POJĘĆ NIEJAWNYCH

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

public DelinquentInvoiceSpecification(Date currentDate) {


this.currentDate = currentDate;
}

public boolean isSatisfiedBy(Invoice candidate) {


int gracePeriod =
candidate.customer().getPaymentGracePeriod();
Date firmDeadline =
DateUtility.addDaysToDate(candidate.dueDate(),

Rysunek 9.14.
Model używający
SPECYFIKACJI
do walidacji

W JAKI SPOSÓB ZAMODELOWAĆ MNIEJ OCZYWISTE POJĘCIA 265

699b15244ae879c728b313782703a4d7
6
gracePeriod);
return currentDate.after(firmDeadline);
}
}

Przypuśćmy teraz, że w przypadku gdy sprzedawca przyprowadzi


klienta z wymagalnymi rachunkami, będziemy musieli wyświetlić czer-
woną flagę. W tym celu musimy jedynie napisać w klasie klienta metodę
w rodzaju:
public boolean accountIsDelinquent(Customer customer) {
Date today = new Date();
Specification delinquentSpec =
new DelinquentInvoiceSpecification(today);
Iterator it = customer.getInvoices().iterator();
while (it.hasNext()) {
Invoice candidate = (Invoice) it.next();
if (delinquentSpec.isSatisfiedBy(candidate)) return true;
}
return false;
}

Wybór (lub odszukiwanie)


Walidacja powoduje przetestowanie konkretnego obiektu w celu spraw-
dzenia, czy spełnia on pewne kryteria, być może po to, aby klient mógł
wyciągnąć wnioski z tego wyniku testu. Inną powszechną potrzebą jest
wybór podzbioru kolekcji obiektów na podstawie określonych kryteriów.
W tym przypadku może zostać wykorzystana ta sama idea SPECYFIKACJI,
jednak problemy z implementacją będą odmienne.
Przypuśćmy, że jednym z wymagań aplikacji było wyświetlenie listy
wszystkich klientów z przeterminowanymi fakturami (Invoices). W teorii
mogłaby zostać wykorzystana zdefiniowana poprzednio specyfikacja faktur
przeterminowanych (Delinquent Invoice Specification), jednak w prak-
tyce jej implementacja musiałaby prawdopodobnie ulec zmianie. Aby uka-
zać, że idea jest ta sama, załóżmy najpierw, że liczba faktur jest mała i być
może są one już umieszczone w pamięci. W takim przypadku mogłaby
wciąż pasować prosta implementacja utworzona do testowania. Repozy-
torium faktur (Invoice Repository) mogłoby dysponować uogólnioną
metodą do wyboru faktur na podstawie SPECYFIKACJI:
public Set selectSatisfying(InvoiceSpecification spec) {
Set results = new HashSet();
Iterator it = invoices.iterator();
while (it.hasNext()) {
Invoice candidate = (Invoice) it.next();
if (spec.isSatisfiedBy(candidate)) results.add(candidate);
}
return results;
}

266 ROZDZIAŁ 9. ODKRYWANIE POJĘĆ NIEJAWNYCH

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

Ten pojedynczy wiersz kodu definiuje podstawową zasadę działania


operacji. Oczywiście, obiekty Invoice prawdopodobnie nie znajdują się
w pamięci. Może ich być tysiące. W typowym systemie biznesowym dane
znajdują się prawdopodobnie w relacyjnej bazie danych. Jak wskaza-
liśmy to w poprzednich rozdziałach, w przypadku zagłębiania się w te
wszystkie inne technologie zazwyczaj traci się model z pola widzenia.
Relacyjne bazy danych dysponują potężnymi możliwościami wyszu-
kiwania danych. W jaki sposób moglibyśmy skorzystać z tych możliwości,
aby efektywnie rozwiązać problem, zachowując wciąż model SPECY-
FIKACJI? PROJEKT STEROWANY MODELEM wymaga, aby model
związany był z implementacją, jednak wciąż pozostawia wolność wyboru
dowolnej implementacji, która w wierny sposób wychwytuje znaczenie
modelu. Szczęśliwie dla nas, SPECYFIKACJE w języku SQL tworzy się
w bardzo naturalny sposób.
Poniżej przedstawiony jest prosty przykład, w którym zapytanie
zahermetyzowane jest w tej samej klasie, co reguła testująca. Do obiektu
Invoice Specification dodana jest jedna metoda zaimplementowana
w podklasie o nazwie Delinquent Invoice Specification:
public String asSQL() {
return
"SELECT * FROM INVOICE, CUSTOMER" +
" WHERE INVOICE.CUST_ID = CUSTOMER.ID" +
" AND INVOICE.DUE_DATE + CUSTOMER.GRACE_PERIOD" +
" < " + SQLUtility.dateAsSQL(currentDate);
}

SPECYFIKACJE pasują dobrze do REPOZYTORIÓW, stanowią-


cych element składowy do udostępniania wyszukiwania obiektów dzie-
dziny oraz hermetyzacji interfejsu do bazy danych (patrz rysunek 9.15).
Jednak ten projekt wciąż ma pewne problemy. Najważniejszym z nich
jest przeniknięcie struktury tabel do WARSTWY DZIEDZINY. Obie te
rzeczy powinny być od siebie oddzielone w warstwie mapującej obiekty
dziedziny na tabele relacyjne. Taka niejawna duplikacja danych może nega-
tywnie wpłynąć na możliwość modyfikacji oraz utrzymywania obiektów
Invoice oraz Customer, ponieważ jakakolwiek zmiana w ich mapowa-
niu będzie musiała teraz być śledzona w więcej niż jednym miejscu.
Ten przykład obrazuje jednak, w jaki sposób można utrzymywać reguły
w jednym miejscu. Niektóre szkielety mapowania obiektowo-relacyjnego

W JAKI SPOSÓB ZAMODELOWAĆ MNIEJ OCZYWISTE POJĘCIA 267

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);
}

public String whereGracePeriodPast_SQL(Date aDate) {


return
"SELECT * FROM INVOICE, CUSTOMER" +
" WHERE INVOICE.CUST_ID = CUSTOMER.ID" +
" AND INVOICE.DUE_DATE + CUSTOMER.GRACE_PERIOD" +
" < " + SQLUtility.dateAsSQL(aDate);
}

268 ROZDZIAŁ 9. ODKRYWANIE POJĘĆ NIEJAWNYCH

699b15244ae879c728b313782703a4d7
6
public Set selectSatisfying(InvoiceSpecification spec) {
return spec.satisfyingElementsFrom(this);
}
}

Metoda asSql() w klasie Invoice Specification została zastąpiona


przez metodę satisfyingElementsFrom(InvoiceRepository), którą klasa
Delinquent Invoice Specification implementuje w następujący sposób
public class DelinquentInvoiceSpecification {
// W tym miejscu podstawowy kod klasy DelinquentInvoiceSpecification
public Set satisfyingElementsFrom(
InvoiceRepository repository) {
//Reguła wymagalności jest zdefiniowana jako:
// "okres karencji jest w danym dniu przekroczony"
return repository.selectWhereGracePeriodPast(currentDate);
}
}

W ten sposób zapytanie SQL jest umieszczane w REPOZYTORIUM,


podczas gdy SPECYFIKACJA kontroluje miejsce jego użycia. Reguły nie
wpisują się tak starannie w SPECYFIKACJĘ, jednak zachowana jest tam
podstawowa deklaracja tego, co składa się na przeterminowanie (to znaczy
przekroczony okres karencji).
REPOZYTORIUM dysponuje teraz bardzo specjalizowanym zapy-
taniem, które w tym przypadku zostanie z dużym prawdopodobieństwem
użyte. Będzie to rozwiązanie akceptowalne, jednak w zależności od stosun-
ku liczby faktur przeterminowanych od tych wymagalnych pośrednie roz-
wiązanie, korzystające z bardziej ogólnych metod REPOZYTORIUM,
mogłoby wciąż dawać dobre wyniki wydajnościowe i jednocześnie defi-
niować łatwiejszą do zrozumienia SPECYFIKACJĘ.
public class InvoiceRepository {

public Set selectWhereDueDateIsBefore(Date aDate) {


String sql = whereDueDateIsBefore_SQL(aDate);
ResultSet queryResultSet =
SQLDatabaseInterface.instance().executeQuery(sql);
return buildInvoicesFromResultSet(queryResultSet);
}

public String whereDueDateIsBefore_SQL(Date aDate) {


return
"SELECT * FROM INVOICE" +
" WHERE INVOICE.DUE_DATE" +
" < " + SQLUtility.dateAsSQL(aDate);
}

public Set selectSatisfying(InvoiceSpecification spec) {


return spec.satisfyingElementsFrom(this);
}
}

public class DelinquentInvoiceSpecification {

W JAKI SPOSÓB ZAMODELOWAĆ MNIEJ OCZYWISTE POJĘCIA 269

699b15244ae879c728b313782703a4d7
6
// W tym miejscu podstawowy kod DelinquentInvoiceSpecification

public Set satisfyingElementsFrom(


InvoiceRepository repository) {
Collection pastDueInvoices =
repository.selectWhereDueDateIsBefore(currentDate);
Set delinquentInvoices = new HashSet();
Iterator it = pastDueInvoices.iterator();
while (it.hasNext()) {
Invoice anInvoice = (Invoice) it.next();
if (this.isSatisfiedBy(anInvoice))
delinquentInvoices.add(anInvoice);
}
return delinquentInvoices;
}
}

Powyższy kod będzie stanowić obciążenie pod względem wydajno-


ściowym, ponieważ pobieramy więcej faktur, a następnie musimy je odfil-
trować w pamięci. To, czy będzie to stanowić akceptowalny koszt dla
łatwiejszej refaktoryzacji odpowiedzialności klasy, zależeć będzie całkowicie
od okoliczności. Istnieje wiele różnych sposobów implementacji interakcji
pomiędzy SPECYFIKACJAMI a REPOZYTORIAMI, umożliwiających
wykorzystanie możliwości środowiska programistycznego i utrzymanie na
miejscu podstawowych odpowiedzialności.
Niekiedy zapytania mogą być umieszczone w postaci procedur skła-
dowanych na serwerze w celu zwiększenia wydajności oraz, co bardziej
prawdopodobne, zwiększenia bezpieczeństwa. W takiej sytuacji SPECY-
FIKACJA mogłaby zawierać jedynie parametry dozwolone przez proce-
durę składowaną. Oprócz tego wyjątku, pomiędzy różnymi implemen-
tacjami nie istnieje różnica w modelu. Wybór implementacji może być
dowolny, za wyjątkiem tych miejsc, które są celowo ograniczone w modelu.
Ceną za taką dowolność jest skomplikowany sposób zapisywania oraz
utrzymywania zapytań.
Ta dyskusja jedynie pobieżnie dotyka wyzwań wiążących się z łącze-
niem SPECYFIKACJI z bazami danych i nie będę nawet próbować oma-
wiać wszystkich wyjątków, które mogą powstać. Chciałbym jedynie zazna-
czyć, jakiego rodzaju wyborów trzeba będzie dokonywać. Kilka technicznych
problemów związanych z projektowaniem REPOZYTORIÓW wraz ze
SPECYFIKACJAMI poruszają w książce Fowlera z roku 2003 Mee oraz
Hieatt.

Tworzenie na zamówienie (generowanie)


Jeżeli Pentagon chce zamówić nowy odrzutowiec, specjaliści rządowi
tworzą specyfikację. Może ona wymagać, aby samolot osiągał prędkość
Mach 2, miał zasięg 3000 km, kosztował nie więcej niż 50 milionów

270 ROZDZIAŁ 9. ODKRYWANIE POJĘĆ NIEJAWNYCH

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.

W JAKI SPOSÓB ZAMODELOWAĆ MNIEJ OCZYWISTE POJĘCIA 271

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

272 ROZDZIAŁ 9. ODKRYWANIE POJĘĆ NIEJAWNYCH

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

Jeżeli teraz napisalibyśmy te specyfikacje kontenerów, powinniśmy


być w stanie wziąć konfigurację zapakowanych kontenerów i sprawdzić,
czy spełnia ona ograniczenia.
Czy spełniona
Cecha kontenera Zawartość specyfikacja?
Opancerzony 10 kg trotylu

250 kg piasku
25 kg próbek biologicznych 
Amoniak 

Musiałaby zostać zaimplementowana metoda specyfikacji kontenera


(Container Specification) o nazwie isSatisfied(), sprawdzająca, czy
istnieją odpowiednie cechy kontenera (ContainerFeatures). Na przy-
kład SPECYFIKACJA dołączona do substancji wybuchowych poszuki-
wałaby cechy „opancerzony”.
public class ContainerSpecification {
private ContainerFeature requiredFeature;

public ContainerSpecification(ContainerFeature required) {


requiredFeature = required;
}

boolean isSatisfiedBy(Container aContainer){


return aContainer.getFeatures().contains(requiredFeature);
}
}

A to będzie przykładowy kod klienta tworzący materiał wybuchowy:


tnt.setContainerSpecification(
new ContainerSpecification(ARMORED));

W JAKI SPOSÓB ZAMODELOWAĆ MNIEJ OCZYWISTE POJĘCIA 273

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;
}

W tym momencie moglibyśmy napisać aplikację do monitoringu, która


brałaby bazę zapasów magazynowych, a następnie zgłaszała wszystkie
niebezpiecznie sytuacje.
Iterator it = containers.iterator();
while (it.hasNext()) {
Container container = (Container) it.next();
if (!container.isSafelyPacked())
unsafeContainers.add(container);
}

Oczywiście, nie jest to program, o którego napisanie byliśmy pro-


szeni. Lepiej byłoby poinformować ludzi z biznesu o takiej możliwości,
jednak w tym momencie dano nam zadanie zaprojektowania aplikacji do
pakowania. Dlatego mamy teraz dla niej pierwszy test. Nasze zrozumienie
dziedziny oraz modelu opartego na SPECYFIKACJI stawia nas w pozycji
umożliwiającej zdefiniowanie przejrzystego oraz prostego interfejsu dla
USŁUGI, która weźmie kolekcję obiektów typu Drums oraz Conta-
iner i zapakuje je wszystkie w zgodzie z regułami.
public interface WarehousePacker {
public void pack(Collection containersToFill,
Collection drumsToPack) throws NoAnswerFoundException;
/* ASERCJA: Pod koniec metody pack() specyfikacja ContainerSpecification
dla każdego obiektu Drum powinna być spełniona przez jego Container
Jeżeli nie może zostać znalezione rozwiązanie, powinien zostać zwrócony
wyjątek */
}

W tym momencie zadanie zaprojektowania zoptymalizowanej metody


rozwiązującej ograniczenia oraz spełniającej odpowiedzialności usługi
Packer zostało oddzielone od reszty aplikacji. Co więcej, te mechanizmy
nie będą zaciemniać tej części projektu, która wyraża model (sprawdź
„Deklaratywny styl projektowania” w rozdziale 10. oraz MECHANIZM
SPÓJNOŚCI w rozdziale 15.). Reguły zarządzające pakowaniem wciąż
jeszcze nie zostały wydobyte z obiektów dziedziny.

274 ROZDZIAŁ 9. ODKRYWANIE POJĘĆ NIEJAWNYCH

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

public boolean hasSpaceFor(Drum aDrum) {


return remainingSpace() >= aDrum.getSize();
}

public double remainingSpace() {


double totalContentSize = 0.0;
Iterator it = contents.iterator();
while (it.hasNext()) {
Drum aDrum = (Drum) it.next();
totalContentSize = totalContentSize + aDrum.getSize();
}
return capacity – totalContentSize;
}

public boolean canAccommodate(Drum aDrum) {


return hasSpaceFor(aDrum) &&
aDrum.getContainerSpecification().isSatisfiedBy(this);
}
}

public class PrototypePacker implements WarehousePacker {

W JAKI SPOSÓB ZAMODELOWAĆ MNIEJ OCZYWISTE POJĘCIA 275

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);
}
}

public Container findContainerFor(


Collection containers, Drum drum)
throws NoAnswerFoundException {
Iterator it = containers.iterator();
while (it.hasNext()) {
Container container = (Container) it.next();
if (container.canAccommodate(drum))
return container;
}
throw new NoAnswerFoundException();
}

Oczywiście, temu programowi brakuje jeszcze wielu pożądanych


funkcjonalności. Mógłby na przykład, zanim zacznie pakować niebezpieczne
chemikalia, pakować piasek w specjalne kontenery, a następnie wypełniać
przestrzeń. Oczywiście, nie optymalizuje również przychodów. Jednak i tak
problemy optymalizacyjne nie są nigdy rozwiązywane w sposób doskonały.
Przynajmniej przedstawiona implementacja spełnia utworzone dotych-
czas reguły.
Dysponowanie tym prototypem pozwala programistom aplikacji kon-
tynuować swoją pracę z pełną prędkością, a nawet integrować ją z syste-
mami zewnętrznymi. Zespół otrzymuje również komentarze zwrotne od
ekspertów dziedzinowych sprawdzających prototyp, co pozwala ostatecznie
potwierdzić ich pomysły i wyjaśnić wymagania oraz priorytety. Osta-
tecznie zespół decyduje się na przejęcie prototypu oraz jego modyfikację
w celu przetestowania swoich pomysłów.
Co więcej, członkowie zespołu utrzymują aktualny interfejs spójny
z ostatnim projektem, wymuszając refaktoryzację aplikacji oraz niektórych
obiektów dziedziny, co na początkowym etapie rozwiązuje problemy
integracyjne.
Gdy tylko dostępna stanie się zaawansowana wersja pakowacza
(Packer), integracja kodu będzie już banalnie prosta, ponieważ był on

276 ROZDZIAŁ 9. ODKRYWANIE POJĘĆ NIEJAWNYCH

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.

W JAKI SPOSÓB ZAMODELOWAĆ MNIEJ OCZYWISTE POJĘCIA 277

699b15244ae879c728b313782703a4d7
6
278 ROZDZIAŁ 9. ODKRYWANIE POJĘĆ NIEJAWNYCH

699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 10.

Projekt elastyczny

N adrzędnym celem oprogramowania jest służenie jego użytkow-


nikom. Jednak wcześniej te same programy muszą służyć programistom.
To stwierdzenie jest szczególnie prawdziwe w procesie przykładającym
wagę do refaktoryzacji. W trakcie rozwoju programu programiści będą
aranżować na różne sposoby i przepisywać prawie każdą jego część. Będą
również integrować z aplikacją różne obiekty dziedziny, a także łączyć je
ze sobą nawzajem. Nawet po wielu latach programiści zajmujący się utrzy-
maniem programu będą zmieniać i rozszerzać jego kod. Ludzie muszą
radzić sobie z tymi rzeczami. Jednak czy będą to chcieli robić?
W sytuacji, gdy programowi o złożonej logice brakuje dobrego pro-
jektu, jego refaktoryzacja lub łączenie jego elementów staje się trudne. Gdy
programista nie jest w stanie przewidzieć efektów obliczeń, natychmiast
zaczynają pojawiać się powtórzenia w kodzie. Takie duplikacje bardzo

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

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

ROZDZIAŁ 10. PROJEKT ELASTYCZNY 281

699b15244ae879c728b313782703a4d7
6
Rysunek 10.1.
Niektóre
ze wzorców
wspierających
projekt elastyczny

282 ROZDZIAŁ 10. 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.

INTERFEJSY UJAWNIAJĄCE ZAMIAR 283

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.

Jedynym sposobem, aby przekonać się, co robi metoda paint(Paint),


jest zapoznanie się z kodem.

284 ROZDZIAŁ 10. PROJEKT ELASTYCZNY

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
}

W porządku. Wygląda na to, że ta metoda łączy ze sobą dwie farby,


a wynikiem jej działania jest zwiększenie objętości farby i kolor powstały
ze zmieszania.
Aby zmienić nasze spojrzenie, spróbujmy teraz dla tej metody na-
pisać test (kod bazuje na technologii JUnit).
public void testPaint() {
// Utwórz czystą żółtą farbę o objętości 100
Paint yellow = new Paint(100.0, 0, 50, 0);
// Utwórz czystą niebieską farbę o objętości 100
Paint blue = new Paint(100.0, 0, 0, 50);

// Dodaj niebieską farbę do żółtej


yellow.paint(blue);

// W rezultacie powinniśmy otrzymać nową zieloną farbę o objętości 200.0


assertEquals(200.0, yellow.getV(), 0.01);
assertEquals(25, yellow.getB());
assertEquals(25, yellow.getY());
assertEquals(0, yellow.getR());
}

Pozytywne przejście testu jest dopiero początkiem. Wciąż jednak


nie satysfakcjonuje nas to zbytnio, ponieważ kod w teście nie mówi nam
tego, co właśnie robi. Spróbujmy przepisać test od nowa, aby odzwier-
ciedlić sposób, w jaki chcielibyśmy użyć obiektów Paint, gdybyśmy uży-
wali ich w aplikacji klienckiej. Test zaprezentowany poniżej w pierwszym
podejściu nie zadziała, a nawet wcale się nie skompiluje. Tworzymy go
jednak, aby sprawdzić projekt interfejsu obiektu Paint z perspektywy
osoby programującej kod klienta.
public void testPaint() {
// Rozpocznij od czystej żółtej farby o objętości 100
Paint ourPaint = new Paint(100.0, 0, 50, 0);
// Weź czystą niebieską farbę o objętości 100
Paint blue = new Paint(100.0, 0, 0, 50);

// Dodaj niebieską farbę do żółtej


ourPaint.mixIn(blue);

// W rezultacie powinniśmy otrzymać nową zieloną farbę o objętości 200.0


assertEquals(200.0, ourPaint.getVolume(), 0.01);
assertEquals(25, ourPaint.getBlue());
assertEquals(25, ourPaint.getYellow());
assertEquals(0, ourPaint.getRed());
}

INTERFEJSY UJAWNIAJĄCE ZAMIAR 285

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.

Nowa nazwa metody nie musi mówić czytelnikowi wszystkiego


o efektach „mieszania farby z inną farbą” (do tego będziemy potrzebować
ASERCJI, które omówimy kilka stron dalej). Jednak naprowadzi go na
tyle, aby mógł rozpocząć używanie klasy, szczególnie jeżeli będzie dys-
ponować przykładami testów. Co więcej, umożliwi osobie analizującej
kod klienta zinterpretowanie zamysłu jego programisty. W kilku kolejnych
przykładach umieszczonych w tym rozdziale dokonamy dalszej refakto-
ryzacji tej klasy, aby jeszcze bardziej ją wyjaśnić.

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

286 ROZDZIAŁ 10. PROJEKT ELASTYCZNY

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,

FUNKCJE BEZ EFEKTÓW UBOCZNYCH 287

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.

288 ROZDZIAŁ 10. PROJEKT ELASTYCZNY

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.

public void mixIn(Paint other) {


volume = volume.plus(other.getVolume());
// Pominiętych wiele wierszy skomplikowanej logiki mieszania farb,
// kończącej się przypisaniem nowych wartości zmiennych red, blue oraz yellow
}

Rysunek 10.5.
Efekty uboczne
metody mixIn()

FUNKCJE BEZ EFEKTÓW UBOCZNYCH 289

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.

Refaktoryzacja nowego obiektu reprezentującego kolor pigmentu


(PigmentColor) przekazuje więcej niż we wcześniejszej wersji, jednak
obliczenia są dokładnie takie same i wciąż występują w metodzie mixIn().
W sytuacji, gdy wyjęliśmy dane koloru, powinniśmy zabrać również zwią-
zaną z nim logikę. Zanim jednak to zrobimy, zauważmy, że Pigment-
Color jest WARTOŚCIĄ. Dlatego też powinna być ona traktowana jako
niezmienna. Kiedy mieszaliśmy farbę, został zmieniony obiekt Paint.
Stanowił on ENCJĘ z całym swoim cyklem życia. Dla odróżnienia

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.

290 ROZDZIAŁ 10. PROJEKT ELASTYCZNY

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.

public class PigmentColor {


public PigmentColor mixedWith(PigmentColor other,
double ratio) {
// Wiele wierszy skomplikowanej logiki mieszania farb,
// kończącej się utworzeniem nowego obiektu PigmentColor
// z odpowiednimi wartościami atrybutów red, blue oraz yellow
}
}

public class Paint {


public void mixIn(Paint other) {
volume = volume + other.getVolume();
double ratio = other.getVolume() / volume;
pigmentColor =
pigmentColor.mixedWith(other.pigmentColor(), ratio);
}
}

Rysunek 10.8.

W tym momencie modyfikacje kodu w obiekcie Paint są tak proste,


jak to tylko możliwe. Klasa PigmentColor zachowuje całą wiedzę i w jasny
sposób ją przekazuje, a co więcej, udostępnia FUNKCJĘ BEZ EFEK-
TÓW UBOCZNYCH, której wynik łatwo jest zrozumieć, przetestować, a także

FUNKCJE BEZ EFEKTÓW UBOCZNYCH 291

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.

***

292 ROZDZIAŁ 10. PROJEKT ELASTYCZNY

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.

294 ROZDZIAŁ 10. PROJEKT ELASTYCZNY

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.

Właściwość volume klasy docelowej jest zwiększana o wartość tej


samej objętości przekazanej w postaci parametru operacji. Na podstawie
naszego ogólnego zrozumienia fizycznego procesu mieszania cieczy
możemy stwierdzić, że parametr ten powinien powodować zmniejsze-
nie objętości drugiej farby o tę samą wielkość, wyczerpując ją zupełnie.
Obecna implementacja nie modyfikuje wcale tego argumentu, a sama
operacja modyfikacji argumentów jest szczególnie ryzykownym rodzajem
efektu ubocznego.
Rozpocznijmy od zdefiniowania warunków końcowych metody
mixIn() w sposób odzwierciedlający bieżącą implementację:

Po p1.mixIn(p2):
p1.volume jest zwiększany o wartość p2.volume.
p2.volume pozostaje bez zmian.

Niestety, programiści będą popełniać błędy, ponieważ te właściwości


nie mieszczą się w idei, do zastanawiania nad którą ich zachęcaliśmy.
Łatwym rozwiązaniem byłaby zmiana objętości drugiej farby do zera.
Zmiana argumentu jest złą praktyką, jednak byłoby to wyjście proste
i intuicyjne. Moglibyśmy również określić niezmiennik:
Mieszanie nie zmienia całkowitej objętości farby.

Wstrzymajmy się jednak na chwilę. Gdy programiści zastanawiali


się na tą możliwością, dokonali pewnego odkrycia. Wydaje się, że istniał
ważny powód, dla którego twórcy zdecydowali się na tego rodzaju imple-
mentację. Pod koniec działania program wyświetla listę niezmieszanych farb,
które były dodane do koloru. W końcu głównym zadaniem tego programu jest
pomoc użytkownikom w doborze odpowiednich farb do zmieszania.

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.

Teraz widzimy lepiej


W trakcie poszukiwania lepszego modelu mieliśmy znaczną przewagę nad
jego twórcami. Przyczyniło się do tego przetwarzanie wiedzy oraz refakto-
ryzacja ku głębszemu zrozumieniu, jakie miały miejsce wcześniej. Na
przykład zdecydowaliśmy się obliczać kolor przy użyciu FUNKCJI BEZ
EFEKTÓW UBOCZNYCH operującej na WARTOŚCI. Oznacza to, że
możemy powtarzać obliczenia tyle razy, ile będziemy potrzebować. Powin-
niśmy to wykorzystać.
Wygląda na to, że przyznaliśmy klasie Paint dwie różne i podsta-
wowe odpowiedzialności. Postarajmy się je rozdzielić.

Rysunek 10.10.

296 ROZDZIAŁ 10. PROJEKT ELASTYCZNY

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

StockPaint paint1 = new StockPaint(1.0, yellow);


StockPaint paint2 = new StockPaint(1.5, blue);
MixedPaint mix = new MixedPaint();

mix.mixIn(paint1);
mix.mixIn(paint2);
assertEquals(2.5, mix.getVolume(), 0.01);
}

Ten model wychwytuje oraz komunikuje zdecydowanie więcej


informacji o dziedzinie. Zarówno niezmienniki, jak i warunki końcowe
są logiczne, co zdecydowanie ułatwi ich utrzymanie oraz wykorzystanie
w programie.

***
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ąć

298 ROZDZIAŁ 10. PROJEKT ELASTYCZNY

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.

ZARYSY KONCEPCYJNE 299

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

300 ROZDZIAŁ 10. PROJEKT ELASTYCZNY

699b15244ae879c728b313782703a4d7
6
Rysunek 10.11.

uproszczenia istniejącej funkcjonalności programistka wybrała model, który


zdecydowanie ułatwiał wprowadzanie nowych harmonogramów. Jednak
czy oznacza to, że znalazła ZARYS KONCEPCYJNY, który pomoże jej
w zmianie projektu dziedziny i nadążaniu z rozwojem za biznesem? Nie
ma żadnej gwarancji na to, jak ten projekt poradzi sobie z nieprzewidzianą
zmianą, jednak programistka miała nadzieję, że przynajmniej poprawi on
zawiłości poprzedniego projektu.

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

ZARYSY KONCEPCYJNE 301

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.

Stary projekt wymuszałby duplikację dwóch klas PaymentHistory.


(To utrudnienie mogło spowodować spostrzeżenie, że klasa Payment
powinna być współdzielona, co mogłoby doprowadzić innym tokiem rozu-
mowania do podobnego modelu). Na szczęście, programistka przewidziała
zmianę i stworzyła projekt tak elastyczny, że mógł pomieścić tę nową
zmianę. Było to możliwe, ponieważ w poprzedniej refaktoryzacji projekt
został uwspólniony z pojęciami dziedziny.

***

302 ROZDZIAŁ 10. PROJEKT ELASTYCZNY

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.

ZARYSY KONCEPCYJNE 303

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”

304 ROZDZIAŁ 10. PROJEKT ELASTYCZNY

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

KLASY SAMODZIELNE 305

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.

306 ROZDZIAŁ 10. PROJEKT ELASTYCZNY

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

ZAMKNIĘCIE OPERACJI 307

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()) {

308 ROZDZIAŁ 10. PROJEKT ELASTYCZNY

699b15244ae879c728b313782703a4d7
6
Employee anEmployee = it.next();
if (anEmployee.salary() < 40000)
lowPaidEmployees.add(anEmployee);
}

Patrząc na to pod względem logicznym, pobrałem podzbiór pewnego


zbioru. Do czego więc jest mi w ogóle potrzebne to nowe dodatkowe
pojęcie, Iterator, z całą jego logiką? W języku Smalltalk najzwyczajniej
w świecie wywołałbym operację „select” na kolekcji, przekazując jej jako
argument warunek używany do wyboru obiektów. W efekcie operacja
zwróciłaby mi nową kolekcję (Collection), zawierającą jedynie te elementy,
które przeszły wskazany test.
employees := (pewien zestaw zawierający obiekty klasy Employee).
lowPaidEmployees := employees select:
[:anEmployee | anEmployee salary < 40000].

Kolekcje w Smalltalku zawierają inne przydatne FUNKCJE tego


rodzaju, zwracające kolekcje, które mogą zawierać obiekty różnych innych
skonkretyzowanych klas. Operacje nie są zamknięte, ponieważ jako argu-
ment przyjmują one pewien „blok”. Jednak bloki w Smalltalku są pod-
stawowym typem bibliotecznym, dlatego nie obciążają zbytnio umysłu
programisty. Ponieważ zwracana wartość zgodna jest z rodzajem argu-
mentu obiektu implementującego, wartości te mogą być ze sobą łączone,
podobnie do szeregu filtrów. Bardzo łatwo jest też je napisać lub odczytać.
Nie wprowadzają one żadnych dodatkowych pojęć, które nie są istotne
dla problemu wyszukiwania podzbiorów.

***
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,

ZAMKNIĘCIE OPERACJI 309

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ęść.

310 ROZDZIAŁ 10. PROJEKT ELASTYCZNY

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.

Języki właściwe dziedzinie


Bardzo interesującym podejściem, które niekiedy bywa deklaratywne, jest
język właściwy dziedzinie. W tym podejściu kod klienta tworzony jest
w języku programowania, dostosowanym do określonego modelu dla kon-
kretnej dziedziny. Na przykład język dla systemów logistycznych mógłby
zawierać już pojęcia w rodzaju ładunku lub trasy, wraz ze składnią używaną

PROJEKTOWANIE DEKLARATYWNE 311

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.

Deklaratywny styl projektowania


Gdy dysponujemy już INTERFEJSAMI UJAWNIAJĄCYMI ZAMIAR,
FUNKCJAMI BEZ EFEKTÓW UBOCZNYCH oraz ASERCJAMI,
wkraczamy powoli na terytorium deklaratywne. Wiele korzyści projek-
towania deklaratywnego może zostać osiągniętych, gdy tylko stworzysz

3
Jest to odmiana języka programowania Lisp — przyp. tłum.

312 ROZDZIAŁ 10. PROJEKT ELASTYCZNY

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);
}

DEKLARATYWNY STYL PROJEKTOWANIA 313

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;

public ContainerSpecification(ContainerFeature required) {


requiredFeature = required;
}

boolean isSatisfiedBy(Object candidate){


if (!candidate instanceof Container) return false;
return
(Container)candidate.getFeatures().contains(requiredFeature);
}
}

A teraz rozszerzmy interfejs Specification, dodając trzy nowe


operacje:
public interface Specification {
boolean isSatisfiedBy(Object candidate);

Specification and(Specification other);


Specification or(Specification other);
Specification not();
}

Przypomnijmy, że niektóre specyfikacje kontenera (Container Spe-


cification) wymagały kontenerów (Container) wentylowanych (venti-
lated), a inne opancerzonych (armored). Substancja chemiczna, która
jest zarówno delikatna, jak i wybuchowa, prawdopodobnie wymagałaby obu
tych SPECYFIKACJI jednocześnie. Wszystko to można w prosty sposób
osiągnąć przy użyciu nowych metod:
Specification ventilated = new ContainerSpecification(VENTILATED);
Specification armored = new ContainerSpecification(ARMORED);
Specification both = ventilated.and(armored);

Ta deklaracja definiuje nowy obiekt Specification z oczekiwanymi


właściwościami. To połączenie wymagałoby bardziej skomplikowanej spe-
cyfikacji kontenera do specjalnego użytku.
Przypuśćmy, że mieliśmy więcej niż jeden rodzaj wentylowanego
kontenera. Dla niektórych towarów nie miałoby większego znaczenia,
do jakiego rodzaju zostałyby zapakowane. Mogłyby zostać umieszczone
w dowolnym z nich.
Specification ventilatedType1 =
new ContainerSpecification(VENTILATED_TYPE_1);
Specification ventilatedType2 =

314 ROZDZIAŁ 10. PROJEKT ELASTYCZNY

699b15244ae879c728b313782703a4d7
6
new ContainerSpecification(VENTILATED_TYPE_2);
Specification either = ventilatedType1.or(ventilatedType2);

Gdyby jednak umieszczanie piasku w specjalistycznym kontenerze


uznawane było za marnowanie zasobów, moglibyśmy tego zabronić,
SPECYFIKUJĄC „tani” kontener bez specjalnych funkcjonalności.
Specification cheap = (ventilated.not()).and(armored.not());

Takie ograniczenie zapobiegałoby niektórym nieefektywnym zacho-


waniom prototypu aplikacji pakowacza opisanego w rozdziale 9.
Możliwość tworzenia z prostych elementów złożonych specyfikacji
zwiększa wymowność kodu. Wszystkie kombinacje są również zapisane
w stylu deklaratywnym.
W zależności od sposobu implementacji SPECYFIKACJI definicja
wspomnianych operatorów może być prosta lub trudna. Poniższa prosta
implementacja może być w niektórych sytuacjach nieefektywna, zaś
w innych całkiem praktyczna. Głównym jej celem było wyjaśnienie pojęć.
Podobnie jak w przypadku każdego wzorca, istnieje wiele różnych sposo-
bów ich implementacji.

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);
}

DEKLARATYWNY STYL PROJEKTOWANIA 315

699b15244ae879c728b313782703a4d7
6
public Specification not() {
return new NotSpecification(this);
}
}

public class AndSpecification extends AbstractSpecification {


Specification one;
Specification other;
public AndSpecification(Specification x, Specification y) {
one = x;
other = y;
}
public boolean isSatisfiedBy(Object candidate) {
return one.isSatisfiedBy(candidate) &&
other.isSatisfiedBy(candidate);
}
}

public class OrSpecification extends AbstractSpecification {


Specification one;
Specification other;
public OrSpecification(Specification x, Specification y) {
one = x;
other = y;
}
public boolean isSatisfiedBy(Object candidate) {
return one.isSatisfiedBy(candidate) ||
other.isSatisfiedBy(candidate);
}
}

public class NotSpecification extends AbstractSpecification {


Specification wrapped;

public NotSpecification(Specification x) {
wrapped = x;
}
public boolean isSatisfiedBy(Object candidate) {
return !wrapped.isSatisfiedBy(candidate);
}
}

Powyższy kod został napisany w sposób najprostszy do umieszczenia


w książce. Jak napisałem wcześniej, mogą zdarzyć się sytuacje, w których
jest on nieefektywny. Istnieją jednak inne możliwości implementacji,
które zmniejszyłyby liczbę obiektów lub zwiększyły wydajność, a może
nawet były zgodne ze specyficznymi technologiami obecnymi w danym
projekcie. Ważną rzeczą jest model, który wychwytuje kluczowe pojęcia
dziedziny, wraz z implementacją zgodną z modelem. To pozostawia mnó-
stwo miejsca na rozwiązanie problemów wydajnościowych.
Również pełne uogólnienie nie jest w wielu przypadkach niezbędne.
W szczególności operator AND wydaje się używany zdecydowanie częściej

316 ROZDZIAŁ 10. PROJEKT ELASTYCZNY

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

DEKLARATYWNY STYL PROJEKTOWANIA 317

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)

Ten projekt ma swoje zalety (+) oraz wady (–):


+ Mała liczba obiektów.
+ Wydajne wykorzystanie pamięci.
– Wymaga bardziej zaawansowanych programistów.
Musisz znaleźć kompromisową implementację, działającą w Twoich
okolicznościach. Ten sam wzorzec oraz model mogą być podstawą wielu
różnych implementacji.

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

318 ROZDZIAŁ 10. PROJEKT ELASTYCZNY

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

Bardziej rygorystyczna SPECYFIKACJA podporządkowuje4 sobie tę


mniej rygorystyczną. Mogłaby zająć jej miejsce, nie przeoczywszy żadnego
z wymagań poprzedniej specyfikacji.

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.

DEKLARATYWNY STYL PROJEKTOWANIA 319

699b15244ae879c728b313782703a4d7
6
public class MinimumAgeSpecification {
int threshold;

public boolean isSatisfiedBy(Person candidate) {


return candidate.getAge() >= threshold;
}

public boolean subsumes(MinimumAgeSpecification other) {


return threshold >= other.getThreshold();
}
}

W takim przypadku test JUnit mógłby wyglądać następująco:


drivingAge = new MinimumAgeSpecification(16);
votingAge = new MinimumAgeSpecification(18);
assertTrue(votingAge.subsumes(drivingAge));

Innym praktycznym przypadkiem szczególnym, dobrze dopasowa-


nym do naszego problemu ze specyfikacją kontenera, może być interfejs
SPECYFIKACJI łączący subsumpcję z pojedynczym operatorem logicz-
nym AND.
public interface Specification {
boolean isSatisfiedBy(Object candidate);
Specification and(Specification other);
boolean subsumes(Specification other);
}

Udowodnienie implikacji z jednym operatorem AND jest proste:


A AND B → A

lub w bardziej skomplikowanym przypadku:


A AND B AND C → A AND B

Jeżeli więc specyfikacja kompozytu jest w stanie zebrać wszystkie


SPECYFIKACJE liści, które są połączone operacją „AND”, będziemy
jedynie musieli sprawdzić, czy bardziej ogólna SPECYFIKACJA posiada
wszystkie liście, które ma też ta szczególna, lub też posiada ich nawet więcej
od niej — jej liście są nadzbiorem zbioru liści SPECYFIKACJI szczególnej.
public boolean subsumes(Specification other) {
if (other instanceof CompositeSpecification) {
Collection otherLeaves =
(CompositeSpecification) other.leafSpecifications();
Iterator it = otherLeaves.iterator();
while (it.hasNext()) {
if (!leafSpecifications().contains(it.next()))
return false;
}
} else {
if (!leafSpecifications().contains(other))
return false;

320 ROZDZIAŁ 10. PROJEKT ELASTYCZNY

699b15244ae879c728b313782703a4d7
6
}
return true;
}

Moglibyśmy jeszcze rozwijać tę interakcję, aby mogła służyć porów-


naniu specjalnie wybranych sparametryzowanych SPECYFIKACJI liści
lub innych elementów.
Niestety, jeżeli dodalibyśmy operatory OR lub NOT, te dowody
strasznie się skomplikują. W większości przypadków najlepiej jest unikać
takiej złożoności, rezygnując z niektórych operatorów lub subsumpcji.
Jeżeli jednak obie te rzeczy są wymagane, rozważ ostrożnie, czy zysk jest
na tyle duży, aby usprawiedliwiać tę dodatkową złożoność.

SPECYFIKACJA Arystotelesa
Wszyscy ludzie są śmiertelni. Specification manSpec = new
ManSpecification();
Specification mortalSpec = new
MortalSpecification();
assert manSpec.subsumes(mortalSpec);

Arystoteles jest człowiekiem. Man aristotle = new Man();


assert manSpec.isSatisfiedBy
(aristotle);

Dlatego Arystoteles jest śmiertelny. assert mortalSpec.isSatisfiedBy


(aristotle);

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

KIERUNKI ATAKU 321

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

322 ROZDZIAŁ 10. PROJEKT ELASTYCZNY

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.

Wstępny projekt dystrybucji płatności


W trakcie refaktoryzacji ten kod stanie się łatwiejszy do zrozumienia,
dlatego też nie przywiązujmy się do tej wersji.

Rysunek 10.16.

public class Loan {


private Map shares;

// Pominęliśmy metody dostępu, konstruktory oraz bardzo proste metody

public Map distributePrincipalPayment(double paymentAmount) {


Map paymentShares = new HashMap();
Map loanShares = getShares();
double total = getAmount();
Iterator it = loanShares.keySet().iterator();
while(it.hasNext()) {
Object owner = it.next();
double initialLoanShareAmount = getShareAmount(owner);
double paymentShareAmount =
initialLoanShareAmount / total * paymentAmount;
Share paymentShare =
new Share(owner, paymentShareAmount);
paymentShares.put(owner, paymentShare);

double newLoanShareAmount =
initialLoanShareAmount - paymentShareAmount;
Share newLoanShare =
new Share(owner, newLoanShareAmount);
loanShares.put(owner, newLoanShare);
}
return paymentShares;
}

public double getAmount() {


Map loanShares = getShares();
double total = 0.0;
Iterator it = loanShares.keySet().iterator();
while(it.hasNext()) {
Share loanShare = (Share) loanShares.get(it.next());
total = total + loanShare.getAmount();
}

KIERUNKI ATAKU 323

699b15244ae879c728b313782703a4d7
6
return total;
}
}

Oddzielanie poleceń oraz FUNKCJI


BEZ EFEKTÓW UBOCZNYCH
Projekt dysponuje już INTERFEJSAMI UJAWNIAJĄCYMI ZAMIAR.
Jednak metoda o nazwie distributePaymentPrincipal() wykonuje pewną
niebezpieczną rzecz: oblicza udziały dla potrzeb dystrybucji środków, a także
modyfikuje pożyczkę. Spróbujmy ją zrefaktoryzować w taki sposób, aby
oddzielić zapytanie od modyfikatora.

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);
}
}

public Map calculatePrincipalPaymentShares(double paymentAmount) {


Map paymentShares = new HashMap();
Map loanShares = getShares();
double total = getAmount();
Iterator it = loanShares.keySet().iterator();
while(it.hasNext()) {
Object lender = it.next();
Share loanShare = (Share) loanShares.get(lender);
double paymentShareAmount =
loanShare.getAmount() / total * paymentAmount;
Share paymentShare = new Share(lender, paymentShareAmount);
paymentShares.put(lender, paymentShare);
}
return paymentShares;
}

324 ROZDZIAŁ 10. PROJEKT ELASTYCZNY

699b15244ae879c728b313782703a4d7
6
Kod klienta wygląda teraz następująco:
Map distribution =
aLoan.calculatePrincipalPaymentShares(paymentAmount);
aLoan.applyPrincipalPaymentShares(distribution);

Całkiem dobrze! FUNKCJE hermetyzują teraz dużą część złożoności


logiki za INTERFEJSAMI UJAWNIAJĄCYMI ZAMYSŁ. Jednak kod
zaczyna się komplikować w chwili, gdy dodajemy metody applyDrawdown(),
calculateFeePaymentShares() i podobne. Każde rozszerzenie komplikuje
i przeciąża kod. Moglibyśmy dojść do punktu, w którym granularność
kodu jest zbyt mała. W podejściu tradycyjnym rozdzielilibyśmy metody
obliczeniowe na podprogramy. To mógłby być dobry ruch pośredni,
jednak ostatecznie chcielibyśmy zobaczyć zarys granic pojęciowych oraz
pogłębić model. Elementy projektu z takimi ZARYSAMI KONCEPCYJ-
NYMI mogłyby następnie być łączone, aby tworzyć potrzebne odmiany.

Ujawnianie ukrytych pojęć


Istnieje już wystarczająco dużo wskazówek do podjęcia prób odszukania
nowego modelu. W bieżącej implementacji obiekty Share są bierne i mani-
pulowane na różne złożone niskopoziomowe sposoby. Dzieje się tak,
ponieważ większość reguł oraz obliczeń dotyczących udziałów nie sto-
suje się do poszczególnych udziałów, lecz do ich grup. Brakuje więc pew-
nego pojęcia: udziały są ze sobą powiązane w grupy, tworząc całość. Ujaw-
nienie tego pojęcia pozwoli nam na bardziej zwięzłe wyrażenie tych reguł
oraz obliczeń.

Rysunek 10.18.

Obiekt Share Pie reprezentuje całkowitą dystrybucję określonej


pożyczki (Loan). Jest on ENCJĄ, której tożsamość jest lokalna wewnątrz
AGREGATU pożyczki. Rzeczywista dystrybucja obliczeń może zostać
oddelegowana do obiektu Share Pie.

KIERUNKI ATAKU 325

699b15244ae879c728b313782703a4d7
6
Rysunek 10.19.

public class Loan {


private SharePie shares;

// Pominięte metody dostępu, konstruktory oraz proste metody

public Map calculatePrincipalPaymentDistribution(


double paymentAmount) {
return getShares().prorated(paymentAmount);
}
public void applyPrincipalPayment(Map paymentShares) {
shares.decrease(paymentShares);
}
}

W ten sposób obiekt Loan został uproszczony, a obliczenia udziałów


są scentralizowane w WARTOŚCI skoncentrowanej na tej odpowiedzial-
ności. Wciąż jednak obliczenia nie stały się prawdziwie uniwersalne, a ich
wykorzystanie nie jest prostsze.

Obiekt SharePie staje się WARTOŚCIA


— kaskada spostrzeżeń
Bardzo często doświadczenie wynikające z implementacji nowego projektu
będzie skutkować nowymi spostrzeżeniami w samym modelu. W tym
przykładzie mocne związanie obiektów Loan oraz SharePie wydaje się
zaciemniać relacje obiektów SharePie oraz Share. Co stałoby się, gdy-
byśmy uczynili obiekt SharePie WARTOŚCIĄ?
Wtedy operacje increase(Map) oraz decrease(Map) stałyby się niedo-
zwolone, ponieważ obiekt SharePie musiałby być niezmienny. Aby zmie-
nić wartość udziału, musiałby zostać zastąpiony cały obiekt. Mógłbyś
oczywiście zdefiniować operację addShares(Map), zwracającą cały nowy
i większy obiekt SharePie.

326 ROZDZIAŁ 10. PROJEKT ELASTYCZNY

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();

// Pominęliśmy metody dostępowe oraz inne proste metody

public double getAmount() {


double total = 0.0;
Iterator it = shares.keySet().iterator();
while(it.hasNext()) { Wynik jest równy sumie
Share loanShare = getShare(it.next()); argumentów.
total = total + loanShare.getAmount();
}
return total;
}

public SharePie minus(SharePie otherShares) {


SharePie result = new SharePie(); Różnicę pomiędzy dwoma
Set owners = new HashSet();
obiektami Pie stanowi
owners.addAll(getOwners());
owners.addAll(otherShares.getOwners()); różnica pomiędzy
Iterator it = owners.iterator(); udziałami każdego
while(it.hasNext()) { z udziałowców.

KIERUNKI ATAKU 327

699b15244ae879c728b313782703a4d7
6
Object owner = it.next();
double resultShareAmount = getShareAmount(owner) –
otherShares.getShareAmount(owner);
result.add(owner, resultShareAmount);
}
return result;
}

Połączenie dwóch public SharePie plus(SharePie otherShares) {


obiektów Pie jest równe // Podobnie do implementacji operacji minus()
połączeniu udziałów }
każdego z udziałowców.
public SharePie prorated(double amountToProrate) {
SharePie proration = new SharePie();
Suma może double basis = getAmount();
być proporcjonalnie Iterator it = shares.keySet().iterator();
podzielona pomiędzy while(it.hasNext()) {
wszystkich udziałowców. Object owner = it.next();
Share share = getShare(owner);
double proratedShareAmount =
share.getAmount() / basis * amountToProrate;
proration.add(owner, proratedShareAmount);
}
return proration;
}

Elastyczność nowego projektu


W tym momencie metody w ważnej klasie Loan powinny być tak proste
jak te poniżej:
public class Loan {
private SharePie shares;

// Pominęliśmy metody dostępu, konstruktory oraz metody proste

public SharePie calculatePrincipalPaymentDistribution(


double paymentAmount)
{
return shares.prorated(paymentAmount);
}

public void applyPrincipalPayment(SharePie paymentShares) {


setShares(shares.minus(paymentShares));
}
}

Każda z tych krótkich metod wyraża swoje znaczenie. Dokonanie


spłaty głównej oznacza odjęcie udział po udziale płatności z pożyczki.
Dystrybucja spłaty jest wykonywana poprzez podział środków zgodnie
z zasadą pro rata pomiędzy udziałowców. Projekt obiektu SharePie pozwolił

328 ROZDZIAŁ 10. PROJEKT ELASTYCZNY

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);
}
}

public class Loan {


. . .
public void applyDrawdown(SharePie drawdownShares) {
setShares(shares.plus(drawdownShares));
}
}

Aby sprawdzić odstępstwa każdego z pożyczkodawców od jego wcze-


śniej ustalonego wkładu, należy wziąć teoretyczną dystrybucję wymaganej
kwoty pożyczki i odjąć ją od rzeczywistych w niej udziałów:
SharePie originalAgreement =
aFacility.getShares().prorated(aLoan.getAmount());
SharePie actual = aLoan.getShares();
SharePie deviation = actual.minus(originalAgreement);

Na tę łatwość połączeń oraz dobrą ekspresywność kodu składają się


pewne cechy projektu dla obiektu SharePie:
 Złożona logika jest zahermetyzowana w specjalizowanej WARTOŚCI
z FUNKCJAMI BEZ EFEKTÓW UBOCZNYCH. Większość zło-
żonej logiki została umieszczona w tych niezmiennych obiektach.
Ponieważ obiekty SharePie są WARTOŚCIAMI, operacje mate-
matyczne mogą utworzyć ich nowe instancje, których możemy
dowolnie używać do zastąpienia ich starszych odpowiedników.
Żadna z metod obiektu SharePie nie dokonuje jakiejkolwiek
zmiany do istniejącego obiektu. Umożliwia nam to dowolne stoso-
wanie operacji plus(), minus() oraz prorated() w bezpośrednich
obliczeniach, a także ich łączenie, ponieważ możemy oczekiwać, że
będą wykonywać dokładnie te czynności, które są sugerowane przez

KIERUNKI ATAKU 329

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)

x Operacje zmieniajÈce stan sÈ proste i scharakteryzowane ASERCJAMI.


Wysokopoziomowe abstrakcje matematyki udziaïów pozwalajÈ na
precyzyjne opisanie niezmienników transakcji przy uĝyciu stylu dekla-
ratywnego. Na przykïad odstÚpstwo jest równe róĝnicy pomiÚdzy
rzeczywistym udziaïem a kwotÈ poĝyczki podzielonÈ wedïug udziaïów
w linii kredytowej.
x PojÚcia modelu sÈ rozdzielone — operacje wykorzystujÈ minimalnÈ liczbÚ
innych typów. Niektóre metody klasy SharePie przejawiajÈ cechy
ZAMKNI}CIA OPERACJI (metody do dodawania oraz odejmowa-
nia sÈ zamkniÚte w obiektach SharePie). Argumenty lub wartoĂci
zwracane przez inne metody teĝ sÈ prostymi wartoĂciami. Wpraw-
dzie nie sÈ one zamkniÚte, ale dodajÈ bardzo niewiele narzutu pojÚ-
ciowego. Klasa SharePie wspóïpracuje blisko tylko z jednÈ innÈ
klasÈ, Share. W rezultacie klasy SharePie sÈ samodzielne, ïatwe
do zrozumienia oraz testowania, a takĝe moĝna je w prosty sposób
ïÈczyÊ, aby utworzyÊ transakcje deklaratywne. Te wïaĂciwoĂci zostaïy
odziedziczone z formalizmu matematycznego.
x Znajomy formalizm uïatwia zrozumienie protokoïu. Caïkowicie orygi-
nalny protokóï do manipulacji udziaïami mógï byÊ ïatwo zapisany
z wykorzystaniem terminologii finansowej. W zasadzie mógïby zostaÊ
nawet uelastyczniony. Miaïby jednak wciÈĝ dwie wady. Po pierwsze,
musiaïby zostaÊ wynaleziony, co jest zadaniem trudnym i niepewnym.
Po drugie, musiaïby siÚ go nauczyÊ kaĝdy, kto chciaïby go uĝywaÊ.
Osoby, które ujrzÈ matematykÚ udziaïów, odnajdÈ w niej znany juĝ
system, a poniewaĝ w staranny sposób utrzymywaliĂmy model zgodny
z reguïami arytmetyki, nie bÚdzie on nikogo myliÊ.
RozpoczynajÈc od wydzielenia czÚĂci problemu, odpowiadajÈcej for-
malizmowi matematycznemu, dotarliĂmy do projektu elastycznego dla
udziaïów (Share), który wpïynie równieĝ na dalsze usprawnienie gïównych
metod klas poĝyczki (Loan) oraz linii kredytowej (Facility) — szersza
dyskusja na temat DZIEDZINY G’ÓWNEJ umieszczona zostanie
w rozdziale 15.

330 ROZDZIA’ 10. PROJEKT ELASTYCZNY

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.

KIERUNKI ATAKU 331

699b15244ae879c728b313782703a4d7
6
332 ROZDZIAŁ 10. PROJEKT ELASTYCZNY

699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 11.

Stosowanie wzorców
analitycznych

M odele dogłębne oraz projekty elastyczne nie przychodzą zbyt


łatwo. Postęp w pracach nad ich tworzeniem wymaga wielu dyskusji,
przyswojenia dużej ilości wiedzy o dziedzinie, a także licznych prób i błę-
dów. Niekiedy jednak możemy otrzymać wsparcie.
Kiedy doświadczony programista spogląda na problem dziedziny, może
dostrzec znajomy rodzaj odpowiedzialności lub znajomo wyglądającą sieć
relacji. Wtedy może sięgnąć do pamięci, aby przypomnieć sobie, w jaki
sposób ten problem został rozwiązany wcześniej. Jakich modeli próbował
użyć i które z nich działały. Jakie trudności napotkał w trakcie imple-
mentacji i w jaki sposób je rozwiązał. Nagle okazuje się, że próby i błędy
z wcześniejszych doświadczeń stają się istotne dla nowej sytuacji. Niektóre
z tych wzorców zostały nawet udokumentowane i udostępnione, co
pozwala nam wszystkim skorzystać z nagromadzonego doświadczenia.
W przeciwieństwie do części II tej książki i zasad projektu elastycz-
nego opisanych w rozdziale 10., te wzorce reprezentują wyższy poziom
abstrakcji w bardziej specjalizowanej dziedzinie. Do reprezentacji pew-
nych pojęć wymagają również tylko niewielu obiektów. Pozwalają nam
na ominięcie kosztownych prób i błędów i rozpoczęcie od modelu, który
jest już od razu wymowny oraz możliwy do zaimplementowania, a także
adresuje pewne subtelności, których nauka mogłaby okazać się kosztowna.
Już od pierwszego momentu konieczne będą jednak refaktoryzacja oraz
eksperymentowanie. Nie ma przecież idealnych rozwiązań z pudełka.

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]

Jak wskazuje autor, wzorce analityczne powstały z praktycznego


doświadczenia i dlatego też są bardzo praktyczne, pod warunkiem zastoso-
wania ich w odpowiedniej sytuacji. Co więcej, dla osoby stojącej przed
wymagającą dziedziną stanowią one bardzo cenny produkt, od którego
może rozpocząć się iteracyjny proces tworzenia aplikacji. Sama nazwa
wzorców uwypukla ich pojęciową naturę. Wzorce analityczne nie są roz-
wiązaniami technologicznymi. Stanowią one raczej wskazówki pomaga-
jące w opracowaniu modelu w określonej dziedzinie.
Niestety, nazwa nie oddaje zbyt dobrze znacznej dyskusji o imple-
mentacji (również z przykładami kodu), jaka w tych wzorcach również
występuje. Fowler rozumie pułapki teoretycznej analizy bez zastanawiania
się nad praktycznym projektem. Oto na przykład, w jaki sposób wybiegał
on nawet znacznie poza okres implementacji, myśląc o wpływie okre-
ślonych decyzji na późniejsze długoterminowe utrzymanie systemu:
Kiedy tworzyliśmy nowy dział (księgowości), utworzyliśmy całą sieć
nowych instancji dla reguł zapisu w księgach rachunkowych. Mogliśmy
to wykonać bez żadnej rekompilacji lub przebudowywania systemu
w czasie, gdy był on dostępy operacyjnie. Oczywiście, istniałyby rów-
nież pewne nieprzewidywalne sytuacje, w których potrzebowalibyśmy
nowego podtypu reguły, jednak są to przypadki rzadkie (str. 151 książki
Fowlera).

W dojrzałych projektach wybory modelowe są bardzo często dyk-


towane doświadczeniem w danej aplikacji. Konieczne może być wypró-
bowanie wielu implementacji różnych komponentów. Niektóre z nich
będą musiały zostać przetestowane w produkcji lub nawet być przez
chwilę utrzymywane. Jeżeli tylko taka wiedza byłaby dostępna, można by
uniknąć wielu problemów. Wzorce analityczne mogą przekazać doświad-
czenie wyniesione z innych projektów, łącząc spostrzeżenia dotyczące
modelu z rozszerzoną dyskusją na temat kierunków rozwoju projektu
oraz konsekwencji pewnych wyborów implementacyjnych. Wyrwane
z kontekstu dyskusje nad koncepcjami modelu przyczyniają się do trud-
ności w implementacji, a także stanowią ryzyko rozejścia się analizy z pro-
jektem, co stoi w sprzeczności z PROJEKTOWANIEM STEROWANYM
MODELEM.

334 ROZDZIAŁ 11. STOSOWANIE WZORCÓW ANALITYCZNYCH

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

NALICZANIE ODSETEK NA KONTACH 335

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.

Modele księgowe we wzorcach analitycznych


Aplikacje biznesowe różnego rodzaju mogą śledzić stan kont, za-
wierających pewne wartości, zazwyczaj pieniężne. W wielu aplika-
cjach nie wystarcza jedynie śledzenie kwoty rachunku, lecz istotne
mogą okazać się kontrolowanie i księgowanie każdej zmiany na
tym koncie. Tak przypadek będzie stanowić motywację do zasto-
sowania najbardziej podstawowego z modeli księgowych.

Rysunek 11.2.
Prosty model
księgowy

W powyższym modelu wartość (ang. value) może być dodana do


konta poprzez wprowadzenie wpisu (Entry), a usunięta przez analo-
giczny wpis z wartością ujemną. Wpisy nie są nigdy usuwane, dlatego
zachowujemy całą ich historię. Ostateczny bilans operacji jest łączną
sumą wszystkich wpisów. Może on zostać obliczony na żądanie lub
zachowany, wszystko zależy od decyzji implementacyjnej zaher-
metyzowanej w interfejsie reprezentującym konto (Account).
Podstawową zasadą księgowości jest zachowanie ciągłości. Pieniądze
nie pojawiają się znikąd i nie znikają bez śladu. Są jedynie przeno-
szone z jednego konta (Account) na inne.

Rysunek 11.3.
Model transakcyjny

336 ROZDZIAŁ 11. STOSOWANIE WZORCÓW ANALITYCZNYCH

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.

Lektura rozdziału przynosi programistce (Programista 1) szereg nowych


pomysłów. Dlatego pokazuje ona ten rozdział swojemu koledze (Pro-
gramista 2), odpowiedzialnemu za stworzenie części logiki obliczania
odsetek, a także za nocny program wsadowy.

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.

NALICZANIE ODSETEK NA KONTACH 337

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?

338 ROZDZIAŁ 11. STOSOWANIE WZORCÓW ANALITYCZNYCH

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

Ekspert: Wygląda naprawdę dobrze! Rysunek 11.6.


Programista 2: Zmiana skryptu wsadowego w celu uwzględnienia tych Diagram oparty
nowych obiektów będzie prosta. na koncie,
z pominięciem
Programista 1: Utworzenie nowego kalkulatora odsetkowego (Interest transakcji
Calculator) zajmie kilka dni. Trzeba będzie zmienić też kilka testów.
Jednak po tej zmianie będą one zdecydowanie bardziej przejrzyste.

NALICZANIE ODSETEK NA KONTACH 339

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.

Niekiedy w naszych programach istnieją obszary, których nawet nie podej-


rzewaliśmy o to, że mogą potencjalnie skorzystać z modelu dziedziny.
Mogły pierwotnie być utworzone w bardzo prostym kształcie, a następnie
w sposób mechaniczny zwiększać swoją złożoność. Mogą bardziej przy-
pominać skomplikowany kod aplikacji niż logikę dziedziny. Wzorce anali-
tyczne mogą być szczególnie przydatne w ukazywaniu nam takich miejsc.
W kolejnym przykładzie programista ma kilka spostrzeżeń na temat
nocnego przetwarzania wsadowego, które nie wydawało się wcześniej mieć
związku z dziedziną.

340 ROZDZIAŁ 11. STOSOWANIE WZORCÓW ANALITYCZNYCH

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.

SPOSTRZEŻENIA NA TEMAT NOCNEGO PRZETWARZANIA 341

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.

Wykonywanie reguł księgowania


Reguła księgowania (Posting Rule) ustanowiła pewną zależność
pojęciową pomiędzy kontami, jeżeli jednak zatrzymalibyśmy się
z analizą wzorca w tym miejscu, mielibyśmy trudności z jego zasto-
sowaniem. Jedną z najtrudniejszych części w projektowaniu zależ-
ności jest kontrola zależności czasowych dla uaktualnień. Fowler
zaprezentował trzy możliwości:
1. „Wywołanie ochocze” jest najbardziej oczywiste, jednak zazwyczaj
najmniej praktyczne. Każdorazowe dodanie wpisu (Entry) do konta
(Account) skutkuje wywołaniem reguł księgowania (Posting Rules),
a wszystkie uaktualnienia wykonywane są natychmiast.
2. „Wywoływanie na podstawie konta” pozwala na odroczenie przetwa-
rzania. W pewnym momencie do obiektu konta wysyłany jest komu-
nikat wywołujący reguły księgowania, przetwarzające wszystkie wpisy
od ostatniego uruchomienia.
3. Ostatecznie „wywoływanie na podstawie reguły księgowania” jest
zapoczątkowywane przez zewnętrznego agenta, który nakazuje uru-
chomienie reguł księgowania. Reguła jest odpowiedzialna za odszuka-
nie wszystkich wpisów utworzonych na wejściowym koncie od czasu
jej ostatniego uruchomienia.
Chociaż w systemie możliwe jest mieszanie wszystkich tych
trybów wywoływania, każdy zestaw reguł musi dysponować jednym
jasno zdefiniowanym miejscem ich uruchomienia oraz odpowiedzial-
nością za rozpoznawanie wpisów do wejściowych kont. Dodanie do
JĘZYKA WSZECHOBECNEGO tych trzech trybów wywołania

342 ROZDZIAŁ 11. STOSOWANIE WZORCÓW ANALITYCZNYCH

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

Programista 2 musiał przedyskutować z kimś swoje nowe pomysły.


Spotkał się więc w tym celu ze swoją koleżanką (Programista 1), która
była odpowiedzialna za modelowanie sum przyrostowych.
Programista 2: W pewnym momencie nocny skrypt stał się miejscem,
do którego wszystko zamiataliśmy. W skrypcie tym występuje pewna
niejawna logika wpływająca na jego działanie, która zaczęła się coraz
bardziej komplikować. Już od dłuższego czasu chciałem dla niego
wykonać projekt sterowany modelem, oddzielając warstwę dziedziny,
a jednocześnie umieszczając ten skrypt w prostej warstwie na górze
tej dziedziny. Nigdy nie mogłem jednak wymyślić, jak miałby wyglą-
dać jej model. Wyglądało na to, że są to tylko proste procedury, które
jako obiekty nie miałyby sensu. Kiedy czytałem podrozdział książki
o wzorcach analitycznych, mówiący o regułach księgowania, przyszło
mi do głowy kilka pomysłów. Oto, co miałem na myśli. [Wręcza szkic].

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

SPOSTRZEŻENIA NA TEMAT NOCNEGO PRZETWARZANIA 343

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

344 ROZDZIA’ 11. STOSOWANIE WZORCÓW ANALITYCZNYCH

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

SPOSTRZEŻENIA NA TEMAT NOCNEGO PRZETWARZANIA 345

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.

346 ROZDZIAŁ 11. STOSOWANIE WZORCÓW ANALITYCZNYCH

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.

SPOSTRZEŻENIA NA TEMAT NOCNEGO PRZETWARZANIA 347

699b15244ae879c728b313782703a4d7
6
348 ROZDZIAŁ 11. STOSOWANIE WZORCÓW ANALITYCZNYCH

699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 12.

Powiązanie wzorców
projektowych
z modelem

Z aprezentowane dotychczas w tej książce wzorce przeznaczone są


w szczególności do rozwiązywania problemów w modelu dziedziny w kon-
tekście PROJEKTOWANIA STEROWANEGO MODELEM. W rzeczy-
wistości jednak większość wzorców, którymi zajmowaliśmy się do tej pory,
jest bardzo techniczna. W takim razie jaka jest różnica pomiędzy wzorcem
projektowym a wzorcem dziedziny? Dla początkujących autorzy książki
pt. Wzorce projektowe mają radę:
To punkt widzenia definiuje indywidualną ocenę tego, co jest, a co
nie jest wzorcem. To, co dla jednej osoby jest wzorcem, dla innej może
być zaledwie prymitywnym elementem składowym modelu. W tej
książce skoncentrowaliśmy się na wzorcach na pewnym poziomie
abstrakcji. Wzorce projektowe nie zajmują się takimi kwestiami pro-
jektowymi jak listy połączone lub tablice haszujące, które mogą być
zaprogramowane w klasach i w ten sposób wykorzystane. Nie są też
złożonymi projektami właściwymi dla danej dziedziny, przeznaczo-
nymi dla całej aplikacji lub podsystemu. Są one raczej opisem komu-
nikacji obiektów oraz klas skonfigurowanych w celu rozwiązywania
ogólnego problemu projektowego w określonym kontekście. [Gamma
1995 r., str. 3]

Niektóre ze wzorców projektowych (lecz nie wszystkie) mogą być


użyte w charakterze wzorców dziedziny. W tym celu należy odrobinę
zmienić swoje nastawienie. Książka pt. Wzorce projektowe udostępnia katalog

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.

350 ROZDZIAŁ 12. POWIĄZANIE WZORCÓW PROJEKTOWYCH Z MODELEM

699b15244ae879c728b313782703a4d7
6
STRATEGIA
(zwana również POLITYKĄ)

Zdefiniuj rodzinę algorytmów, każdy z nich zahermetyzuj i spraw,


aby mogły być stosowane wymiennie. STRATEGIA pozwala na to,
aby algorytmy różniły się niezależnie od używających ich klas.
[Gamma 1995 r.]

Modele dziedzinowe zawierają procesy, które nie są motywowane


kwestiami technicznymi, jednak są one w rzeczywistości istotne
w dziedzinie danego problemu. Jeżeli będzie konieczne zastoso-
wanie alternatywnych procesów, złożoność związana z wyborem
odpowiedniego z nich nakłada się na złożoność samych proce-
sów, co może spowodować wymykanie się ich spod kontroli.
W trakcie modelowania procesów często zdajemy sobie sprawę, że
istnieje więcej niż jeden poprawny sposób ich wykonania. Gdy rozpocz-
niemy opisywanie tych alternatyw, nasza definicja procesu stanie się zawiła
i skomplikowana. Wybrane przez nas rzeczywiste alternatywy logiki pro-
cesu zostaną przemieszane z resztą logiki.
Bardzo często chcielibyśmy oddzielić te alternatywy od głównej
istoty procesu. Wtedy moglibyśmy lepiej dostrzec zarówno sam główny
proces, jak i opcje, którymi będziemy dysponować. Taki problem rozwią-
zywany jest już przez wzorzec o nazwie STRATEGIA, który jest powszech-
nie stosowany przez społeczność zajmującą się projektowaniem oprogra-
mowania. Niemniej jednak koncentruje się on głównie na kwestiach

STRATEGIA (ZWANA RÓWNIEŻ POLITYKĄ) 351

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

352 ROZDZIAŁ 12. POWIĄZANIE WZORCÓW PROJEKTOWYCH Z MODELEM

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ść

STRATEGIA (ZWANA RÓWNIEŻ POLITYKĄ) 353

699b15244ae879c728b313782703a4d7
6
Rysunek 12.2.
Alternatywne
STRATEGIE
(POLITYKI)
przekazywane
w postaci argumentu

mogłaby być nieakceptowalna. Do tej aplikacji powrócimy jeszcze raz


w rozdziale 14., „Utrzymywanie integralności modelu”, w którym uży-
jemy tego samego interfejsu z całkowicie inną implementacją usługi
trasowania.

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

354 ROZDZIAŁ 12. POWIĄZANIE WZORCÓW PROJEKTOWYCH Z MODELEM

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.

STRATEGIA (ZWANA RÓWNIEŻ POLITYKĄ) 355

699b15244ae879c728b313782703a4d7
6
KOMPOZYT

Skomponuj obiekty w postaci struktur drzewiastych w celu zaprezen-


towania hierarchii typu część-całość (ang. part-whole). KOMPOZYT
pozwala jednakowo traktować poszczególne obiekty oraz kompozycje
obiektów. [Gamma 1995 r.]

Modelując złożone dziedziny, bardzo często napotykamy obiekt złożony


z części, które z kolei złożone są z części złożonych z innych części, nie-
kiedy zagłębionych dowolnie głęboko. W niektórych dziedzinach każdy
z tych poziomów jest odmienny pojęciowo, jednak w innych przypadkach
części stanowią element większej całości i stanowią prawie tę samą rzecz,
tylko że mniejszą.
Jeżeli ta relacja zawartych w sobie kontenerów nie zostanie
odzwierciedlona w modelu, wtedy ich wspólna logika musi być
powtórzona na każdym poziomie hierarchii, a zagnieżdżenie staje
się sztywne (na przykład kontenery nie mogą zazwyczaj zawierać
innych kontenerów na swoim poziomie, a liczba poziomów jest
sztywna). Kod klienta musi radzić sobie z różnymi poziomami
hierarchii, używając różnych interfejsów, nawet jeżeli z punktu
widzenia pojęciowego nie ma żadnej różnicy w ich działaniu.
Rekurencja w hierarchii w celu utworzenia zagregowanych infor-
macji jest bardzo skomplikowana.
Przed zastosowaniem dowolnego wzorca projektowego w dziedzi-
nie powinniśmy najpierw sprawdzić, czy dana idea wzorca rzeczywiście
dobrze pasuje do pojęcia dziedziny. Oczywiście, w przypadku niektórych

356 ROZDZIAŁ 12. POWIĄZANIE WZORCÓW PROJEKTOWYCH Z MODELEM

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.

358 ROZDZIAŁ 12. POWIĄZANIE WZORCÓW PROJEKTOWYCH Z MODELEM

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

W przeciwieństwie do poprzedniego diagramu, statyczny diagram klas


nie mówi nam jednak zbyt wiele o tym, w jaki sposób różne segmenty
trasy pasują do siebie razem. Model jest zdecydowanie czymś więcej niż
tylko statycznym diagramem klas. Dlatego informacje niezbędne do złoże-
nia modelu przekażemy za pomocą innych diagramów (patrz rysunek 12.9)
oraz poprzez (teraz znacznie prostszy) kod. Ten model wychwytuje głę-
boką zależność wszystkich tych różnych rodzajów „tras”. Utworzenie
planu operacyjnego jest znów proste, podobnie jak inne operacje prze-
glądania trasy.
Dysponując trasą złożoną z innych tras, poskładanych razem od
początku do końca w celu dostarczenia ładunku z jednego miejsca w inne,
możesz teraz tworzyć implementacje tras o różnych poziomach szczegó-
łowości. Na przykład możesz skrócić trasę pod koniec i przekierować
w inne miejsce, możesz też z dowolnym poziomem szczegółowości opi-
sywać każdy etap, a także korzystać z wielu rodzajów prawdopodobnie
przydatnych alternatyw.

360 ROZDZIAŁ 12. POWIĄZANIE WZORCÓW PROJEKTOWYCH Z MODELEM

699b15244ae879c728b313782703a4d7
6
Rysunek 12.9.
Instancje
reprezentujące
całą trasę

Oczywiście, w tej chwili nie potrzebujemy ich wszystkich. I radzi-


liśmy sobie całkiem dobrze bez KOMPOZYTU, nawet nie mając tych
wszystkich segmentów tras i rozróżniania etapów. Wzorzec projektowy
powinien być stosowany jedynie wtedy, gdy jest naprawdę potrzebny.

***

Dlaczego nie wzorzec PYŁKU


(FLYWEIGHT)?
Ponieważ już wcześniej (w rozdziale 5.) odwoływałem się do wzorca
PYŁKU, mogłeś założyć, że jest on przykładem wzorca stosowanego
w modelach dziedziny. W rzeczywistości wzorzec PYŁKU jest dobrym
przykładem wzorca projektowego, który nie ma związku z modelem
dziedziny.
Stosowanie tego wzorca ma sens, gdy używamy wielokrotnie ograni-
czonego zestawu WARTOŚCI (jak w przykładzie przyłączy elektrycznych
na planie domu). Stanowi to alternatywę implementacyjną dostępną dla

DLACZEGO NIE WZORZEC PYŁKU (FLYWEIGHT)? 361

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.

Nie zamierzam zamieszczać całej listy wzorców projektowych, które mogą


być używane w tym charakterze. I chociaż nie mogę sobie wyobrazić
żadnego przykładu używającego interpretera w charakterze wzorca dzie-
dzinowego, to jednak nie twierdzę, że nie istnieje żadna koncepcja dzie-
dziny, do której on by pasował. Jedynym wymaganiem jest, aby wzorzec
mówił coś o dziedzinie pojęciowej, a nie był tylko technicznym rozwią-
zaniem technicznego problemu.

362 ROZDZIAŁ 12. POWIĄZANIE WZORCÓW PROJEKTOWYCH Z MODELEM

699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 13.

Refaktoryzacja
ku głębszemu
zrozumieniu

R efaktoryzacja ku głębszemu zrozumieniu jest procesem wielo-


płaszczyznowym. Dlatego warto przez moment zatrzymać się i podsu-
mować wszystkie główne założenia. Trzeba się skoncentrować na trzech
zasadniczych aspektach:
1. Żyj w dziedzinie.
2. Spoglądaj na rzeczy pod różnymi kątami.
3. Utrzymuj niezakłócony dialog z ekspertami dziedzinowymi.
Odkrywanie dziedziny tworzy szerszy kontekst dla procesu refak-
toryzacji.
Tradycyjny scenariusz refaktoryzacji wykonywany jest przez pro-
gramistę lub dwóch wspólnie programujących i analizujących, które
miejsca w kodzie mogą zostać zmienione, a następnie modyfikujących go
w locie (oczywiście, jednocześnie tworzą testy jednostkowe, aby zwery-
fikować rezultat zmian). Tego rodzaju działania powinny być realizowane
przez cały czas, jednak nie mogą stanowić wyłącznego obszaru naszych
zainteresowań.
W poprzednich pięciu rozdziałach przedstawialiśmy rozszerzony obraz
refaktoryzacji nałożony na tradycyjne podejście mikrorefaktoryzacji.

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

364 ROZDZIAŁ 13. REFAKTORYZACJA KU GŁĘBSZEMU ZROZUMIENIU

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ą

WCZEŚNIEJSZE ODKRYCIA 365

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.

Projekt dla programistów


Aplikacja nie jest tylko dla użytkowników, lecz także dla programistów.
To programiści muszą zintegrować kod z innymi częściami systemu. W pro-
cesie iteracyjnym programiści stale zmieniają kod. Refaktoryzacja ku głęb-
szemu zrozumieniu prowadzi do projektu elastycznego, jak również z niego
korzysta.
Projekt elastyczny w dobry sposób komunikuje swoje przeznaczenie,
co z kolei pomaga przewidzieć rezultat działającego kodu. Dlatego też

366 ROZDZIAŁ 13. REFAKTORYZACJA KU GŁĘBSZEMU ZROZUMIENIU

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

WYCZUCIE CZASU 367

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.

Kryzys jako źródło możliwości


Standardowy model ewolucji przez ponad sto lat od chwili odkrycia go
przez Karola Darwina mówił o stopniowej zmianie gatunków przez cały
czas ich istnienia. Nagle w latach 70. ubiegłego wieku ten model został
zastąpiony przez „punktualizm”. Jest on rozszerzonym spojrzeniem na
ewolucję, mówiącym, że długie etapy stopniowej zmiany lub stabilności są
przerywane przez relatywnie krótkie momenty gwałtownych przemian.
To wprowadziło nową równowagę. Rozwój oprogramowania ma celowy
kierunek, którego brak ewolucji (chociaż może być on niewidoczny
w niektórych projektach), jednak niezależnie od tego podlega on też temu
rytmowi.
Klasyczny opis refaktoryzacji brzmi bardzo stabilnie. Natomiast refak-
toryzacja ku głębszemu zrozumieniu zazwyczaj taka nie jest. Okres stałego
ulepszania modelu może nagle doprowadzić do spostrzeżenia, które zmieni
wszystko. Takie momenty przełomowe nie zdarzają się codziennie, cho-
ciaż stanowią źródło dużej części zmian prowadzących do dogłębnego
modelu oraz projektu elastycznego.
Taka sytuacja zazwyczaj nie przypomina możliwości, a raczej wygląda
jak kryzys. Nagle pojawia się jakieś oczywiste niedopasowanie w modelu,
na przykład jakaś wielka dziura w tym, co może on wyrazić, lub nieja-
sność w którymś z obszarów. A może w modelu są błędne założenia?
Oznaczać to będzie osiągnięcie przez zespół poziomu zrozumienia
dziedziny. Z ich nowego punktu widzenia stary model wygląda słabo.
Mogą także dostrzec zdecydowanie lepszy.
Refaktoryzacja ku głębszemu zrozumieniu jest procesem ciągłym.
Niejawne pojęcia są odnajdywane i ujawniane. Elementy projektu są uela-
styczniane, przyjmując niekiedy styl deklaratywny. Nagle projekt dociera do
momentu przełomowego i przemieszcza się w kierunku modelu dogłęb-
nego. Następnie powolne ulepszanie zaczyna się od początku.

368 ROZDZIAŁ 13. REFAKTORYZACJA KU GŁĘBSZEMU ZROZUMIENIU

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

PROJEKT STRATEGICZNY 371

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

P racowałem kiedyś przy projekcie, w którym kilka zespołów rów-


nolegle tworzyło duży nowy system. Pewnego dnia zespół pracujący nad
modułem do fakturowania klienta stał się gotów do zaimplementowania
obiektu o nazwie Charge. Odkrył on jednak, że inny zespól taki obiekt
już zbudował. Z tego powodu członkowie pierwszego zespołu zaczęli
z zaangażowaniem używać istniejącego obiektu. Odkryli szybko, że nie
ma on „kodu wydatku”, więc czym prędzej go dodali. Obiekt posiadał
już wymagany atrybut o nazwie „kwota zaksięgowana”. Wprawdzie pro-
gramiści planowali nazwać go „kwota dłużna”, lecz cóż znaczy nazwa —
najzwyczajniej ją zmienili. Po dodaniu kilku innych metod oraz asocjacji
otrzymali coś, co przypominało to, czego szukali, dodatkowo bez wpro-
wadzania zbytniego bałaganu do istniejącego kodu. Wprawdzie musieli
zignorować wiele asocjacji, których nie potrzebowali, jednak ich moduł
aplikacji działał.
Kilka dni później w module aplikacji do płatności rachunków, dla
której obiekt Charge był oryginalnie zaprojektowany, zaczęły pojawiać
się tajemnicze problemy. Występowały dziwne obciążenia, których nikt
nie dodawał, a także takie, które nie miały żadnego sensu. W przypadku
użycia niektórych z funkcji programu, a w szczególności raportowania
częściowego podatku miesięcznego, program się zawieszał. Analiza błędu
wykazała, że awaria programu wynikała z błędnego działania funkcji uży-
wanej do sumowania kwoty odliczanej dla wszystkich dotychczasowych
płatności miesięcznych. Dziwne rekordy nie miały wartości w polu „wyma-
gany procent”, chociaż sprawdzenie modułu służącego do wprowadzania

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-

374 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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

KONTEKST ZWIĄZANY 375

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

376 ROZDZIAŁ 14. UTRZYMYWANIE 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ę.

W dużych projektach istnieje wspólnie wiele modeli i taka sytuacja


jest w wielu przypadkach w porządku. Różne modele mają zastosowanie
w różnych kontekstach. Na przykład możesz być zmuszony zintegrować
swoją nową aplikację z systemem zewnętrznym, nad którym zespół nie
będzie mieć kontroli. Jest to prawdopodobnie zrozumiała dla wszystkich
sytuacja, określająca oddzielny kontekst, do którego nie będzie mieć zasto-
sowania tworzony model. Jednak inne przypadki mogą być bardziej
ogólne i mylące. W historyjce opowiedzianej na początku tego rozdziału
dwa zespoły pracowały nad różną funkcjonalnością tego samego nowego
systemu. Czy pracowały nad tym samym modelem? Ich zamiarem było
współdzielenie przynajmniej jego części, jednak nie istniała linia rozgra-
niczająca, mówiąca im, co powinni, a czego nie powinni dzielić. Projektanci
nie dysponowali również żadnym procesem umożliwiającym utrzymy-
wanie spójności współdzielonego modelu lub szybkie wynajdowanie
odstępstw od niego. Zdali sobie sprawę z tego, że odeszli od niego dopiero
wtedy, gdy zachowanie systemu stało się nieprzewidywalne.
Nawet jeden zespół może skończyć z wieloma modelami. Komu-
nikacja wewnątrz zespołu może zaniknąć, prowadząc do subtelnie wyklu-
czających się interpretacji modelu. Stary kod może często odzwierciedlać
wcześniejsze podejście do modelu, nieco różniące się od obecnego.
Wszyscy są świadomi, że format daty w innym systemie jest różny
i wymaga konwersji, jednak jest to jedynie mechaniczny wymiar problemu.

KONTEKST ZWIĄZANY 377

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

378 ROZDZIA’ 14. UTRZYMYWANIE INTEGRALNO¥CI MODELU

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.

KONTEKST ZWIĄZANY 379

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.

380 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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?

Rozpoznawanie odprysków pojęciowych


w KONTEKŚCIE ZWIĄZANYM
Wiele symptomów może wskazywać na nierozpoznane różnice w modelu.
Najbardziej oczywiste jest niedopasowanie zakodowanych interfejsów.
Również prawdopodobnym sygnałem jest nieoczekiwane zachowanie.
W wychwyceniu tego rodzaju projektów może pomóc proces CIĄGŁEJ
INTEGRACJI z testami jednostkowymi. Jednak najwcześniejszym sygna-
łem ostrzegawczym jest nieporozumienie w językach.
Łączenie elementów różnych modeli powoduje dwa rodzaje proble-
mów: zduplikowane pojęcia oraz fałszywe pokrewieństwa. Duplikacja pojęć
oznacza, że istnieją dwa elementy modelu (oraz związane z nimi imple-
mentacje), które w rzeczywistości reprezentują to samo pojęcie. Za każdym
razem, gdy informacja ulegnie zmianie, będzie to oznaczać wykonanie kon-
wersji i uaktualnienie jej w dwóch miejscach. W każdej sytuacji, gdy nowa
wiedza prowadzi do zmiany w jednym z obiektów, drugi również musi
zostać ponownie przeanalizowany oraz zmieniony. Ponowna analiza
w rzeczywistości nie występuje zbyt często, dlatego w rezultacie otrzymu-
jemy dwie wersje tego samego pomysłu, sterowane różnymi regułami,
a nawet posiadające różne dane. Co więcej, członkowie zespołu muszą
nauczyć się nie jednego, a dwóch sposobów wykonywania tej samej rzeczy,
jak również wszystkich sposobów synchronizacji.
Fałszywe pokrewieństwa mogą występować rzadziej, jednak zdra-
dziecko mogą być bardziej szkodliwe. To przypadek, w którym dwie
osoby używające tego samego określenia (lub zaimplementowanego
obiektu) sądzą, że mówią o tej samej rzeczy, gdy w rzeczywistości tak
nie jest. Przykładem jest początek tego rozdziału, gdzie występowała typowa

KONTEKST ZWIĄZANY 381

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.

382 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

699b15244ae879c728b313782703a4d7
6
CIĄGŁA INTEGRACJA

Dysponując zdefiniowanym KONTEKSTEM ZWIĄZANYM, musimy


już się go trzymać.
***
Kiedy szereg osób pracuje w tym samym KONTEKŚCIE
ZWIĄZANYM, model ma dużą tendencję do fragmentacji. Im
większy zespół, tym większy problem, ale już trzy lub cztery
osoby mogą napotkać poważne problemy. Jednak ciągłe dziele-
nie systemu na coraz mniejsze KONTEKSTY prowadzi osta-
tecznie do utraty cennego poziomu integracji oraz spójności.
Programiści niekiedy nie rozumieją w pełni celu istnienia pewnych
obiektów lub interakcji zamodelowanych przez kogoś innego, zmieniając
je w sposób wykluczający ich przydatność w oryginalnym celu. Niekiedy
nie rozumieją, że pojęcia, nad którymi pracują, są już osadzone w innej
części modelu, i w ten sposób duplikują (niedokładnie) te pojęcia oraz
ich zachowanie. Innym razem są oni świadomi istnienia tych wyrażeń,
lecz obawiają się w nie ingerować ze strachu przed uszkodzeniem istnie-
jącej funkcjonalności, dlatego też decydują się na duplikację pojęć wraz z ich
funkcjonalnościami.
Bardzo trudno jest utrzymać odpowiedni poziom komunikacji, nie-
zbędny do utworzenia zunifikowanego systemu o dowolnym rozmiarze.
Musimy dysponować sposobem na zwiększenie komunikacji oraz zmniej-
szenie złożoności. Jednocześnie będziemy potrzebować środków bezpie-
czeństwa, zapobiegających nadmiernej ostrożności, na przykład progra-
mistów duplikujących funkcjonalność z obawy przed uszkodzeniem
istniejącego kodu.

CIĄGŁA INTEGRACJA 383

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.

384 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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

CIĄGŁA INTEGRACJA 385

699b15244ae879c728b313782703a4d7
6
MAPA KONTEKSTÓW

Pojedynczy KONTEKST ZWIĄZANY nie udostępnia widoku global-


nego. Konteksty innych modeli mogą być wciąż niejednoznaczne i ulegać
zmianie.
***
Członkowie innych zespołów nie będą znali granic KON-
TEKSTU i mogą nieświadomie wykonywać zmiany rozmywające
jego granice lub komplikujące wzajemne połączenia. Gdy pomię-
dzy różnymi kontekstami muszą zostać utworzone połączenia,
konteksty te zazwyczaj wychodzą poza swój zakres.
Musimy uniknąć ryzyka używania kodu pomiędzy KONTEK-
STAMI ZWIĄZANYMI. Integracja funkcjonalności oraz danych musi prze-
chodzić przez proces ich tłumaczenia. Całe zamieszanie można zmniejszyć,
definiując relacje pomiędzy różnymi kontekstami oraz tworząc globalny
widok wszystkich kontekstów modeli w projekcie.
MAPA KONTEKSTÓW (ang. CONTEXT MAP) nakłada się na
zarządzanie projektem oraz projektowanie oprogramowania. Naturalną
rzeczą jest, że granice odwzorowują kontury organizacji zespołu. Ludzie
pracujący blisko ze sobą będą w sposób naturalny dzielić kontekst modelu.
Osoby w różnych zespołach, a także te, które są w tym samym zespole,
lecz ze sobą nie rozmawiają, będą używać innych kontekstów. Wpływ na
to może mieć również przestrzeń biurowa, gdyż członkowie zespołu
znajdujący się w przeciwnych końcach budynku (nie wspominając o tych
w różnych miastach) prawdopodobnie odejdą od wspólnego kontekstu,

386 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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

MAPA KONTEKSTÓW 387

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.

388 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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

MAPA KONTEKSTÓW 389

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

Lista identyfikatorów węzłów (Node) → Itinerary

Aby tego dokonać, musimy przyjrzeć się znaczeniu elementu jed-


nego modelu i zastanowić się, w jaki sposób wyrazić go w terminologii
drugiego modelu.
Rozpoczynając od pierwszego mapowania (Route Specification →
Lista kodów lokalizacji), musimy najpierw pomyśleć o znaczeniu sekwencji
lokalizacji na liście. Pierwszym elementem na liście będzie początek
ścieżki, która następnie będzie musiała przechodzić przez wszystkie lokali-
zacje w celu dotarcia do tej ostatniej na liście. Pośrodku ścieżki powinna
znaleźć się lokalizacja punktu odpraw celnych (jeżeli taki jest wymagany).
(Na szczęście oba zespoły używały tych samów kodów lokalizacji,
więc nie musimy zajmować się ich tłumaczeniem).

390 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

699b15244ae879c728b313782703a4d7
6
Rysunek 14.4.
Tłumaczenie
zapytania do usługi
przeszukiwania sieci
(Network Traversal
Service)

Zauważ, że odwrotne tłumaczenie byłoby niejednoznaczne, ponie-


waż wejście usługi przeszukiwania sieci dopuszcza dowolną liczbę punk-
tów pośrednich, a nie tylko jeden ściśle wyznaczony jako punkt odpraw
celnych. Na szczęście nie stanowi to problemu, ponieważ nie potrzebu-
jemy tłumaczenia w tym kierunku, jednak daje lepsze zrozumienie, dla-
czego niektóre mapowania są niemożliwe.
A teraz przetłumaczmy rezultat: (Lista identyfikatorów węzłów
(Node) → Itinerary). Załóżmy, że do odnalezienia węzła (Node) oraz
obiektów Shipping Operation będziemy mogli użyć REPOZYTO-
RIUM i skorzystać z otrzymanego identyfikatora węzła. W jaki więc
sposób węzły mapują się na etapy (Leg)? Na podstawie kodu rodzaju
operacji (operationType) możemy podzielić listę węzłów na pary wypły-
nięcie (ang. depart)/przypłynięcie (ang. arrive). Każda z takich par stanowi
jeden etap (Leg).

Rysunek 14.5.
Tłumaczenie trasy
w usłudze
przeszukiwania
sieci połączeń

Atrybuty dla każdej pary węzłów będą mapowane następująco:


departureNode.shippingOperation.vesselVoyageId → leg.vesselVoyageId
departureNode.shippingOperation.date → leg.loadDate
departureNode.locationCode → leg.loadLocationCode
arrivalNode.shippingOperation.date → leg.unloadDate
arrivalNode.locationCode → leg.unloadLocationCode

MAPA KONTEKSTÓW 391

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

To będzie ten jeden obiekt, nad utrzymaniem i rozwojem którego muszą


pracować oba zespoły. Jego projekt powinien umożliwiać łatwe stworzenie
testów jednostkowych, a wspólne opracowywanie przez zespoły zestawu
takich testów byłoby szczególnie dobrym pomysłem. W przeciwnym razie
mogą one pójść oddzielnymi drogami.

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();

392 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

699b15244ae879c728b313782703a4d7
6
List constraintLocations =
translator.convertConstraints(spec);
// Pobierz dostęp do NetworkTraversalService
List pathNodes =
traversalService.findPath(constraintLocations);
Itinerary result = translator.convert(pathNodes);
return result;
}

Całkiem nieźle. Dzięki KONTEKSTOM ZWIĄZANYM każdy


z modeli został utrzymany w całkiem przejrzystym stanie, co pozwala
zespołom na pracę w dużym stopniu niezależnie. Jeżeli początkowe zało-
żenia były poprawne, cała konstrukcja zadziałałaby prawdopodobnie pra-
widłowo (wrócimy do tego później w tym rozdziale).
Wzajemne nakładanie się obu kontekstów jest stosunkowo nie-
wielkie. Interfejs usługi trasowania odizolowuje pozostałą część projektu
KONTEKSTU rezerwacji od zdarzeń zachodzących w części odszuku-
jącej trasę. Testowanie interfejsu jest proste, ponieważ jest on zbudowany
z FUNKCJI BEZ EFEKTÓW UBOCZNYCH. Jednym z sekretów
umożliwiających komfortowe współistnienie z innymi KONTEKSTAMI
jest dysponowanie efektywnym zestawem testów dla interfejsów. W trak-
cie negocjowania ograniczenia arsenału nuklearnego z Rosjanami prezy-
dent Reagan stwierdził kiedyś: „Ufaj, lecz sprawdzaj”1.
Stworzenie zestawu testów automatycznych wstawiających specyfika-
cje trasy (Route Specification) do usługi trasowania (Routing Service)
oraz sprawdzających zwracany plan podróży (Itinerary) powinno być
względnie łatwe.

Konteksty modeli istnieją zawsze, lecz bez poświęcenia im świadomej


uwagi mogą na siebie zachodzić oraz zmieniać się bez ostrzeżenia. Poprzez
bezpośrednie zdefiniowanie KONTEKSTÓW ZWIĄZANYCH oraz
MAPY KONTEKSTÓW Twój zespół może rozpocząć nadzorowanie
procesu unifikacji modeli oraz łączenia tych, które różnią się od siebie.

Testowanie na granicach KONTEKSTU


Szczególnie istotne jest testowanie punktów kontaktu z innymi KON-
TEKSTAMI ZWIĄZANYMI. Testy pozwalają zrekompensować subtel-
ności tłumaczeń, a także komunikacji niskopoziomowej, istniejących

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.

MAPA KONTEKSTÓW 393

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.

Organizacja oraz dokumentacja


MAP KONTEKSTÓW
W tej kwestii istnieją tylko dwa ważne punkty:
1. KONTEKSTY ZWIĄZANE powinny mieć takie nazwy, aby moż-
liwe było rozmawianie o nich. Te nazwy powinny być dołączone do
JĘZYKA WSZECHOBECNEGO zespołu.
2. Każdy powinien wiedzieć, gdzie leżą granice, a także być w stanie
rozpoznać KONTEKST każdego fragmentu kodu oraz każdej sytuacji.
Drugie wymaganie mogłoby zostać spełnione na różne sposoby,
w zależności od kultury zespołu. Po zdefiniowaniu KONTEKSTÓW
ZWIĄZANYCH naturalną koleją rzeczy wydaje się rozdzielenie kodu
różnych KONTEKSTÓW do różnych MODUŁÓW. Powstaje pytanie,
w jaki sposób śledzić, który MODUŁ należy do którego KONTEKSTU.
W tym celu może być przydatna konwencja nazewnicza lub dowolny
inny łatwy mechanizm umożliwiający uniknięcie nieporozumień.
Równie ważną kwestią jest komunikacja pojęciowych granic w taki
sposób, aby każdy członek zespołu rozumiał je w ten sam sposób. Do
celów komunikacyjnych osobiście lubię używać nieformalnych diagramów
przypominających użyte w tym przykładzie. Mogą także być używane
bardziej rygorystyczne diagramy lub listy tekstowe, obrazujące wszystkie
pakiety w danym KONTEKŚCIE wraz z ich punktami kontaktu oraz
mechanizmami odpowiedzialnymi za połączenia oraz tłumaczenie.
Z takiego podejścia zadowolone mogą być niektóre zespoły, podczas
gdy innym mogą wystarczyć ustne ustalenia oraz duża liczba dyskusji.
W każdym przypadku dołączenie MAPY KONTEKSTÓW do dys-
kusji jest istotne, jeżeli zakładamy, że ich nazwy muszą wejść w skład
JĘZYKA WSZECHOBECNEGO. Nie mów: „Kod zespołu Jurka zmie-
nił się, co sprawia, że musimy również zmienić kod komunikujący się
z nim”. Powiedz w zamian: „Model sieci transportu uległ zmianie, dlatego też
musimy zmienić obiekt tłumacza dla kontekstu rezerwacji”.

394 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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

RELACJE POMIĘDZY KONTEKSTAMI ZWIĄZANYMI 395

699b15244ae879c728b313782703a4d7
6
JĄDRO WSPÓŁDZIELONE

W sytuacji, gdy ograniczony jest zakres integracji funkcjonalnej,


narzut związany z CIĄGŁĄ INTEGRACJĄ może być uznawany za zbyt
wysoki. Może zdarzyć się tak szczególnie w przypadku, gdy zespoły nie
mają ani umiejętności, ani organizacji politycznej, aby utrzymywać ciągłą
integrację, lub gdy pojedynczy zespół jest zbyt duży i niesprawny. W takiej
sytuacji można by zdefiniować KONTEKSTY ZWIĄZANE lub zamienić
jeden zespół na wiele mniejszych.
***
Nieskoordynowane zespoły pracujące nad blisko powiąza-
nymi aplikacjami mogą przez pewien czas zdążać do przodu,
lecz efekt ich pracy może nie pasować do siebie. W rezultacie
mogą one spędzić więcej czasu na opracowywaniu oraz wyposa-
żaniu warstw tłumaczenia niż na CIĄGŁEJ INTEGRACJI, jed-
nocześnie zwiększając poświęcony wysiłek i tracąc zyski z używa-
nia wspólnego JĘZYKA WSZECHOBECNEGO.
W wielu projektach, które widziałem, warstwa infrastruktury współ-
dzielona była przez zespoły pracujące raczej niezależnie. Analogiczne
podejście mogłoby też dobrze funkcjonować w dziedzinie. Pełna synchro-
nizacja całego modelu oraz kodu bazowego mogłaby być zbyt dużym
narzutem, jednak ostrożnie wybrany podzbiór może małym kosztem przy-
nieść większość korzyści.

396 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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.

JĄDRO WSPÓŁDZIELONE 397

699b15244ae879c728b313782703a4d7
6
ZESPOŁY PROGRAMISTYCZNE
KLIENTA – DOSTAWCY

Bardzo często jeden podsystem po prostu zasila inny, to znaczy kompo-


nent „podrzędny” wykonuje analizę lub inne funkcje, które przekazują
bardzo mało informacji z powrotem do komponentu „nadrzędnego”.
W ten sam jednokierunkowy sposób rozkładają się również wszystkie
zależności. Te dwa podsystemy służą zazwyczaj bardzo różnym grupom
użytkowników, wykonującym inne zadania, w których przydatne mogą
być różne modele. Także zestaw narzędzi może być inny, co sprawia, że
kod programu nie może być współdzielony.
***
Oba systemy w sposób naturalny rozdzielają się do dwóch KON-
TEKSTÓW ZWIĄZANYCH. Jest to szczególnie prawdziwe w sytuacji,
gdy dwa komponenty do implementacji wymagają różnych umiejętności
lub narzędzi. Tłumaczenie jest zdecydowanie prostsze, gdy musi być
wykonywane tylko w jednym kierunku. Jednak w zależności od relacji
politycznych obu zespołów mogą wystąpić problemy.
Beztroskie programowanie zespołu nadrzędnego może zostać
zahamowane w przypadku, gdy zespół podrzędny ma prawo
wnieść do zmian sprzeciw lub też gdy wnioskowanie o zmiany
jest zbyt uciążliwe. Zespół nadrzędny może również krępować
obawa o uszkodzenie systemu podrzędnego. W tym samym czasie
zespół podrzędny może być bezsilny i skazany na łaskę priory-
tetów zespołu nadrzędnego.
Komponent podrzędny wymaga pewnych rzeczy od nadrzędnego,
jednak ten drugi nie jest odpowiedzialny za efekty pracy pierwszego.

398 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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ć

ZESPOŁY PROGRAMISTYCZNE KLIENTA – DOSTAWCY 399

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.

400 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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:

ZESPOŁY PROGRAMISTYCZNE KLIENTA – DOSTAWCY 401

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

402 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

699b15244ae879c728b313782703a4d7
6
KONFORMISTA

Jeżeli dwa zespoły w relacji nadrzędny – podrzędny nie mogą w efek-


tywny sposób być kierowane z tego samego miejsca, nie zadziałają również
poprawnie wzorce współpracy w rodzaju ZESPOŁÓW KLIENTA –
– DOSTAWCY. Próba ich naiwnego zastosowania przysporzy zespołowi
podrzędnemu kłopotów. Taka sytuacja może mieć miejsce w przypadku
dużej firmy, w której dwa zespoły są od siebie daleko w hierarchii zarzą-
dzania lub też współdzielony nadzór jest obojętny dla relacji obu zespo-
łów. Można ją również spotkać pomiędzy zespołami z różnych firm, gdy
działalność klienta nie jest szczególnie ważna dla dostawcy. Być może
dostawca ma wielu małych klientów lub zmienia kierunek marketingowy
i przestaje doceniać starych klientów. Może być po prostu źle zarządzany,
a może już wypadł z biznesu. Bez względu na przyczynę takiej sytuacji,
zespół podrzędny jest zdany na siebie.
W przypadku dwóch zespołów programistycznych w relacji
nadrzędny – podrzędny, w której zespół nadrzędny nie ma moty-
wacji, aby zaspokajać potrzeby zespołu podrzędnego, ten ostatni
jest bezradny. Wprawdzie altruizm może motywować programi-
stów zespołu nadrzędnego do składania obietnic, jednak bardzo
rzadko są one spełniane. Wiara w tego rodzaju dobre intencje
skłania zespół podrzędny do tworzenia planów na bazie funk-
cjonalności, które nigdy nie będą dostępne. Projekt podrzędny

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.

404 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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

406 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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

WARSTWA ZAPOBIEGAJĄCA USZKODZENIU 407

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.

Projektowanie interfejsu WARSTWY


ZAPOBIEGAJĄCEJ USZKODZENIU
Publiczny interfejs WARSTWY ZAPOBIEGAJĄCEJ USZKODZENIU
przyjmuje zazwyczaj postać zestawu USŁUG, chociaż niekiedy może
być także ENCJĄ. Utworzenie całej nowej warstwy odpowiedzialnej za
tłumaczenie pomiędzy semantyką dwóch systemów daje nam możliwość
utworzenia ponownej abstrakcji zachowania innego systemu i zaofero-
wania jego usług oraz informacji dla naszego systemu w sposób zgodny
z naszym modelem. W tym modelu może nawet nie mieć sensu zapre-
zentowanie systemu zewnętrznego w postaci pojedynczego komponentu.
Być może najlepszym wyjściem będzie użycie wielu USŁUG (lub nie-
kiedy ENCJI), z których każda będzie mieć spójną odpowiedzialność
w kategoriach naszego modelu.

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

408 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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

WARSTWA ZAPOBIEGAJĄCA USZKODZENIU 409

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ż

410 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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ś

WARSTWA ZAPOBIEGAJĄCA USZKODZENIU 411

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

412 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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

WARSTWA ZAPOBIEGAJĄCA USZKODZENIU 413

699b15244ae879c728b313782703a4d7
6
ODDZIELNE DROGI

Musimy bezwzględnie zawężać wymagania. Dwa zestawy funkcjonal-


ności pozbawione niezbędnych relacji mogą być od siebie oddzielone.
***
Integracja jest zawsze kosztowna. Niekiedy korzyści z niej
są małe.
Oprócz zwyczajowego kosztu zarządzania zespołami integracja
narzuca kompromisy. Prosty specjalizowany model, który może zaspokoić
określone potrzeby, musi ustąpić miejsca modelowi bardziej abstrakcyj-
nemu, mogącemu obsłużyć wszystkie sytuacje. Być może jakaś całkowi-
cie inna technologia mogłaby udostępnić pewne funkcjonalności w bar-
dzo prosty sposób, jednak trudno jest się z nią zintegrować. Być może
nadążanie za którymś zespołem jest tak trudne, że żaden sposób nie
działa dobrze w chwili, gdy inne zespoły próbują z nim współpracować.
W wielu sytuacjach integracja nie dostarcza żadnych istotnych korzy-
ści. Jeżeli dwie części funkcjonalne nie korzystają nawzajem ze swoich
funkcjonalności lub nie wymagają interakcji pomiędzy współdzielonymi
przez siebie obiektami albo nie współdzielą w swoich operacjach żadnych
danych, wtedy integracja (nawet przez warstwę tłumaczenia) może nie
być konieczna. Fakt, że określone funkcjonalności są powiązane ze sobą
w pewnym przypadku użycia, nie oznacza wcale, że muszą być zinte-
growane.
Dlatego:
Zadeklaruj KONTEKST ZWIĄZANY w taki sposób, aby nie
był powiązany z żadnym innym, co pozwoli programistom odna-
leźć proste specjalizowane rozwiązania w małym zakresie.

414 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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

ODDZIELNE DROGI 415

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

416 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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

USŁUGA OTWARTEGO GOSPODARZA 417

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.

418 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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.

JĘZYK OPUBLIKOWANY 419

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.

420 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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

JĘZYK OPUBLIKOWANY 421

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

Pierwszy z nich, przyspieszywszy kroku,


nos rozbił na słoniowym boku o twardą jego skórę.
Więc do swych towarzyszy pięciu krzyknął:
— Już wiem o tym zwierzęciu, że jest najtwardszym murem.
...
Trzeci, podchodząc do zwierzęcia,
nie więcej miał od tamtych szczęścia.
Słoń trąbę swą rozprężał, a on dotknąwszy trąby dłonią, rzekł:
— Ja już wszystko wiem o słoniu: Słoń jest gatunkiem węża!

Wtedy powiedział ślepiec Czwarty, bardzo ciekawy i uparty:


— Chcę wiedzieć, czego nie wiem!
I kiedy sam przy słoniu stał, rzekł, obejmując mu kolano:
— Już wiem, że słoń jest drzewem!
...

422 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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ą!

I żaden z ślepców tych aż do dziś


nie chce się z innym ślepcem zgodzić,
część prawdy tylko znając.
Każdy przy swojej trwa opinii,
każdy ma rację swą jak inni,
lecz wspólnie jej nie mają.
...
— z Ballady o słoniu2 Johna Godfreya Saxe’a (1816 – 1887),
opartej na historii opisanej w Udanie, tekście hinduskim.
W zależności od swoich celów sterujących interakcją ze słoniem
niewidomi ludzie mogli wciąż uczynić pewien postęp, nawet jeżeli nie
zgadzali się w pełni z naturą tego słonia. Jeżeli nie byłaby wymagana inte-
gracja, nie miałby też znaczenia fakt, że modele nie są zunifikowane.
Gdyby jednak integracja była wymagana, mogliby oni nie być w stanie
dojść do zgody na temat tego, czym jest słoń, jednak otrzymaliby sporo
korzyści jedynie z prostego uświadomienia sobie, że nie zgadzają się ze
sobą. W ten sposób uniknęliby nieświadomego dyskutowania na wyklu-
czające się tematy.
Diagramy na rysunku 14.9 są reprezentacjami w języku UML modeli,
jakie niewidomi ludzie utworzyli w stosunku do słonia. Po ustanowie-
niu oddzielnych KONTEKSTÓW ZWIĄZANYCH sytuacja jest już na
tyle dla nich jasna, aby mogli utworzyć pewien sposób komunikacji ze
sobą o tych niewielu aspektach dziedziny, co do których się zgadzają.
Być może byłoby to położenie słonia.
Ponieważ niewidome osoby chciałyby wymienić więcej informacji
na temat słonia, wartość wynikająca ze współdzielenia pojedynczego KON-
TEKSTU ZWIĄZANEGO zwiększa się odpowiednio. Jednak unifikacja
zasadniczo odmiennych modeli stanowi wyzwanie. Żadna z osób praw-
dopodobnie nie porzuci swojego modelu i nie zaadaptuje żadnego innego.
W końcu człowiek, który dotknął ogona, wie, że słoń nie przypomina
drzewa, a taki model byłby dla niego bez znaczenia i nieprzydatny. Uni-
fikacja wielu modeli prawie zawsze oznacza konieczność utworzenia
nowego modelu.

2
J. Godfrey Saxe, Księga nonsensu, tłum. Antoni Marianowicz, Wydawnictwa
Artystyczne i Filmowe, Warszawa 1986 — przyp. red.

UNIFIKACJA SŁONIA 423

699b15244ae879c728b313782703a4d7
6
Rysunek 14.9.
Cztery konteksty
bez integracji

Rysunek 14.10.
Cztery konteksty
— minimalna
integracja

Przy użyciu pewnej dozy wyobraźni oraz ciągłej dyskusji (prawdo-


podobnie gorącej) osoby niewidome mogłyby ostatecznie uświadomić
sobie, że wspólnie opisywały i modelowały część większej całości. Do
wielu celów unifikacja typu część-całość nie musi wymagać wiele dodat-
kowej pracy. Przynajmniej w pierwszym etapie integracji wymagane będzie
tylko uświadomienie sobie, ile części będzie ze sobą związanych. Dla
pewnych potrzeb może być adekwatne rozpatrywanie słonia w charakterze
muru podtrzymywanego drzewami, z liną na jednym końcu i wężem na
drugim.

Rysunek 14.11.
Jeden kontekst
— przybliżona
integracja

Unifikacja różnych modeli słonia jest znaczenie prostsza od więk-


szości tego rodzaju połączeń. Niestety, sytuacja, w której dwa modele
opisują całkowicie różne części całości, jest wyjątkowa, chociaż jest to

424 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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

Tego rodzaju drugie podejście do integracji modelu zdaje się usuwać


przypadkowe lub niepoprawne aspekty indywidualnych modeli i tworzy
nowe pojęcia — w tym przypadku „zwierzę” z częściami „trąba”, „noga”,
„ciało” oraz „ogon”. Każda z tych części ma swoje własne właściwości oraz
jasne relacje z innymi częściami. Pomyślna unifikacja modelu w dużym
stopniu zależy od minimalizmu. Słoń jest mniej więcej wężem, jednak
w tym przypadku „mniej” jest prawdopodobnie ważniejsze od „więcej”.
Lepiej jest nie mieć możliwości tryskania wodą, niż dysponować niepo-
prawnymi zębami jadowymi.
Jeżeli celem będzie po prostu odnalezienie słonia, wtedy najzupeł-
niej wystarczy tłumaczenie pomiędzy sposobami wyrażania położenia

UNIFIKACJA SŁONIA 425

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.

Wybór strategii kontekstu modelu


Zawsze bardzo ważne jest narysowanie MAPY KONTEKSTÓW, aby poka-
zać obecną sytuację w danym momencie. Kiedy to już zrobisz, bardzo często
możesz chcieć zmienić rzeczywistość. Teraz możesz już w sposób świa-
domy wybierać granice oraz relacje KONTEKSTU. Oto kilka wskazówek.

Decyzja zespołowa lub wyższa


Najpierw zespoły muszą podjąć decyzję, w którym miejscu zdefiniować
KONTEKSTY ZWIĄZANE oraz jakiego rodzaju relacje mają być pomię-
dzy nimi. Decyzja ta musi zostać podjęta przez zespoły lub przynajmniej
przekazana do całego zespołu oraz zrozumiana przez każdego z jego
członków. W rzeczywistości tego rodzaju decyzje bardzo często wyma-
gają ustaleń poza własnym zespołem. Teoretycznie decyzje o tym, czy
rozszerzać, czy dzielić KONTEKSTY ZWIĄZANE, powinny być oparte
na rachunku zysków i strat, z uwzględnieniem wartości samodzielnych
akcji zespołu oraz wartości bezpośredniej i bogatej integracji. W rzeczy-
wistości sposób integrowania systemów determinują polityczne relacje
pomiędzy zespołami. Technicznie doskonalsza unifikacja może być nie-
wykonalna z powodu struktury raportowania. To kierownictwo może
podyktować niewydolne połączenie. Nie zawsze otrzymasz to, czego ocze-
kujesz, lecz przynajmniej powinieneś być w stanie wyciągnąć i zakomu-

426 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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.

Stawianie siebie w kontekście


Gdy pracujemy nad projektem informatycznym, jesteśmy przede wszyst-
kim zainteresowani częściami systemu zmienianymi przez nasz zespół
(„systemem w projektowaniu”), a w drugiej kolejności systemami, z któ-
rymi będą się one komunikować. W typowym przypadku system w projek-
towaniu będzie umieszczony w jednym lub dwóch KONTEKSTACH
ZWIĄZANYCH, nad którymi będą pracować zespoły programistyczne,
być może z kolejnym KONTEKSTEM lub dwoma w roli wspierającej.
Dodatkowo dochodzić będą jeszcze relacje pomiędzy tymi KONTEK-
STAMI a systemami zewnętrznymi. Stanowi to prosty typowy widok,
niezbędny, aby dać komuś pojęcie o ogólnych oczekiwaniach, jakie praw-
dopodobnie napotkasz.
W rzeczywistości stanowimy część podstawowego KONTEKSTU,
w którym pracujemy, i to powiązanie powinno być odzwierciedlone
w naszej MAPIE KONTEKSTÓW. Nie stanowi to problemu w przy-
padku, gdy jesteśmy świadomi tego związku i pamiętamy o tym, gdy
wykraczamy poza granice stosowania tej MAPY.

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.

WYBÓR STRATEGII KONTEKSTU MODELU 427

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.

Akceptacja tego, czego nie możemy zmienić


— wyznaczanie zewnętrznych systemów
Najlepiej jest rozpocząć od najprostszych decyzji. Niektóre podsystemy
nie będą w sposób oczywisty w żadnym z KONTEKSTÓW ZWIĄZA-
NYCH systemu w projektowaniu. Przykładem mógłby być duży stary
system, który nie zostanie od razu zastąpiony, a także system zewnętrzny
udostępniający potrzebne usługi. Takie obszary można natychmiast ziden-
tyfikować i przygotować się na wydzielenie ich z projektu.
W tym miejscu musimy wykazać się ostrożnością w przyjmowaniu
założeń. Wygodne wydaje się myślenie o każdym z tych systemów jako
tworzącym swój własny KONTEKST ZWIĄZANY, jednak większość
systemów zewnętrznych w słabym stopniu spełnia tę definicję. Po pierw-
sze, KONTEKST ZWIĄZANY jest zdefiniowany przez zamiar unifika-
cji modelu w określonych granicach. Oczywiście, możesz mieć kontrolę
nad utrzymywaniem starego systemu i zadeklarować ten zamiar lub też
zespół utrzymujący ten system może być dobrze skoordynowany i wyko-
nywać nieformalną postać CIĄGŁEJ INTEGRACJI, jednak nie przyj-
muj tego za pewnik. Spróbuj to sprawdzić i jeżeli proces programowania
nie jest dobrze zintegrowany, wykaż się szczególną ostrożnością. Odkry-
wanie sprzeczności semantycznych w różnych częściach takich systemów
nie jest wcale rzadkością.

428 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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.

WYBÓR STRATEGII KONTEKSTU MODELU 429

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

430 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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ę

WYBÓR STRATEGII KONTEKSTU MODELU 431

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.

432 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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.

WYBÓR STRATEGII KONTEKSTU MODELU 433

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

Łączenie KONTEKSTÓW — ODDZIELNE


DROGI → JĄDRO WSPÓŁDZIELONE
Narzut związany z tłumaczeniem jest zbyt duży. Duplikacje są zbyt oczy-
wiste. Istnieje wiele motywacji leżących u podstaw łączenia KONTEK-
STÓW ZWIĄZANYCH. Jest to dość trudna rzecz do wykonania. Nie jest
wprawdzie na to zbyt późno, lecz wymagać będzie to pewnej cierpliwości.

434 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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,

436 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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

Wygaszanie starego systemu


Wszystkie dobre rzeczy muszą się kiedyś skończyć, nawet dobry program
komputerowy. Jednak nie dzieje się to samo. Stare systemy mogą być tak
wplecione w biznes oraz inne systemy, że wyciągnięcie ich stamtąd
może trwać wiele lat. Na szczęście, nie musi to być zrobione od razu.
Istnieje tyle różnych możliwości, że w tym miejscu mogę jedynie
pobieżnie prześlizgnąć się po całym temacie. Omówimy jednak bardzo
powszechny przypadek: stary system, codziennie używany przez biznes,
został ostatnio wsparty przez szereg bardziej nowoczesnych systemów,
które komunikują się ze starym poprzez WARSTWĘ ZAPOBIEGAJĄCĄ
USZKODZENIU.
Jedną z pierwszych czynności powinien być wybór strategii testo-
wania. Dla nowych funkcjonalności w nowych systemach powinny zostać
utworzone automatyczne testy jednostkowe. Jednak wygaszanie starego
systemu oznacza specjalne potrzeby w kwestii testowania. Niektóre orga-
nizacje wykorzystują przez pewien czas równolegle stare i nowe systemy.

438 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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

USŁUGA OTWARTEGO GOSPODARZA →


JĘZYK OPUBLIKOWANY
Do integracji z innymi systemami używałeś szeregu tymczasowych proto-
kołów, jednak obciążenie wynikające z ich utrzymywania dramatycznie
rośnie w sytuacji, gdy coraz więcej systemów chce uzyskać dostęp, a być
może interakcje stają się coraz trudniejsze do zrozumienia. Chciałbyś sfor-
malizować relacje pomiędzy systemami przy użyciu JĘZYKA OPUBLI-
KOWANEGO.
1. Jeżeli dostępny jest język będący standardem przemysłowym, spró-
buj go przetestować i użyj go, jeżeli tylko jest to możliwe.
2. Jeżeli natomiast nie jest dostępny żaden standard lub wcześniej opubli-
kowany język, rozpocznij od wyostrzenia DZIEDZINY GŁÓWNEJ
systemu, który będzie pełnić rolę gospodarza (patrz rozdział 15.).
3. W roli podstawowego języka wymiany użyj DZIEDZINY GŁÓW-
NEJ, jeżeli to możliwe, stosując standardowe paradygmaty do
wymiany danych w rodzaju języka XML.
4. Opublikuj nowy język dla (przynajmniej) wszystkich zaintereso-
wanych stron.
5. Jeżeli używana jest również architektura nowego systemu, również
ją opublikuj.
6. Dla każdego współdziałającego systemu utwórz warstwy tłumaczące.
7. Przełącz się na nowy system.
W tym momencie nowe systemy współpracujące powinny być w sta-
nie dołączyć się przy minimalnym zamieszaniu.
Pamiętaj, że JĘZYK OPUBLIKOWANY musi być stabilny, jednak
wciąż będziesz potrzebował pewnej swobody w celu zmiany modelu
gospodarza w trakcie tego nieskończonego procesu refaktoryzacji. Dla-
tego nie utożsamiaj języka wymiany z modelem gospodarza. Utrzymywanie ich
blisko siebie zmniejszy narzut związany z tłumaczeniem i będziesz mógł
wybrać dla swojego gospodarza wzorzec KONFORMISTY. Jeżeli jednak
kompromis pomiędzy kosztem a zyskami będzie to uzasadniać, zachowaj
sobie prawo do wzmocnienia warstwy tłumaczenia.

440 ROZDZIAŁ 14. UTRZYMYWANIE INTEGRALNOŚCI MODELU

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

James Clerk Maxwell,


Traktat o elektryczności i magnetyzmie z roku 1873
Powyższe cztery równania wraz z definicjami ich argumentów
oraz zasadami matematyki, na której one polegają,
wyrażają całość klasycznej dziewiętnastowiecznej
nauki o elektromagnetyzmie.

W jaki sposób możesz skoncentrować się na głównym problemie


i nie utonąć w morzu pobocznych zagadnień? ARCHITEKTURA WAR-
STWOWA oddziela pojęcia dziedziny od logiki technicznej, umożliwia-
jącej działanie systemu komputerowego. Jednak w dużych systemach
nawet ta odizolowana dziedzina może być skomplikowana w sposób unie-
możliwiający zarządzanie nią.
Destylacja jest procesem oddzielania składników mieszaniny w celu
wydobycia esencji w takiej postaci, która będzie bardziej wartościowa oraz
przydatna. Model zatem będzie destylacją wiedzy. Z każdą wykonaną
refaktoryzacją ku głębszemu zrozumieniu będziemy tworzyć abstrakcję
pewnego istotnego aspektu wiedzy dziedzinowej oraz jej priorytetów. Wra-
cając na chwilę do widoku strategicznego, w tym rozdziale przyjrzymy się
sposobom odróżnienia szerokich obszarów modelu oraz destylacji modelu
dziedziny jako całości.

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.

ROZDZIAŁ 15. DESTYLACJA

1
Dosłownie „DZIEDZINY RDZENIA” — przyp. tłum.

444 ROZDZIAŁ 15. DESTYLACJA

699b15244ae879c728b313782703a4d7
6
Rysunek 15.1.
Mapa nawigacji
dla strategicznej
destylacji

ROZDZIAŁ 15. DESTYLACJA 445

699b15244ae879c728b313782703a4d7
6
DZIEDZINA GŁÓWNA

W trakcie projektowania rozległego systemu występuje wiele współ-


pracujących komponentów, z których każdy jest skomplikowany
i całkowicie niezbędny do osiągnięcia sukcesu. Powoduje to, że
esencja modelu dziedziny, stanowiąca rzeczywisty skarb opisu-
jący biznes, może być zaciemniona oraz zaniedbana.
Trudny do zrozumienia system trudno jest też zmienić. Ciężko jest
też przewidzieć skutki takich zmian. Programista, znalazłszy się poza swoim
własnym obszarem umiejętności, może się zgubić. (Jest to szczególnie
prawdziwe podczas wprowadzania do zespołu nowych ludzi, jednak nawet
starzy członkowie zespołu mogą się zmagać z taką sytuacją, o ile kod nie
jest bardzo wymowny i dobrze zorganizowany). Zmusza to ludzi do spe-
cjalizacji. Kiedy dodatkowo programiści ograniczą swoje zadanie do okre-
ślonych modułów, zmniejszy to jeszcze bardziej wymagany transfer wiedzy.
Na podziale zadań na mniejsze, łatwiejsze do zarządzania części cierpi
płynność integracji systemu oraz swoboda w przydzielaniu pracy. W sytu-
acji, gdy programista nie uświadomi sobie, że dana logika istnieje w innej
części systemu, zwiększy się duplikacja kodu, a sam system stanie się jesz-
cze bardziej złożony.
To wszystko są konsekwencje każdego z trudnych do zrozumienia
projektów. Jednak utrata ogólnego obrazu dziedziny rodzi kolejne równie
poważne ryzyko.
Okrutna rzeczywistość sprawia, że nie wszystkie części pro-
jektu mogą być tak samo dopracowane. Muszą zostać ustanowione
priorytety. Aby model dziedziny stał się prawdziwym składnikiem

446 ROZDZIAŁ 15. DESTYLACJA

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ść

DZIEDZINA GŁÓWNA 447

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

448 ROZDZIAŁ 15. DESTYLACJA

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.

Kto wykonuje prace?


Najbardziej utalentowani technicznie członkowie zespołów programistycz-
nych bardzo rzadko mają wystarczającą wiedzę o dziedzinie. Ogranicza
to znacznie ich przydatność i wzmacnia tendencję do przypisywania ich
do komponentów pomocniczych, podtrzymując złośliwą prawidłowość,
w której brak wiedzy odciąga ich od pracy, która zbudowałaby ich wiedzę
dziedzinową.

DZIEDZINA GŁÓWNA 449

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.

450 ROZDZIAŁ 15. DESTYLACJA

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.

ZWIĘKSZANIE DESTYLACJI 451

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

452 ROZDZIAŁ 15. DESTYLACJA

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:

Rozwiązanie 1. Gotowy, standaryzowany produkt


Niekiedy możesz kupić implementację lub użyć kodu udostępnianego
na otwartej licencji.
Zalety:
 Mniej kodu do utworzenia.
 Przeniesienie na zewnątrz wysiłku związanego z utrzymaniem.
 Kod jest prawdopodobnie bardziej dojrzały, zastosowany w wielu
miejscach i dlatego może być odporny na błędy i bardziej kompletny
niż kod utworzony osobiście.
Wady:
 Wciąż będziesz musiał poświęcić czas na ocenę oraz zrozumienie
kodu przed jego użyciem.
 Ograniczona kontrola jakości w przemyśle sprawia, że nie będziesz
mógł liczyć na stabilność i poprawność kodu.
 Kod może być zbyt skomplikowany do Twoich celów. Integracja
może wymagać więcej pracy niż stworzenie minimalistycznej imple-
mentacji samemu.
 Obce elementy zazwyczaj nie integrują się w prosty sposób. Może
w nich istnieć oddzielny KONTEKST ZWIĄZANY. Nawet jeżeli
tak nie jest, to i tak odwoływanie się do ENCJI z innych pakietów
może być utrudnione.
 Kod może wprowadzić zależności związane z platformą, wersją
kompilatora itp.
Można zbadać dostępne na rynku rozwiązania modelujące poddzie-
dziny, jednak zazwyczaj nie są one warte wysiłku. Widziałem już szczę-
śliwe historie aplikacji z bardzo rozbudowanymi wymaganiami do prze-
pływu zadań, które używały do ich obsługi dostępnych zewnętrznych

PODDZIEDZINY OGÓLNE 453

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.

Rozwiązanie 2. Opublikowany projekt lub model


Zalety:
 Bardziej dojrzały niż model opracowany samemu; odzwierciedla
przemyślenia wielu osób.
 Dostępna natychmiast dokumentacja o dobrej jakości.
Wady:
 Może nie całkiem pasować do Twoich potrzeb lub też być zbyt
skomplikowany.
Tom Lehrer (artysta komediowy z lat 50. i 60.) powiedział kiedyś, że
jego sukcesem w matematyce było „Ściąganie! Plagiatowanie. Nie pozwól
nikomu uchronić się przed Twoim wzrokiem... Jednak zawsze bądź gotów
nazwać to badaniem”. Jest to całkiem dobra rada w odniesieniu do modelo-
wania dziedziny, a szczególnie atakowania PODDZIEDZIN OGÓLNYCH.
Taka metoda działa najlepiej, gdy dostępny jest szeroko rozpowszech-
niony model, jak chociażby jeden z tych umieszczonych w książce Analysis
Patterns (Fowler 1996 r.) (patrz rozdział 11.).
Kiedy w danym obszarze istnieje już wysoce sformalizowany i rygo-
rystyczny model, staraj się go wykorzystać. Księgowość i fizyka są dwoma
przykładami, które przychodzą mi na myśl. Nie tylko są one solidne
i uproszczone, lecz także zrozumiałe dla wszystkich ludzi, co zmniejsza
wysiłek związany z ich obecnym i przyszłym szkoleniem (patrz rozdział 10.,
omawiający wprowadzony formalizm).
Nie czuj się w obowiązku implementować wszystkich aspektów opu-
blikowanego modelu, jeżeli możesz zidentyfikować uproszczony podzbiór,
który będzie spójny oraz będzie zaspokajał Twoje potrzeby. W sytuacji,
w której istnieje dobrze rozpracowany i udokumentowany, a jeszcze lepiej
sformalizowany model, nie ma sensu wynajdywać koła na nowo.

454 ROZDZIAŁ 15. DESTYLACJA

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

Rozwiązanie 4. Samodzielna implementacja


Zalety:
 Łatwa integracja.
 Otrzymujesz tylko to, czego potrzebujesz, i nic więcej.
 Możesz zatrudnić na kontrakt tymczasowych programistów.

PODDZIEDZINY OGÓLNE 455

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-

456 ROZDZIAŁ 15. DESTYLACJA

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

PODDZIEDZINY OGÓLNE 457

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

Strategia projektu logistyki morskiej Strategia projektu ubezpieczeniowego


Zalety Zalety
• OGÓLNY model oddzielony od RDZENIA. • OGÓLNY model oddzielony od RDZENIA.
• Dojrzały model RDZENIA, co pozwala zmienić Wady
zasoby bez opóźniania pracy. • Model RDZENIA nie został rozwinięty,
• Zespół wiedział dokładnie, czego potrzebował. dlatego uwaga poświęcona innym problemom
• Krytyczna funkcjonalność pomocnicza była mniejsza.
dla harmonogramu przewozów • Nieznane wymagania doprowadziły do próby
międzynarodowych. stworzenia pełnego uogólnienia, chociaż mogła
• Programista na krótkim kontrakcie wystarczyć prostsza konwersja, dotycząca jedynie
terminowym użyty do OGÓLNEGO zadania. Ameryki Północnej.
Wady • Przypisani zostali długoletni programiści, którzy
• Odciągnięcie najlepszego programisty od pracy mogli w tym czasie pogłębiać swoją wiedzę
nad RDZENIEM. dziedzinową.

Osobom technicznym zajmowanie się możliwymi do określenia pro-


blemami w rodzaju konwersji stref czasowych wydaje się sprawiać przy-
jemność. W ten sposób łatwo jest im uzasadnić poświęcanie tym proble-
mom czasu. Jednak zdyscyplinowane spojrzenie na priorytety wskazuje
zazwyczaj na DZIEDZINĘ GŁÓWNĄ.

458 ROZDZIAŁ 15. DESTYLACJA

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.

Zarządzanie ryzykiem projektowym


Projekty zwinne zarządzają ryzykiem, podejmując zazwyczaj najbardziej
ryzykowne zadania najwcześniej, jak to tylko możliwe. W szczególności
XP wymaga natychmiast działającego od początku do końca systemu.

PODDZIEDZINY OGÓLNE 459

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.

460 ROZDZIAŁ 15. DESTYLACJA

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

OPIS WIZJI DZIEDZINY 461

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.

A to, chociaż jest bardzo ważne,


To jest część OPISU WIZJI DZIEDZINY nie stanowi OPISU WIZJI DZIEDZINY
Automatyzacja fabryki półprzewodników Automatyzacja fabryki półprzewodników
Model dziedziny będzie reprezentować stan mate- Oprogramowanie powinno być dostępne internecie
riałów oraz wyposażenia w fabryce wafli krzemo- poprzez wykorzystanie serwletów, jednak powinno
wych w taki sposób, aby możliwe było przeprowa- być stworzone w sposób dopuszczający inne
dzenie wymaganych audytów oraz automatycznego interfejsy.
trasowania produktu. Wszędzie, gdzie to możliwe, powinny być uży-
Model nie będzie obejmować zasobów ludzkich wane technologie będące standardem przemy-
wymaganych w procesie, jednak musi umożliwiać słowym, aby zapobiec programowaniu dedyko-
selektywną automatyzację procesu poprzez funk- wanemu, przez co zostaną zmniejszone koszty
cjonalność pobierania przepisów. zarządzania, a także zwiększony dostęp do ze-
Reprezentacja stanu fabryki powinna być zro- wnętrznej wiedzy eksperckiej. Preferowane jest
zumiała dla kierowników, aby umożliwić im lepsze korzystanie z rozwiązań otwartych (jak na przy-
zrozumienie i wspierać ich w podejmowaniu lep- kład serwer WWW Apache).
szych decyzji. Serwer WWW będzie działać na dedykowa-
nym serwerze. Aplikacja będzie działać na poje-
dynczym dedykowanym serwerze.

***

462 ROZDZIAŁ 15. DESTYLACJA

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

OPIS WIZJI DZIEDZINY 463

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.

464 ROZDZIAŁ 15. DESTYLACJA

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.

RDZEŃ WYRÓŻNIONY 465

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

466 ROZDZIAŁ 15. DESTYLACJA

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.

Dokument destylacji jako narzędzie procesowe


Teoretycznie w projekcie XP dowolna para osób (dwóch programistów
pracujących razem) może zmienić dowolny kod w systemie. W praktyce nie-
które ze zmian mają duże konsekwencje i wymagają większej konsultacji

RDZEŃ WYRÓŻNIONY 467

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.

468 ROZDZIAŁ 15. DESTYLACJA

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.

SPÓJNE MECHANIZMY 469

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.

470 ROZDZIAŁ 15. DESTYLACJA

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.

Innym przykładem SPÓJNEGO MECHANIZMU byłby szkielet do


tworzenia SPECYFIKACJI obiektów, wspierający wymagane od nich
podstawowe operacje łączenia oraz porównywania. Używając tego rodzaju
szkieletu, DZIEDZINA GŁÓWNA wraz z OGÓLNYMI PODDZIE-
DZINAMI mogą zadeklarować swoje SPECYFIKACJE w jasnym i łatwym
do zrozumienia języku opisanym w tym wzorcu (patrz rozdział 10.). Skom-
plikowane operacje związane z wykonywaniem porównywania oraz łącze-
nia mogą być pozostawione szkieletowi.
***

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.

SPÓJNE MECHANIZMY 471

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.

472 ROZDZIAŁ 15. DESTYLACJA

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.

Destylacja do stylu deklaratywnego


Projekt deklaratywny wraz ze „stylem deklaratywnym” stanowią temat
rozdziału 10., jednak ten styl projektowania wymaga specjalnej wzmianki
w tym rozdziale, poświęconym destylacji strategicznej. Wartość destylacji
polega na umożliwieniu nam ujrzenia tego, co właściwe robimy — przebicia
się przez esencję bez rozpraszania się przez nieistotne szczegóły. Ważne
części DZIEDZINY GŁÓWNEJ mogą podążać śladem stylu deklaratyw-
nego, podczas gdy projekt wspierający udostępnia ekonomiczny język do
wyrażania pojęć oraz reguł RDZENIA przy jednoczesnej hermetyzacji
środków obliczeniowych oraz ich wymuszeniu.
SPÓJNE MECHANIZMY są zdecydowanie bardziej przydatne, gdy
umożliwiają dostęp przez INTERFEJSY UJAWNIAJĄCE ZAMIAR,
wraz z pojęciowo spójnymi ASERCJAMI oraz FUNKCJAMI BEZ EFEK-
TÓW UBOCZNYCH. MECHANIZMY oraz projekt elastyczny umoż-
liwiają DZIEDZINIE GŁÓWNEJ stworzenie znaczących opisów zamiast
wywoływania zagmatwanych funkcji. Jednak zdecydowanie korzystna jest
sytuacja, gdy część samej DZIEDZINY GŁÓWNEJ uzyskuje przełom do
głębokiego modelu i zaczyna funkcjonować w roli języka, który może wyra-
zić najważniejsze scenariusze aplikacji w sposób elastyczny i precyzyjny.
Model dogłębny bardzo często występuje z odpowiadającym mu pro-
jektem elastycznym. W przypadku, gdy ten drugi osiąga dojrzałość, udo-
stępnia łatwy do zrozumienia zestaw elementów, które mogą być połą-
czone w sposób jednoznaczny w celu wykonania złożonych zadań lub
wyrażenia złożonych informacji, zupełnie w ten sam sposób, jak słowa
tworzące zdania. W tym momencie kod klienta przybiera styl deklaratywny
i może być oddestylowany w znacznie większym stopniu.
Faktoryzacja OGÓLNYCH PODDZIEDZIN zmniejsza zamieszanie,
a SPÓJNE MECHANIZMY służą do hermetyzacji złożonych operacji.
Takie podejście pozostawia bardziej skoncentrowany model z mniejszą
liczbą kwestii odwracających uwagę, które nie dodają wartości do sposobu,
w który użytkownicy wykonują swoje czynności. Jednak prawie nigdy nie
uda się znaleźć dobrego miejsca dla wszystkich rzeczy w modelu dziedziny,
poza jego RDZENIEM. RDZEŃ ODDZIELONY podejmuje bezpo-
średnią próbę strukturalnego oddzielenia DZIEDZINY GŁÓWNEJ...

DESTYLACJA DO STYLU DEKLARATYWNEGO 473

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

474 ROZDZIAŁ 15. DESTYLACJA

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.

Koszt utworzenia RDZENIA


ODDZIELONEGO
Oddzielanie RDZENIA zaciemni niekiedy relacje z ściśle związanymi
klasami poza RDZENIEM, a nawet je bardziej skomplikuje. Jednak ten
koszt jest zdecydowanie warty poniesienia wobec korzyści z wyjaśnienia
DZIEDZINY GŁÓWNEJ oraz sprawienia, że jest z nią łatwiej pracować.
RDZEŃ ODDZIELONY pozwoli na zwiększenie spójności DZIE-
DZINY GŁÓWNEJ. Istnieje wiele istotnych sposobów rozbicia modelu.
Może to niekiedy spowodować, że w trakcie tworzenia RDZENIA
ODDZIELONEGO ładna spójność MODUŁU zostanie zniszczona, co
spowoduje jej poświęcenie na rzecz ujawnienia spójności DZIEDZINY
GŁÓWNEJ. Będzie to stanowić zysk netto, ponieważ największa wartość
dodana aplikacji przedsiębiorstwa pochodzi z właściwych temu przedsię-
biorstwu aspektów modelu.
Oczywiście, innym kosztem będzie fakt, iż oddzielanie RDZENIA
pochłania dużo wysiłku. Należy przyznać, że decyzja o zastosowaniu
RDZENIA ODDZIELONEGO może potencjalnie zaangażować progra-
mistów do zmian w całym systemie.

RDZEŃ ODDZIELONY 475

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.

Rozwijanie decyzji zespołowych


Podobnie jak w przypadku wielu strategicznych decyzji projektowych, do
RDZENIA ODDZIELONEGO musi podejść razem cały zespół. Ta czyn-
ność wymaga istnienia procesu podejmowania decyzji zespołowych oraz
zespołu na tyle zdyscyplinowanego oraz skoordynowanego, aby te decy-
zje podejmować. Wyzwaniem będzie ograniczenie wszystkich członków
zespołu, aby używali tej samej definicji RDZENIA, a jednocześnie unik-
nięcie paraliżu w podejmowaniu tych decyzji. Ponieważ DZIEDZINA
GŁÓWNA rozwija się podobnie do każdego innego aspektu projektu,
doświadczenie zdobywane w trakcie pracy z RDZENIEM ODDZIELO-
NYM będzie prowadzić do nowych przemyśleń nad tym, co jest zasadnicze,
a co stanowi jedynie element wspierający. Takie przemyślenia powinny
wpływać na ulepszoną definicję DZIEDZINY GŁÓWNEJ oraz MODU-
ŁÓW RDZENIA ODDZIELONEGO.
Oznacza to, że nowe przemyślenia muszą być uwspólniane z zespo-
łem na bieżąco, jednak żadna osoba (lub para programistów) nie może
pracować nad nimi jednostronnie. Każdy proces wynikający ze wspólnej
decyzji — niezależnie od tego, czy jest to kompromis, czy dyrektywa lidera
zespołu — musi być na tyle elastyczny, aby umożliwiać powtarzalne
modyfikacje kierunku, w którym podąża. Komunikacja musi być na tyle
efektywna, aby utrzymywać wszystkich w jednym widoku RDZENIA.

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.

476 ROZDZIAŁ 15. DESTYLACJA

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

Ta aplikacja nie jest zaprojektowana dla działu sprzedaży. Będzie ona


używana na linii frontu operatorów firmy.
Spróbujmy więc przypisać wszystkie problemy z finansami (prawdo-
podobnie równie ważne) do roli pomocniczej. Ktoś już niektóre z tych
elementów przeniósł do oddzielnego pakietu (Billing). Możemy taki
podział zachować i dalej uważać, że odgrywa rolę pomocniczą.
Cała nasza uwaga powinna być skupiona na obsłudze ładunków,
czyli dostarczeniu ładunku zgodnie z wymaganiami klienta. Wydobycie
klas najbardziej bezpośrednio związanych z tymi działaniami tworzy
RDZEŃ ODDZIELONY w nowym pakiecie o nazwie Delivery. Zostało
to zaprezentowane na rysunku 15.3.

RDZEŃ ODDZIELONY 477

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,

478 ROZDZIAŁ 15. DESTYLACJA

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

RDZEŃ ODDZIELONY 479

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.

480 ROZDZIAŁ 15. DESTYLACJA

699b15244ae879c728b313782703a4d7
6
RDZEŃ ABSTRAKCYJNY

Nawet model DZIEDZINY GŁÓWNEJ zazwyczaj ma tak wiele szcze-


gółów, że komunikacja całego obrazu może być trudna.
***
Z dużym modelem radzimy sobie zazwyczaj, dzieląc go na węższe
poddziedziny, które są na tyle małe, aby można je było objąć i umieścić
w oddzielnych MODUŁACH. Ten styl pakowania, polegający na redukcji
złożoności, bardzo często sprawia, że złożonymi modelami można zarzą-
dzać. Niekiedy jednak utworzenie oddzielnych MODUŁÓW może zaciem-
nić, a nawet skomplikować interakcje pomiędzy poddziedzinami.
Jeżeli pomiędzy poddziedzinami w oddzielnych MODUŁACH
istnieje wiele interakcji, pomiędzy tymi MODUŁAMI musi zostać
utworzonych wiele połączeń lub też interakcje muszą być pośred-
nie, co zdecydowanie zaciemnia cały model.
Rozważ dzielenie modelu w sposób poziomy zamiast pionowego.
Polimorfizm daje nam wiele możliwości redukcji licznych szczegółowych
odmian różnych instancji obiektów typu abstrakcyjnego. Jeżeli większość
interakcji pomiędzy MODUŁAMI można wyrazić na poziomie polimor-
ficznych interfejsów, rozsądna może wydawać się refaktoryzacja tych typów
do specjalnego MODUŁU RDZENIA.
Nie szukamy tu wcale żadnej technicznej sztuczki. Takie podejście
jest cenną techniką jedynie wtedy, gdy polimorficzne interfejsy kore-
spondują z podstawowymi pojęciami w dziedzinie. W takim przypadku
utworzenie tych abstrakcji oddziela MODUŁY, destylując jednocześnie
coraz mniejszą i bardziej spójną DZIEDZINĘ GŁÓWNĄ.

RDZEŃ ABSTRAKCYJNY 481

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

Głęboka destylacja modelu


Destylacja nie operuje jedynie na wysokim poziomie oddzielania części
dziedziny z RDZENIA. Oznacza ona również ulepszanie poddziedzin,
szczególnie DZIEDZINY GŁÓWNEJ, poprzez stałą refaktoryzację ku
głębszemu zrozumieniu, w kierunku głębokiego modelu i projektu elastycz-
nego. Celem jest utworzenie projektu z modelem oczywistym — w prosty
sposób wyrażającym dziedzinę. Model dogłębny destyluje najbardziej pod-
stawowe aspekty dziedziny do postaci prostych elementów, które następnie
można łączyć w celu rozwiązania ważnych problemów aplikacji.

482 ROZDZIAŁ 15. DESTYLACJA

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.

Wybór celów refaktoryzacji


W sytuacji, gdy napotkasz źle zrefaktoryzowany duży system, pojawia się
pytanie, w którym miejscu należy zacząć. W społeczności XP odpowiedzią
wydaje się jeden z następujących punktów:
1. Rozpocznij gdziekolwiek, ponieważ wszystko musi być zrefaktory-
zowane.
2. Rozpocznij od miejsca sprawiającego problemy. Rozpocznę refak-
toryzację w tym miejscu, które muszę zmienić, aby zakończyć moje
określone zadanie.
Nie przywiązuję się do żadnej z tych zasad. Pierwsza z nich jest
zwykle niepraktyczna, z wyjątkiem nielicznych projektów, przy których
pracują wyłącznie najlepsi programiści. Druga wydaje się obchodzić pro-
blem, lecząc symptomy, a ignorując przyczynę i uciekając nieśmiało od
największych komplikacji. Ostatecznie kod będzie stawać się coraz trud-
niejszy do refaktoryzacji.
Jeżeli więc nie możesz zmienić wszystkiego i nie możesz również
kierować się największymi problemami, to co powinieneś zrobić?
1. W przypadku refaktoryzacji wymuszonej problemem sprawdź, czy
główny problem obejmuje DZIEDZINĘ GŁÓWNĄ lub relacje
RDZENIA z pomocniczymi elementami. Jeżeli odpowiedź jest twier-
dząca, znalazłeś przyczynę i powinieneś zacząć od jej naprawienia.
2. Jeżeli jednak masz luksus dowolnego wyboru przedmiotu refaktoryza-
cji, powinieneś najpierw skoncentrować się na lepszej faktoryzacji
DZIEDZINY GŁÓWNEJ i ulepszeniu podziału RDZENIA, a także
udoskonaleniu wspierających poddziedzin do postaci OGÓLNEJ
W ten sposób możesz uzyskać najwięcej korzyści z refaktoryzacji.

WYBÓR CELÓW REFAKTORYZACJI 483

699b15244ae879c728b313782703a4d7
6
484 ROZDZIAŁ 15. DESTYLACJA

699b15244ae879c728b313782703a4d7
6
ROZDZIAŁ 16.

Struktury dużej skali

Tysiące ludzi pracowało niezależnie, tworząc narzutę poświęconą ofiarom AIDS1.

M ała firma projektowa z Doliny Krzemowej została poproszona


o stworzenie symulatora systemu komunikacji z satelitami. Prace nad sys-
temem postępowały dobrze. Został utworzony PROJEKT STEROWANY
MODELEM, mogący wyrazić oraz zasymulować szeroki zakres warunków
sieciowych oraz awarii.

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

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

Nawet w przypadku podziału na MODUŁY duży model może być wciąż


zbyt skomplikowany, aby go zrozumieć. MODUŁY powodują podział
projektu na części łatwe do zarządzania, jednak wciąż może być ich dużo.
Modularność nie powoduje także wcale koniecznie jednolitości projektu.
Obiekt po obiekcie, pakiet po pakiecie stosowanych może być wiele decy-
zji projektowych, z których każda będzie mieć uzasadnienie, jednak będzie
zbyt szczegółowa.
Ścisły podział wymuszany przez KONTEKSTY ZWIĄZANE zapo-
biega uszkodzeniom oraz chaosowi, jednak struktury te same w sobie nie
ułatwiają ujrzenia systemu jako całości.
Pomóc może w tym destylacja, poprzez skupienie uwagi na DZIE-
DZINIE GŁÓWNEJ, a także zmuszenie pozostałych poddziedzin do
pełnienia roli pomocniczych. Jednak wciąż będzie konieczne zrozumienie
elementów pomocniczych oraz ich relacji z DZIEDZINĄ GŁÓWNĄ,
a także pomiędzy sobą. I chociaż DZIEDZINA GŁÓWNA idealnie
powinna być tak jasna i łatwa do zrozumienia, że niepotrzebne byłyby
dodatkowe wskazówki, to nie zawsze jednak znajdujemy się w takiej
sytuacji.
W projekcie o dowolnym rozmiarze ludzie muszą pracować nieco
bardziej niezależnie nad różnymi częściami systemu. Bez komunikacji
lub reguł narastać może bałagan wynikający z różnych stylów oraz roz-
wiązań tego samego problemu. Znacznie utrudnia to zrozumienie, w jaki

ROZDZIAŁ 16. STRUKTURY DUŻEJ SKALI 487

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.

488 ROZDZIAŁ 16. STRUKTURY DUŻEJ SKALI

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

ROZDZIAŁ 16. STRUKTURY DUŻEJ SKALI 489

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.

490 ROZDZIAŁ 16. STRUKTURY DUŻEJ SKALI

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.

PORZĄDEK EWOLUCYJNY 491

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.

492 ROZDZIAŁ 16. STRUKTURY DUŻEJ SKALI

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

METAFORA SYSTEMU 493

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

494 ROZDZIAŁ 16. STRUKTURY DUŻEJ SKALI

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

METAFORA SYSTEMU 495

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

496 ROZDZIAŁ 16. STRUKTURY DUŻEJ SKALI

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.

WARSTWY ODPOWIEDZIALNOŚCI 497

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.

498 ROZDZIAŁ 16. STRUKTURY DUŻEJ SKALI

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

WARSTWY ODPOWIEDZIALNOŚCI 499

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

Odpowiedzialności „wsparcia decyzji”


Ta warstwa programu udostępnia użytkownikowi narzędzia do plano-
wania oraz podejmowania decyzji. Mogłaby ona zautomatyzować niektóre
z nich (jak na przykład automatyczną zmianę trasy ładunków w miarę
zmiany harmonogramu transportu).
Obiekt Router jest USŁUGĄ, która pomaga agentowi rezerwują-
cemu wybrać najlepszy sposób wysłania ładunku (Cargo). Ta cecha powo-
duje umieszczenie obiektu Router w warstwie wsparcia decyzji.

500 ROZDZIAŁ 16. STRUKTURY DUŻEJ SKALI

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

WARSTWY ODPOWIEDZIALNOŚCI 501

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.

W jaki sposób struktura


wpływa na bieżący projekt?
Kiedy zostanie już zaadaptowana struktura dużej skali, kolejne decyzje
modelujące oraz projektowe muszą ją brać pod uwagę. Aby to zilustro-
wać, przypuśćmy, że do tego projektu z warstwami musimy dodać nową
funkcjonalność. Właśnie dowiedzieliśmy się od ekspertów dziedzinowych,
że dla pewnych kategorii materiałów niebezpiecznych zastosowanie mają
ograniczenia w wyznaczaniu trasy. Część materiałów nie może być dopusz-
czona do transportu w niektórych portach. Musimy sprawić, aby klasa
Router przestrzegała tych regulacji.

502 ROZDZIAŁ 16. STRUKTURY DUŻEJ SKALI

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

WARSTWY ODPOWIEDZIALNOŚCI 503

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.

504 ROZDZIAŁ 16. STRUKTURY DUŻEJ SKALI

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.

Wybór odpowiednich warstw


Odnalezienie dobrych WARSTW ODPOWIEDZIALNOŚCI lub dowol-
nej struktury o dużej skali wynika z dobrego zrozumienia dziedziny pro-
blemu oraz eksperymentowania. Jeżeli dopuścisz PORZĄDEK EWO-
LUCYJNY, wtedy wybór początkowego miejsca rozpoczęcia pracy nie

WARSTWY ODPOWIEDZIALNOŚCI 505

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.

506 ROZDZIAŁ 16. STRUKTURY DUŻEJ SKALI

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.

WARSTWY ODPOWIEDZIALNOŚCI 507

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

508 ROZDZIAŁ 16. STRUKTURY DUŻEJ SKALI

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.

WARSTWY ODPOWIEDZIALNOŚCI 509

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.

510 ROZDZIAŁ 16. STRUKTURY DUŻEJ SKALI

699b15244ae879c728b313782703a4d7
6
POZIOM WIEDZY

[POZIOM WIEDZY jest] grupą obiektów opisujących, w jaki sposób


inna grupa obiektów powinna się zachować [Martin Fowler Accoun-
tability, www.martinfowler.com]

POZIOM WIEDZY umożliwia rozwikłanie skomplikowanych proble-


mów w sytuacji, gdy musimy użytkownikowi pozwolić na modyfikację
pewnej części modelu przy jednoczesnym zachowaniu szerszego zestawu
reguł. Ten wzorzec odpowiada na wymagania programów umożliwiających
konfigurację swojego zachowania, w których role oraz relacje pomiędzy
ENCJAMI muszą być zmieniane w chwili instalacji, a nawet działania
programu.
W książce Analysis Patterns (Fowler 1996 r., str. 24 – 27) wzorzec
ten wyłania się z dyskusji na temat odpowiedzialności za modelowanie
wewnątrz organizacji, a następnie jest stosowany do reguł księgowania.
Chociaż wzorzec pojawia się w kilku rozdziałach, to jednak nie ma
własnego poświęconego tylko jemu, ponieważ różni się od innych wzor-
ców analitycznych w książce. Zamiast modelować dziedzinę w sposób
podobny do innych wzorców analitycznych, POZIOM WIEDZY kształ-
tuje model.
Aby dokładnie zrozumieć problem, rozważ modele „odpowiedzial-
ności”. Organizacje składają się z ludzi oraz innych mniejszych organi-
zacji. Modele te definiują role, jakie wszystkie te składniki odgrywają,
a także relacje pomiędzy nimi. Reguły zarządzające tymi rolami oraz rela-
cjami różnią się znacznie w zależności od rodzaju organizacji. W jednej
firmie „departament” może być kierowany przez „dyrektora” podlegają-
cego z kolei „wiceprezesowi”. W innej firmie „jednostka organizacyjna”
jest nadzorowana przez „menedżera”, który z kolei podlega „starszemu
menedżerowi”. Istnieją w końcu całe organizacje „macierzowe”, w których
każda osoba w innym celu raportuje do innego kierownika.
Typowa aplikacja przyjęłaby w tym momencie pewne założenia.
Gdyby nie pasowały, użytkownicy zaczęliby używać pola do wprowadza-
nia danych w inny sposób, niż było to zamierzone. Dowolne zachowanie,

POZIOM WIEDZY 511

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ń

Teraz jednak kierownictwo zdecydowało, że pracownicy biurowi


powinni zostać przeniesieni do planu emerytalnego zakładającego „okre-
ślony zysk”. Problem polega na tym, że personel biurowy jest opłacany
według stawki godzinowej, a ten model nie umożliwia łączenia różnych
planów płacowych. Musi on więc ulec zmianie.

512 ROZDZIAŁ 16. STRUKTURY DUŻEJ SKALI

699b15244ae879c728b313782703a4d7
6
Rysunek 16.16.
Niektórzy
pracownicy
zaprezentowani
przy użyciu
starego modelu

Kolejna propozycja modelu jest całkiem prosta — usuńmy ogra-


niczenia.

Rysunek 16.17.
Zaproponowany
model. Teraz zawiera
on zbyt mało
ograniczeń

Ten model pozwala, aby każdy pracownik był skojarzony z dowolnym


rodzajem planu emerytalnego, w taki sposób że dowolny pracownik admi-
nistracji może być zamieniony miejscem z każdym innym. Model ten został
odrzucony przez kierownictwo, ponieważ nie odzwierciedlał polityki
firmy, w której niektórzy pracownicy mogliby zostać wymienieni, a inni nie.
Dotyczy to również dozorców. Kierownictwo chciałoby otrzymać model
wymuszający politykę:
Pracownicy administracyjni są pracownikami opłacanymi godzinowo
z określonym planem emerytalnym.

Taka polityka sugeruje, że pole „job title”, czyli nazwa stanowiska,


reprezentuje teraz ważne pojęcie dziedziny. Programiści mogliby zrefak-
toryzować model, aby ujawnić to pojęcie w postaci pola Employee Type
(rodzaj pracownika).

POZIOM WIEDZY 513

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

Dostęp do edycji obiektu Employee Type będzie ograniczony do


„użytkownika specjalnego”, który będzie dokonywać zmian jedynie
w sytuacji zmiany polityki firmy. Zwykły użytkownik z działu zarządza-
nia zasobami ludzkimi będzie mógł jedynie zmienić dane użytkownika
lub przypisać go do innego rodzaju pracownika (Employee Type).

514 ROZDZIAŁ 16. STRUKTURY DUŻEJ SKALI

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)

Statyczny model może spowodować problemy. Jednak w przypadku


w pełni elastycznego systemu problem może spowodować możliwość
przypisania dowolnej relacji pomiędzy obiektami. Tego rodzaju system
byłby bardzo nieporęczny w użyciu i nie umożliwiałby wymuszenia wła-
snych reguł organizacji.
W pełni modyfikowalne oprogramowanie jest niepraktyczne dla
każdej organizacji. Nawet jeżeli mogłaby ona pozwolić sobie na zapłacenie
za taki program, to jednak jej struktura zmieniałaby się prawdopodobnie
zbyt często.
Dlatego takie oprogramowanie musi udostępniać opcje pozwalające
użytkownikowi na jego konfigurację, aby odzwierciedlić bieżącą struk-
turę organizacji. Problem polega na tym, że dodanie tego rodzaju opcji
do obiektów modelu sprawia, iż staje się on niepraktyczny. Im bardziej
zwiększysz jego elastyczność, tym bardziej skomplikowane staje się całe
rozwiązanie.
W przypadku aplikacji, w której role oraz relacje pomiędzy
ENCJAMI różnią się w różnych sytuacjach, złożoność może
zwiększyć się wielokrotnie. Ani bardzo ogólne modele, ani te
bardzo zindywidualizowane nie zaspokajają w pełni potrzeb
użytkowników. W celu pokrycia szeregu różnych przypadków
obiekty zawierają odwołania do innych typów lub też atrybuty
używane są na różne sposoby w różnych sytuacjach. Klasy posia-
dające te same dane oraz zachowanie mogą być powielane tylko
w celu obsługi różnych reguł.
Powoduje to, że w modelu jest zawarty kolejny model, mówiący
o innym modelu. POZIOM WIEDZY oddziela ten samodefiniujący się
aspekt modelu i ujawnia jego ograniczenia.

POZIOM WIEDZY 515

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.

Porównanie Terminologia Fowlera Terminologia POSA3


terminologii
Poziom wiedzy Metapoziom
POZIOMU
WIEDZY oraz Poziom operacji Poziom podstawowy
REFLEKSJI
Zachowajmy jednak jasność: narzędzia do refleksji dla języków pro-
gramowania nie nadają się do użytku podczas implementacji POZIOMU
WIEDZY dla modelu dziedziny. Te metaobiekty opisują strukturę oraz
zachowanie samych konstrukcji językowych. Natomiast POZIOM WIE-
DZY musi być utworzony dla zwykłych obiektów.
POZIOM WIEDZY udostępnia dwa przydatne rozróżnienia.
Po pierwsze, koncentruje się na dziedzinie aplikacji, w przeciwieństwie
do znanych przypadków użycia REFLEKSJI. Po drugie, nie zmierza do
obsługi pełnego uogólnienia. Podobnie jak SPECYFIKACJA może być
bardziej przydatna od ogólnego predykatu, tak również bardzo specjali-
zowany zestaw ograniczeń na zbiorze obiektów oraz ich relacjach może

3
POSA jest skrótem od architektury programów sterowanej wzorcami
(ang. Pattern Oriented Software Architecture) z książki Buschmanna z 1996 r.

516 ROZDZIAŁ 16. STRUKTURY DUŻEJ SKALI

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

POZIOM WIEDZY 517

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.

518 ROZDZIAŁ 16. STRUKTURY DUŻEJ SKALI

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

POZIOM WIEDZY 519

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.

520 ROZDZIAŁ 16. STRUKTURY DUŻEJ SKALI

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.

SZKIELET KOMPONENTÓW DOŁĄCZANYCH 521

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.

522 ROZDZIAŁ 16. STRUKTURY DUŻEJ SKALI

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

SZKIELET KOMPONENTÓW DOŁĄCZANYCH 523

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.

***

524 ROZDZIAŁ 16. STRUKTURY DUŻEJ SKALI

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.

SZKIELET KOMPONENTÓW DOŁĄCZANYCH 525

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

Jak ograniczająca powinna być struktura?


Wzorce struktur dużego rozmiaru omówione w tym rozdziale rozciągają
się od bardzo luźnych METAFOR SYSTEMU do restrykcyjnych SZKIE-
LETÓW KOMPONENTÓW DOŁĄCZANYCH. Oczywiście, możliwe
są też inne struktury. Nawet przy użyciu ogólnego wzorca strukturalnego
wciąż będzie istnieć pewien wybór dotyczący stopnia restrykcyjności tych
reguł.
Na przykład WARSTWY ODPOWIEDZIALNOŚCI nakazują
utworzenie pewnych pojęć modelu wraz z ich zależnościami, mógłbyś
jednak dodać reguły, które określiłyby wzorce komunikacji pomiędzy
warstwami.
Rozważ na przykład fabrykę produkcyjną, gdzie program kieruje każdą
część do urządzenia, w którym jest ona przetwarzana zgodnie z instruk-
cją. Poprawny proces jest nakazywany w warstwie polityki i wykonywany
w warstwie operacji. Wciąż jednak nieuniknione będą pomyłki wystę-
pujące w fabryce. Rzeczywista sytuacja nie będzie spójna z regułami pro-
gramu. W takiej sytuacji warstwa operacyjna musi odzwierciedlać rzeczy-
wistość, co oznacza, że gdy od czasu do czasu część zostanie umieszczona
w złym urządzeniu, taka wyjątkowa sytuacja musi zostać bezwarunkowo
zaakceptowana. Musi ona przy tym zostać zakomunikowana do wyższej
warstwy. Następnie warstwa podejmowania decyzji może użyć innych
polityk w celu naprawienia sytuacji, być może kierując daną część do
procesu naprawy lub odrzucając ją całkowicie. Jednak warstwa operacyjna
nie wie nic o wyższych warstwach. Dlatego komunikacja musi zachodzić
w sposób, który nie spowoduje utworzenia zależności niższych warstw
z wyższymi.

526 ROZDZIAŁ 16. STRUKTURY DUŻEJ SKALI

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

REFAKTORYZACJA KU LEPIEJ DOPASOWANEJ STRUKTURZE 527

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.

Komunikacja oraz samodyscyplina


W trakcie programowania oraz refaktoryzacji cały zespół musi podążać
za strukturą. W tym celu musi ona zostać zrozumiana przez wszystkich.
Jej terminologia oraz relacje muszą wejść do JĘZYKA WSZECH-
OBECNEGO.
Struktury dużej skali mogą dostarczyć słownictwo zarówno projek-
towi zmagającemu się z systemem w szerokim wymiarze, jak i poszcze-
gólnym osobom w celu podejmowania harmonijnych decyzji. Ponieważ
jednak większość struktur dużej skali stanowi jedynie luźne wskazówki
pojęciowe, zespoły muszą cechować się samodyscypliną.
Bez stałego zaangażowania osób uczestniczących w projekcie struk-
tury mają tendencję do rozpadania się. Związek struktury z poszczegól-
nymi elementami modelu lub implementacji nie jest zazwyczaj wyrażony
bezpośrednio w kodzie, a testy funkcjonalne nie polegają na strukturze.

528 ROZDZIAŁ 16. STRUKTURY DUŻEJ SKALI

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.

Restrukturyzacja przyczynia się


do projektu elastycznego
Dowolna zmiana struktury może prowadzić do dużej refaktoryzacji.
Struktura ewoluuje w miarę zwiększania się złożoności systemu oraz
pogłębiania zrozumienia dziedziny. W przypadku każdej zmiany struktury
zmieniony musi być cały system w celu dopasowania go do jej nowego kształtu.
Wymaga to oczywiście dużej ilości pracy.
Na szczęście, nie wygląda to aż tak źle, jak mogłoby się wydawać.
Zaobserwowałem, że znacznie łatwiej jest zmodyfikować projekt ze struk-
turą dużej skali niż ten bez niej. Wydaje się to prawdziwe nawet w sytu-
acji, gdy jedna struktura jest zmieniana na inną, dajmy na to METAFORA
na WARSTWY. Nie jestem w stanie całkowicie tego wyjaśnić. Częściowo
tłumaczy to fakt, iż łatwiej jest przearanżować coś, kiedy rozumiesz, jak
to w danej chwili funkcjonuje, a istniejąca wcześniej struktura ułatwia to
zadanie. Częściowo również dyscyplina wymagana do utrzymania wcze-
śniejszej struktury przenika do wszystkich aspektów systemu. Jednak sądzę,
że jest coś jeszcze, ponieważ jeszcze łatwiej jest zmienić system, który miał
dwie poprzednie struktury.
Nowa kurtka skórzana jest sztywna i niewygodna. Jednak po
pierwszym dniu noszenia skóra w łokciach zegnie się już kilka razy i stanie
się miększa. Po kilku kolejnych założeniach rozluźnią się miejsca na
barkach i kurtkę będzie łatwiej założyć. Po miesiącach używania skóra
stanie się elastyczna i komfortowa w użyciu. Podobnie wygląda sytuacja
z wielokrotnie przekształcanymi modelami. Będzie się w nich ciągle
zwiększać zasób osadzonej wiedzy. Zostaną również określone podsta-
wowe kierunki zmian, a cały wysiłek będzie szedł w kierunku ich uelastycznienia
i uproszczenia stabilnych aspektów systemu. W strukturze modelu zaczną
pojawiać się również szersze ZARYSY KONCEPCYJNE podstawowej
dziedziny.

REFAKTORYZACJA KU LEPIEJ DOPASOWANEJ STRUKTURZE 529

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.

530 ROZDZIAŁ 16. STRUKTURY 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ę?

Łączenie struktur dużej skali


z KONTEKSTAMI ZWIĄZANYMI

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

532 ROZDZIAŁ 17. ŁĄCZENIE STRATEGII

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.

ŁĄCZENIE STRUKTUR DUŻEJ SKALI Z KONTEKSTAMI ZWIĄZANYMI 533

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

Łączenie struktur dużej skali


oraz destylacji
Pojęcia struktur dużej skali oraz destylacji również uzupełniają się nawzajem.
Struktura dużej skali może pomóc wyjaśnić relacje wewnątrz DZIEDZINY
GŁÓWNEJ oraz pomiędzy PODDZIEDZINAMI OGÓLNYMI.

534 ROZDZIAŁ 17. ŁĄCZENIE STRATEGII

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

ŁĄCZENIE STRUKTUR DUŻEJ SKALI ORAZ DESTYLACJI 535

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.

Kto określa strategię?


Zazwyczaj zanim rozpocznie się tworzenie właściwej aplikacji, architektura
jest definiowana przez zespół mający w organizacji znacznie więcej władzy
od zespołu rozwijającego aplikację, a następnie przekazywana dalej. Jednak
nie musi to odbywać się w ten sposób. Takie podejście zazwyczaj nie
działa bardzo dobrze.
Decyzje strategiczne zgodnie z definicją muszą być stosowane
w całym projekcie. Istnieje wiele sposobów organizacji projektu, a ja nie
chciałbym zbyt wiele narzucać. Niemniej jednak, aby proces podejmowania
decyzji był efektywny, konieczne jest ustanowienie podstawowych zasad.

536 ROZDZIAŁ 17. ŁĄCZENIE STRATEGII

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.

KTO OKREŚLA STRATEGIĘ? 537

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.

Sześć podstawowych kryteriów


dotyczących podejmowania
strategicznych decyzji projektowych
Sześć kryteriów podejmowania strategicznych decyzji

Decyzja musi dotrzeć do całego zespołu.


Jeżeli cały zespół nie zna strategii i jej nie uwzględnia, wtedy oczywiście
jest ona nieistotna. To wymaganie pomaga ludziom zorganizować się wokół
centralnych zespołów architektonicznych z oficjalnym „autorytetem” —
tak aby wszędzie stosowane były te same reguły. Jak na ironię, architekci
wieży z kości słoniowej są często ignorowani lub obchodzeni. Jeżeli archi-
tektowi brakuje własnego praktycznego doświadczenia z prób zastosowania
swoich własnych reguł do prawdziwych aplikacji, co z kolei prowadzi do
niepraktycznych schematów, programiści nie mają po prostu innego
wyjścia.
W projekcie, w którym panuje dobra komunikacja, projekt strate-
giczny powstający w zespole aplikacyjnym może w rzeczywistości w bar-
dziej efektywny sposób dotrzeć do każdej osoby. Strategia będzie właściwa
i będzie mieć autorytet, aby zostać dołączona do inteligentnych decyzji
społeczności.

538 ROZDZIAŁ 17. ŁĄCZENIE STRATEGII

699b15244ae879c728b313782703a4d7
6
Niezależnie od systemu, bądź mniej zainteresowany autorytetem
wynikającym z decyzji kierownictwa niż rzeczywistymi relacjami strategii
z programistami.

Proces podejmowania decyzji musi uwzględniać uwagi.


Utworzenie organizacyjnego pryncypium w rodzaju struktury dużej skali
lub destylacji wymaga naprawdę głębokiego zrozumienia potrzeb projektu
oraz pojęć dziedziny. Jedynymi osobami, które mają taką wiedzę, są człon-
kowie zespołu tworzącego aplikację. To tłumaczy, dlaczego architektury
aplikacji utworzone przez zespoły architektów są tak rzadko pomocne, nie
umniejszając niepodważalnemu talentowi wielu architektów.
W odróżnieniu od technicznej infrastruktury oraz architektury, pro-
jekt strategiczny sam w sobie nie wymaga tworzenia dużego kodu, chociaż
ma wpływ na cały wysiłek programistyczny. Wymaga on natomiast zaanga-
żowania zespołów tworzących aplikację. Doświadczony architekt może
umieć słuchać pomysłów napływających z szeregu różnych zespołów
i ułatwiać proces tworzenia uogólnionego rozwiązania.
Jeden z technicznych zespołów architektów, z którym pracowałem,
wymieniał w rzeczywistości swoich członków z różnymi zespołami pro-
gramistów, które miały używać stworzonego szkieletu aplikacji. Taka
wymiana umożliwiała nabycie wiedzy praktycznej na temat wyzwań czy-
hających na programistów, a także jednocześnie przekazywała do zespo-
łów wiedzę na temat subtelności zastosowań szkieletu aplikacji. Projekt
strategiczny wymaga takiej samej ścisłej współpracy między zespołami.

Plan musi umożliwiać rozwój.


Wydajny proces tworzenia oprogramowania jest bardzo dynamiczny. Kiedy
zostaną już podjęte decyzje na najwyższym poziomie, wtedy zespół ma
mniej możliwości reakcji na zmianę. Takiej pułapce zapobiega PORZĄ-
DEK EWOLUCYJNY, wzmacniając rolę ciągłej zmiany struktury dużej
skali w odpowiedzi na pogłębiające się zrozumienie dziedziny.
W sytuacji, gdy zbyt wiele decyzji jest zarządzanych z góry, zespół
programistów może zostać spętany bez możliwości rozwiązywania bie-
żących problemów. Dlatego mimo iż harmonijne pryncypium może być
pomocne, musi ono wzrastać i zmieniać się w trakcie życia projektu. Nie
może również pozbawiać zbyt wielu możliwości programistów aplikacji,
których praca jest już wystarczająco trudna.
W przypadku, gdy możliwe jest uwzględnienie komunikatu zwrotnego
od programistów, w odpowiedzi na każdą przeciwność napotkaną w trakcie
tworzenia aplikacji powstają różnego rodzaju innowacje. Podobnie dzieje
się również w przypadku odkrycia nieoczekiwanych możliwości.

SZEŚĆ KRYTERIÓW PODEJMOWANIA STRATEGICZNYCH DECYZJI 539

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.

Projekt strategiczny wymaga minimalizmu oraz pokory.


Destylacja oraz minimalizm są zasadnicze dla poprawnego działania dowol-
nego dobrego projektu. Minimalizm jest nawet bardziej istotny dla pro-
jektu strategicznego. Nawet najmniejsza pomyłka może w przerażający
sposób uniemożliwiać pracę. Oddzielne zespoły architektów muszą zacho-
wać szczególną ostrożność, ponieważ mogą mieć mniejsze wyczucie puła-
pek czyhających na zespoły aplikacyjne. W tym samym czasie entuzjazm
architektów związany z ich podstawową funkcją zwiększa prawdopodo-
bieństwo podjęcia niepoprawnych decyzji. Taki fenomen widziałem już
wiele razy, a nawet raz sam mu uległem. Jeden dobry pomysł prowadził

540 ROZDZIAŁ 17. ŁĄCZENIE STRATEGII

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.

Obiekty są specjalizowane, programiści są uniwersalni.


Podstawą dobrego projektu obiektowego jest przypisanie każdemu obiek-
towi jasnej i wąskiej odpowiedzialności oraz zmniejszenie do absolutnego
minimum wzajemnej zależności. Niekiedy usiłujemy utworzyć w zespole
interakcje tak modelowe, jak powinny być w naszych aplikacjach. Dobry
projekt ma wiele osób wtykających nos w nie swoje sprawy. Programiści
bawią się szkieletami aplikacji. Architekci tworzą kod aplikacji. Każdy roz-
mawia z każdym. To bardzo efektywny chaos. Spraw, aby obiekty były
specjalizowane, a programiści uniwersalni.
Ponieważ dokonałem rozdzielenia projektu strategicznego oraz innych
rodzajów projektów w celu ułatwienia sobie wyjaśniania związanych
z nimi zadań, muszę przyznać, że posiadanie dwóch rodzajów projektu
nie oznacza wcale, że musimy mieć dwa rodzaje osób. Tworzenie projektu
elastycznego z wykorzystaniem modelu dogłębnego jest zaawansowaną
czynnością projektową. Jednak wszystkie szczegóły tej aktywności są tak
ważne, że powinny być wykonane przez kogoś rzeczywiście pracującego
z kodem. Projekty strategiczne wyłaniają się z projektów aplikacyjnych,
chociaż wymagają szerszego obrazu działań, być może rozciągającego się
na wiele zespołów. Ludzie uwielbiają rozdrobnione zadania, sprawiające,
że eksperci projektowi nie muszą znać biznesu, zaś eksperci dziedzinowi
nie muszą rozumieć technologii. Oczywiście, istnieje pewne ograniczenie
tego, czego może nauczyć się człowiek, jednak nadmierna specjalizacja
zmniejsza znaczenie projektu sterowanego dziedziną.

To samo dotyczy szkieletów technicznych


Techniczne szkielety aplikacyjne mogą w znaczny sposób przyspieszyć
rozwój aplikacji, włączając w to warstwę dziedziny. Dostarczają one bowiem
warstwę infrastrukturalną, uwalniającą aplikację od implementacji prostych
usług, a także umożliwiającą odizolowanie dziedziny od innych zagad-
nień. Istnieje jednak ryzyko, że architektura może przeszkadzać w wymownej
implementacji modelu dziedziny oraz uniemożliwiać prostą zmianę. Taka sytuacja

SZEŚĆ KRYTERIÓW PODEJMOWANIA STRATEGICZNYCH DECYZJI 541

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.

Nie twórz szkieletów dla osób nierozgarniętych.


Zespoły podzielone w sposób zakładający, że niektórzy programiści nie
są wystarczająco bystrzy, aby projektować, są skazane na porażkę, ponieważ
nie doceniają trudności tworzenia aplikacji. Jeżeli takie osoby nie są wystar-
czająco bystre, aby projektować, nie powinny być również przypisywane
do zadań programistycznych. Jeżeli natomiast są bystre, to próba ich odizo-
lowania spowoduje jedynie utworzenie barier pomiędzy nimi a potrzeb-
nymi im narzędziami.
Takie nastawienie zatruwa również relacje pomiędzy zespołami. Kie-
dyś skończyło się to dla mnie przypisaniem do takiego aroganckiego zespołu
i przy każdej rozmowie z programistami musiałem ich za to przepraszać.
(Niestety, nigdy nie udało mi się również zmienić takiego zespołu).
Hermetyzacja nieistotnych szczegółów technicznych jest całkowicie odmienna
od wstępnego opakowywania, o którym wypowiadałem się lekceważąco.
Dobry szkielet aplikacji może oddać w ręce programisty potężne abstrakcje
oraz narzędzia i uwolnić go od żmudnej pracy Bardzo trudno jest opisać
różnicę w uogólniony sposób, jednak można się o niej przekonać, pytając
projektantów szkieletu o to, czego oczekują oni od osoby, która będzie
używać narzędzia/szkieletu/komponentów. Jeżeli projektant wydaje się
posiadać wysoki poziom szacunku do użytkownika szkieletu aplikacyjnego,
wtedy jest to prawdopodobnie dobry kierunek.

Wystrzegaj się planu głównego


Grupa architektów (tych, którzy projektują fizyczne budynki) dowodzona
przez Krzysztofa Alexandra opowiadała się za chaotycznym rozwojem
w dziedzinie architektury oraz planowania miasta. W bardzo ładny spo-
sób byli oni w stanie wyjaśnić, dlaczego plan główny nie zadziałał.

542 ROZDZIAŁ 17. ŁĄCZENIE STRATEGII

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.

Plan główny był tradycyjnym sposobem rozwiązania tej trudności.


Podejmował on próby ustanowienia tylu reguł, aby zapewniać ogólną
spójność otoczenia, wciąż jednak pozostawiając poszczególnym budyn-
kom oraz przestrzeniom otwartym możliwość dostosowania się do
lokalnych potrzeb.

(…) i wszystkie części przyszłego uniwersytetu utworzą spójną całość,


ponieważ zostaną zwyczajnie umieszczone w odpowiednich rubry-
kach planu.

(…) w praktyce plan główny zawiódł, ponieważ definiował on porządek


totalitarny zamiast organicznego. Jego twórcy byli zbyt rygorystyczni
i nie mogli się zaadaptować do naturalnych i nieprzewidywalnych
zmian, które niewątpliwie nastąpią w życiu społeczności. W chwili
wystąpienia tych zmian plan główny stawał się zdezaktualizowany i nie
był już przestrzegany. A nawet w obszarze, w którym był on stoso-
wany, wciąż jednak nie precyzował wystarczająco połączeń pomiędzy
budynkami, uwzględnienia ludzkiej skali, zbilansowanej funkcjonal-
ności itp., aby pomóc każdej indywidualnej budowie lub projektowi
dobrze dostosować się do całego otoczenia.

(…) Próba podążania w takim kierunku przypomina raczej koloro-


wanie książeczki dziecka. W najlepszym razie porządek wynikający
z takiego procesu jest banalny.

(…) Z tego powodu plan główny jest jednocześnie zbyt precyzyjny


oraz niewystarczająco precyzyjny, aby stać się źródłem uporządkowania
organicznego. W wymiarze ogólnym jest zbyt precyzyjny, natomiast
w szczegółowym zupełnie nieprecyzyjny.

(…) istnienie planu głównego izoluje użytkowników, (ponieważ z defi-


nicji) członkowie społeczności mogą mieć niewielki wpływ na przy-
szły kształt ich społeczności, gdyż większość ważnych decyzji już
została podjęta.
Fragment książki Alexandra,
The Oregon Experiment, str. 16 – 28, 1975 r.

W zamian Alexander ze swoimi kolegami opowiadali się za użyciem


zestawu pryncypiów stosowanych przez wszystkich członków społeczności
w każdym działaniu tego nieuporządkowanego rozwoju. Powinno to spra-
wić pojawienie się „uporządkowania organicznego”, bardzo dobrze dosto-
sowanego do okoliczności.

SZEŚĆ KRYTERIÓW PODEJMOWANIA STRATEGICZNYCH DECYZJI 543

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.

Świeżo posadzony gaj oliwny

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.

Siedem lat później

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

M oim pierwszym „miłym samochodem”, który dostałem zaraz


po ukończeniu szkoły średniej, był ośmioletni peugeot. Niekiedy nazywany
był on „francuskim mercedesem”. Samochód ten był dobrze wykonany,
niezawodny i przyjemnie się nim jeździło. Niestety, gdy go dostałem,
osiągnął już wiek, w którym pojawiają się problemy z różnymi częściami,
wymagające coraz więcej obsługi serwisowej.
Peugeot jest wiekową firmą, która w czasie dziesiątków lat przeszła
ewolucję. Ma swoją terminologię dotyczącą mechaniki, a jej projekty są
szczególne. Nawet podział funkcji na części jest niekiedy niestandardowy.
W rezultacie powstaje samochód, z którym poradzić sobie mogą jedynie
specjaliści firmy Peugeot, co może być potencjalnym problemem dla kogoś,
kto ma absolwenckie dochody.
W jednym z przypadków zabrałem samochód do lokalnego mechanika,
aby sprawdził wyciek płynów. Po sprawdzeniu podwozia stwierdził on,
że olej „wyciekał z małego pudełka w odległości dwóch trzecich długo-
ści samochodu mierzonej od jego przodu, które wydawało się mieć coś
wspólnego z rozdziałem siły hamowania pomiędzy oś przednią i tylną”.
Następnie mechanik zrezygnował z naprawy i poradził mi udać się do
dealera oddalonego o blisko 100 km. Samochody marki Ford lub Honda
może naprawić każdy, co sprawia, że są one wygodniejsze oraz mniej
kosztowne w użytkowaniu, nawet jeżeli pod względem mechanicznym
są identycznie skomplikowane.
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

554 DODATEK WYKORZYSTANIE SZABLONÓW Z TEJ KSIĄŻKI

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

DODATEK WYKORZYSTANIE SZABLONÓW Z TEJ KSIĄŻKI 555

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.

Nazwy wzorców w zamierzeniu mają być określeniami w języku zespołu


i właśnie w takim kontekście zostały użyte w tej książce. Zawsze, gdy
w dyskusji pojawia się nazwa wzorca, ma ona format DUŻYCH PO-
CHYLONYCH LITER.
Oto, jaki format nadano w tej książce wzorcom. Oczywiście, od tego
prostego planu istnieją odstępstwa, ponieważ zawsze nad sztywną strukturę
przedkładam podejście indywidualne, pozwalające zachować przejrzystość.

556 DODATEK WYKORZYSTANIE SZABLONÓW Z TEJ KSIĄŻKI

699b15244ae879c728b313782703a4d7
6
NAZWA WZORCA
[Ilustracja pomysłu.
Niekiedy wizualna metafora lub sugestywny opis].

[Kontekst. Krótkie wyjaśnienie, w jaki sposób dany pomysł odnosi się do


innych wzorców. W niektórych przypadkach krótki opis wzorca.
Większość dyskusji na temat kontekstu jest jednak w tej książce
umieszczona we wprowadzeniu do rozdziału lub innych podrozdziałach
opisowych, a nie w samym opisie wzorca.
***]
[Dyskusja na temat problemu]
Podsumowanie problemu
Dyskusja na temat rozwiązania problemu przeradza się
w rozwiązanie.
Dlatego:
Podsumowanie rozwiązania.
Konsekwencje. Sprawy do rozważenia w implementacji.
Przykłady.
***
Końcowy kontekst: Krótkie wyjaśnienie, w jaki sposób wzorzec prowadzi
do pozostałych wzorców.
[Dyskusja na temat wyzwań związanych z implementacją. W forma-
towaniu zastosowanym w oryginalnej książce Alexandra dyskusja umiesz-
czona została w akapicie opisującym rozwiązanie problemu, a ja często
naśladowałem Alexandra w tej książce. Niektóre wzorce wymagają jednak
dłuższej dyskusji nad implementacją. Aby zachować zwięzły rozmiar głów-
nej dyskusji na temat wzorca, przesunąłem tego rodzaju długie dyskusje
implementacyjne za opis wzorca.
Długie przykłady, szczególnie te łączące wiele wzorców, są bardzo
często przeniesione poza wzorce].

NAZWA WZORCA 557

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

Alexander C., M. Silverstein, S. Angel, S. Ishikawa oraz D. Abrams, The


Oregon Experiment, Oxford University Press, 1975 r.
Alexander C., S. Ishikawa oraz M. Silverstein, Pattern Language: Towns,
Buildings, Construction, Oxford University Press, 1977 r. [Wydanie
polskie: Język wzorców. Miasta, budynki, konstrukcja, GWP Gdańskie
Wydawnictwo Psychologiczne, 2008 r.]
Alur D., J. Crupi oraz D. Malks, Core J2EE Patterns, Sun Microsystems
Press, 2001 r. [Wydanie polskie: J2EE. Wzorce projektowe. Wydanie 2,
Wydawnictwo Helion, 2004 r.]
Beck K., Smalltalk Best Practice Patterns, Prentice Hall PTR, 1997 r.
Beck K., Extreme Programming Explained: Embrace Change, Addison-Wesley,
1999 r. [Wydanie polskie: Beck K., Andres C., Wydajne programowanie
= Extreme programming, wydanie II, Wydawnictwo Mikom, 2006 r.]
Beck K., Test-Driven Development: By Example, Addison-Wesley, 2003 r.
[Wydanie polskie: TDD. Sztuka tworzenia dobrego kodu, Wydaw-
nictwo Helion, 2014 r.]
Buschmann F., R. Meunier, H. Rohnert, P. Sommerlad oraz M. Stal,
Pattern-Oriented Software Architecture: A System of Patterns, Wiley, 1996 r.
Cockburn A., Surviving Object-Oriented Projects: A Manager’s Guide, Addison-
-Wesley, 1998 r.
Evans E. oraz M. Fowler, Specifications, obrady konferencji PLoP w 1997 r.,
1997 r.
Fayad M. oraz R. Johnson, Domain-Specific Application Frameworks, Wiley,
2000 r.
Fowler M., Analysis Patterns: Reusable Object Models, Addison-Wesley,
1997 r.
Fowler M., Refactoring: Improving the Design of Existing Code, Addison-
-Wesley, 1999 r. [Wydanie polskie: Refaktoryzacja: ulepszanie struktury
istniejącego kodu, Wydawnictwo Helion, 2006 r.]

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

Wszystkie fotografie pojawiajÈce siÚ w tej ksiÈĝce zostaïy zamieszczone


za zgodÈ:
Richard A. Paselk, Uniwersytet Stanowy Humboldta
Astrolabium (rozdziaï 3., strona 73)

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

Biophoto Associates/Photo Researchers, Inc.


ZdjÚcie z mikroskopu elektronowego (rozdziaï 14., strona 377)

Ross J. Venables
’odzie wiosïowe (grupa oraz pojedyncze) (rozdziaï 14., strony 383
oraz 414)

Photodisc Green/Getty Images


Biegacze (rozdziaï 14., strona 398), dziecko (rozdziaï 14., strona 403)

U.S. National Oceanic and Atmospheric Administration


Wielki Mur Chiñski (rozdziaï 14., strona 406)

© 2003 NAMES Project Foundation, Atlanta, Georgia


Fotograf Paul Margolies, www.aidsquilt.org
Narzuta dla chorych na AIDS (rozdziaï 16., strona 485)

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

You might also like