Vous êtes sur la page 1sur 55

IDZ DO

PRZYKADOWY ROZDZIA
SPIS TRECI

KATALOG KSIEK
KATALOG ONLINE
ZAMW DRUKOWANY KATALOG

Wzorce projektowe.
Analiza kodu sposobem
na ich poznanie
Autor: Allen Holub
ISBN: 83-7361-948-8
Tytu oryginau: Holub on Patterns:
Learning Design Patterns by Looking at Code
Format: B5, stron: 464

TWJ KOSZYK
DODAJ DO KOSZYKA

CENNIK I INFORMACJE
ZAMW INFORMACJE
O NOWOCIACH
ZAMW CENNIK

CZYTELNIA
FRAGMENTY KSIEK ONLINE

Opanuj zasady stosowania wzorcw projektowych na praktycznych przykadach


Dowiedz si, czym s wzorce projektowe
Zaimplementuj wzorce we wasnych programach
Poznaj rodzaje wzorcw projektowych
Wzorce projektowe to zapisane w sposb formalny sposoby rozwizywania
najczstszych problemw, z jakimi borykaj si twrcy oprogramowania stosujcy
jzyki obiektowe. Najczciej stosowane wzorce zostay skatalogowane i przedstawione
w postaci diagramw UML, jednak do poprawnego ich wykorzystywania niezbdna jest
wiedza praktyczna. Przystpujc do implementacji wzorca projektowego, naley pozna
zakres jego zastosowania. Tak wiedz najlepiej zdobywa si, analizujc przykady
kodw rdowych.
Dziki ksice Wzorce projektowe. Analiza kodu sposobem na ich poznanie poznasz
wzorce w taki wanie sposb badajc programy, w ktrych je zastosowano. Kady
z omawianych w ksice wzorcw zaprezentowany jest w oparciu o dwie implementacje
szczegowo wyjaniajce zasad jego dziaania. Dziki takim opisom wzorcw
opanujesz t technologi znacznie szybciej ni w przypadku nauki teoretycznych
podstaw oraz prb ich samodzielnego wdraania we wasnych aplikacjach. Unikniesz
typowych bdw i dowiesz si, jak prawidowo wykorzystywa kady z wzorcw.
Zastosowanie wzorcw projektowych
Klasyfikacja wzorcw
Podstawowe pojcia z dziedziny obiektowoci
Interfejsy i wzorce konstrukcyjne
Implementacja wzorcw obserwatora i fasady
Wykorzystanie wzorcw projektowych w aplikacjach bazodanowych

Wydawnictwo Helion
ul. Chopina 6
44-100 Gliwice
tel. (32)230-98-63
e-mail: helion@helion.pl

Ksika zawiera rwnie zestawienie najczciej wykorzystywanych wzorcw


projektowych wraz z opisem ich zastosowa.

Spis treci
O Autorze ......................................................................................... 9
Przedmowa ..................................................................................... 11
Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe .................... 15
Wzorce kontra cechy jzykw programowania .............................................................. 16
Czym waciwie jest wzorzec projektowy? .................................................................... 17
Czemu te wzorce maj w ogle suy? ......................................................................... 21
Rola wzorcw w procesie projektowania ....................................................................... 22
Napicia pomidzy wzorcami a prostot .................................................................. 23
Klasyfikacja wzorcw .................................................................................................... 25
O programowaniu, oglnie ....................................................................................... 26
Programowanie jzyka FORTRAN w Javie ............................................................. 28
Programowanie z otwartymi oczami ........................................................................ 31
Czym jest obiekt? ........................................................................................................... 32
Nonsens! .................................................................................................................. 32
Obiekt jest zbiorem zdolnoci .................................................................................. 33
Jak nie naley tego robi? ........................................................................................ 35
Jak zatem naley to robi prawidowo? ................................................................ 38
Automat komrkowy ...................................................................................................... 42
Metody zwracajce i ustawiajce s ze ............................................................................. 48
Sam wizualizuj swoje dane ...................................................................................... 52
JavaBeans i Struts .................................................................................................... 54
Dostrajanie ............................................................................................................... 55
ycie bez metod get i set .......................................................................................... 56
Kiedy stosowanie akcesorw i mutatorw jest uzasadnione? .................................. 59
Podsumowanie problematyki metod zwracajcych i ustawiajcych ........................ 62

Rozdzia 2. Programowanie z interfejsami i kilka wzorcw konstrukcyjnych ........ 67


Dlaczego sowo kluczowe extends jest ze ..................................................................... 67
Interfejsy kontra klasy .................................................................................................... 69
Utrata elastycznoci ................................................................................................. 69
Sprzganie ................................................................................................................ 71
Problem uomnej klasy bazowej ............................................................................... 73
Dziedziczenie wielokrotne ....................................................................................... 80
Szkielety ................................................................................................................... 81
Wzorce metody szablonowej i metody wytwrczej ................................................. 82
Podsumowanie problemu uomnych klas bazowych ................................................ 88

Wzorce projektowe. Analiza kodu sposobem na ich poznanie


Kiedy stosowanie sowa extends jest uzasadnione ......................................................... 90
Eliminowanie relacji extends ......................................................................................... 93
Wzorce wytwrni i singletonw ............................................................................... 94
Singleton .................................................................................................................. 96
Problem z przetwarzaniem wielowtkowym w przypadku singletonw .................. 98
Blokowanie DCL (nigdy tego nie rb) ................................................................... 100
Zabijanie singletonu ............................................................................................... 101
Wytwrnia abstrakcji ............................................................................................. 103
Pomieszanie wzorcw ............................................................................................ 107
Dynamiczne tworzenie obiektw w ramach wytwrni ........................................... 109
Polecenie i strategia ............................................................................................... 112
Podsumowanie ............................................................................................................. 116

Rozdzia 3. Gra w ycie .................................................................................. 119


Zdobywanie ycia ........................................................................................................ 120
Sporzdzanie mapy struktury ycia .............................................................................. 122
Podsystem zegara: obserwator ..................................................................................... 125
Implementacja wzorca obserwatora: klasa Publisher ............................................. 132
Podsystem zegara: wzorzec wizytatora ........................................................................ 143
Podsystem menu: kompozyt ......................................................................................... 148
Podsystem menu: fasada i most .................................................................................... 156
Klasa MenuSite ............................................................................................................ 158
Klasy rdzenne ............................................................................................................... 177
Klasa Universe ....................................................................................................... 178
Interfejs Cell ........................................................................................................... 182
Klasa Resident ....................................................................................................... 185
Klasa Neighborhood .............................................................................................. 188
Mediator ....................................................................................................................... 197
Jeszcze jedno spojrzenie na wzorzec kompozytu ......................................................... 199
Prototyp .................................................................................................................. 201
Jeszcze raz o wzorcu projektowym kompozytu ............................................................ 205
Waga pirkowa ............................................................................................................ 210
Pula wagi pirkowej ............................................................................................... 215
Memento ...................................................................................................................... 217
Dokoczenie ................................................................................................................. 220
Podsumowanie ............................................................................................................. 225

Rozdzia 4. Implementacja osadzonego interpretera jzyka SQL ....................... 227


Wymagania .................................................................................................................. 228
Architektura .................................................................................................................. 229
Warstwa skadowania danych ...................................................................................... 231
Interfejs Table ........................................................................................................ 231
Wzorzec mostu ....................................................................................................... 237
Tworzenie tabeli: wzorzec projektowy wytwrni abstrakcji .................................. 240
Tworzenie i zapisywanie tabel: pasywne iteratory
i wzorzec projektowy budowniczego ................................................................... 243
Wypenianie tabeli danymi ..................................................................................... 255
Przeszukiwanie tabeli: wzorzec projektowy iteratora ............................................ 258
Implementacja transakcji (funkcji cofania)
przy uyciu wzorca projektowego polecenia ....................................................... 267
Modyfikowanie tabeli: wzorzec projektowy strategii ............................................ 273
Selekcja i zczenia ................................................................................................ 276
Rozmaitoci ............................................................................................................ 282
Odmiany interfejsu Table: wzorzec dekoratora ...................................................... 290

Spis treci

7
Rozbudowa caego systemu o obsug jzyka SQL ..................................................... 300
Struktura moduu jzyka SQL ................................................................................ 301
Podzia danych wejciowych, ponowne omwienie wzorca wagi pirkowej
i analiza wzorca acucha odpowiedzialnoci ...................................................... 301
Skaner: wzorzec acucha odpowiedzialnoci ....................................................... 311
Klasa ParseFailure .................................................................................................. 319
Klasa Database ............................................................................................................. 321
Stosowanie klasy Database .................................................................................... 322
Wzorzec porednika ............................................................................................... 325
Zbir tokenw i pozostae stae .............................................................................. 330
Wzorzec interpretatora ................................................................................................. 337
Obsugiwana cz jzyka SQL ............................................................................. 337
Analiza funkcjonowania interpretera jzyka SQL .................................................. 359
Warstwa JDBC ............................................................................................................. 366
Wzorzec stanu i klasa JDBCConnection ...................................................................... 373
Wyraenia .............................................................................................................. 379
Wzorzec adaptera (zbiory wynikowe) .................................................................... 380
Wykaczanie kodu ................................................................................................. 385
Kiedy mosty nie zdaj egzaminu ............................................................................ 386
Uff! ............................................................................................................................... 387

Dodatek A Krtki podrcznik o wzorcach projektowych ................................... 389


Wzorce konstrukcyjne .................................................................................................. 391
Wytwrnia abstrakcji ............................................................................................. 392
Budowniczy ........................................................................................................... 394
Metoda wytwrcza ................................................................................................. 396
Prototyp .................................................................................................................. 398
Singleton ................................................................................................................ 400
Wzorce strukturalne ..................................................................................................... 403
Adapter ................................................................................................................... 404
Most ....................................................................................................................... 406
Kompozyt ............................................................................................................... 408
Dekorator ............................................................................................................... 410
Fasada .................................................................................................................... 412
Waga pirkowa ...................................................................................................... 414
Porednik ............................................................................................................... 416
Wzorce czynnociowe .................................................................................................. 419
acuch odpowiedzialnoci ................................................................................... 420
Polecenie ................................................................................................................ 422
Interpretator ............................................................................................................ 424
Iterator .................................................................................................................... 426
Mediator ................................................................................................................. 428
Memento ................................................................................................................ 430
Obserwator (publikowanie-abonament) ................................................................. 432
Stan ........................................................................................................................ 434
Strategia ................................................................................................................. 436
Metoda szablonowa ................................................................................................ 438
Wizytator ............................................................................................................... 440

Skorowidz ..................................................................................... 443

Rozdzia 1.

Wstp: programowanie
obiektowe
i wzorce projektowe
W zwykych okolicznociach tego typu ksika powinna si rozpoczyna cytatem z Christophera Alexandra, architekta (budynkw, nie oprogramowania), ktry jako pierwszy
wprowadzi termin wzorca projektowego. Odkryem, e chocia Alexander jest wspaniaym czowiekiem i pisze naprawd wietne ksiki, jego dziea mog by zrozumiae
nie dla wszystkich, zatem pozwol sobie w tym miejscu pomin obowizkowy cytat.
Warto jednak pamita, e tezy prezentowane przez Alexandra stanowi podstawowe
rdo wspczesnych koncepcji wzorcw projektowych.
Podobnie, prawdziwym przeomem w kwestii zastosowa wzorcw projektowych
w wiecie oprogramowania bya ksika Design Patterns: Elements of Reusable Object-Oriented Software autorstwa Gammy, Helma, Johnsona i Vlissidesa (Addison-Wesley,
1995). (Czterej autorzy tej ksiki s przez wikszo projektantw dowcipnie nazywani
Band Czworga.) Moja ksika nie mogaby powsta, gdyby nie powstaa ksika Bandy
Czworga, zatem ja sam (podobnie jak chyba wszyscy programici stosujcy techniki
obiektowe) jestem winien autorom tej ksiki ogromne podzikowania. Tak czy inaczej, wspomniana przeze mnie ksika jest akademick prezentacj wzorcw, ktra dla
wikszoci pocztkujcych programistw z natury rzeczy bdzie po prostu niezrozumiaa. Postanowiem podj ryzyko zatracenia tej akademickiej precyzji i stworzy co
bardziej przystpnego.

16

Wzorce projektowe. Analiza kodu sposobem na ich poznanie

Wzorce kontra cechy


jzykw programowania
Nasze rozwaania powinnimy rozpocz od zgbienia samego pojcia wzorca przez
analiz prostych wasnoci jzykw programowania. Wiele wzorcw projektowych jest
wykorzystywanych tak powszechnie, jest tak gboko zakorzenionych w umysach tylu
programistw, e nie s ju traktowane jak wzorce, tylko jak cechy poszczeglnych jzykw programowania. Wzorce te nie s uwaane za co specjalnego stanowi po
prostu typowe sposoby rozwizywania okrelonych problemw. Niektrzy odrniaj
wzorce od cech jzykowych wycznie na podstawie ich zastosowania (przykadowo,
wzorzec, w przeciwiestwie do cech jzykowych, jest reprezentowany w sposb formalny). Dla mnie taki podzia nie jest jednak wiarygodny. Cecha (wasno) jzykowa
to po prostu wzorzec, ktrego stosowanie nabrao powszechnego wymiaru.
Doskonaym przykadem ewolucji wzorcw do postaci cech jzykowych jest model wyprowadzania struktur danych. We wczesnych latach osiemdziesitych ubiegego wieku,
kiedy krlowa jzyk programowania C, wyprowadzanie struktur danych byo typowym
wzorcem projektowym. W jzyku C istniao wwczas wiele przykadw relacji rozszerzania jednych struktur w inne, bardziej szczegowe. Przykadowo, standardowa implementacja funkcji malloc() wykorzystuje nagwek (klas bazow), ktry jest rozszerzany do postaci innej struktury (klasy potomnej), a ta z kolei dziedziczy metod free()
z klasy bazowej.
Take funkcje abstrakcyjne naleay do wzorca wyprowadzania. W kodzie jzyka programowania C czsto mona si byo natkn na przekazywanie tablic wskanikw do
funkcji, ktre byy inicjalizowane w rny sposb dla rnych klas. W jzyku C++
w taki sam sposb zaimplementowano zarwno metody abstrakcyjne, jak i mechanizm
dziedziczenia interfejsw (ktre w wiecie jzyka C nie zostay nawet nazwane).
Wyprowadzanie struktur jako takie nie byo wbudowane w jzyk programowania C
i wikszo programistw tego jzyka nie stosowaa obiektw, zatem wyprowadzania
struktur z pewnoci nie mona uzna za cech jzyka C by to wic wzorzec. Byo
to co, co mona byo bez trudu znale w wielu programach, ktre z rozmaitych wzgldw musiay rozwizywa podobne problemy, nie byo to jednak dziaanie naturalne
z perspektywy przecitnego programisty jzyka C.
Mechanizmy wyprowadzania (dziedziczenia) i interfejsw s obecnie wbudowanymi
elementami jzykw programowania, mona wic przyj, e stay si cechami tych
jzykw.

Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe

17

Czym waciwie jest


wzorzec projektowy?
Po pierwsze, wzorce projektowe s, co bardzo wane, odkrywane, nie s wic wynalazkami. Kiedy Christopher Alexander analizowa konstrukcje wielu udanych budynkw,
za kadym razem skupia si tylko na jednym ich aspekcie (np. stara si oceni, co
decyduje o przytulnoci pomieszcze), co w duszej perspektywie prowadzio do ciekawych odkry wanie w kwestii odpowiednich wzorcw. Udane, przytulne pomieszczenia rozwizuj okrelone klasy problemw (np. zwizane z owietleniem) w bardzo
podobny sposb. Podobnie, kiedy analizujesz kod wielu programw napisanych przez
rnych programistw, i kiedy skupiasz si na konkretnym problemie implementacyjnym rozwizywanym przez te programy z koniecznoci (np. problem izolacji podsystemw), rwnie moesz dostrzec pewne wzorce. Odkrywasz wwczas, e wielu
programistw niezalenie od siebie stosuje podobne techniki rozwizywania podobnych
problemw. Kiedy ju zdasz sobie spraw z istnienia tych technik, bdziesz widzia
odpowiednie wzorce gdziekolwiek spojrzysz. Za wzorzec mona jednak uzna tylko takie
rozwizanie, ktre zostao zastosowane w wielu programach opracowywanych zupenie
niezalenie od siebie. Twierdzenie Wynalelimy wzorzec projektowy, ktry jest
wic niezawodnie oznak autorw, ktrzy nie rozumiej istoty wzorcw. Istnieje oczywicie moliwo opracowania genialnego projektu, ale nie bdzie to wzorzec projektowy dopty, dopki nie zostanie zastosowany przez wielu programistw pracujcych
cakowicie niezalenie nie tylko od siebie, ale te od autorw projektu. (Nie mona oczywicie wykluczy sytuacji, w ktrej wynaleziony wzorzec staje si wzorcem rzeczywistym z racji jego powszechnego stosowania).
Wzorzec projektowy jest wic ogln technik wykorzystywan do rozwizywania
pewnej klasy powizanych ze sob problemw. Nigdy nie jest to konkretne rozwizanie jakiego problemu. Prawdopodobnie kady architekt, ktremu udao si stworzy
przytulne pomieszczenie zaprojektowa jego owietlenie w nieco inny sposb to
samo dotyczy programistw, z ktrych kady troch inaczej implementuje swoje
rozwizania. Wzorzec jest ogln struktur rozwizania (jeli chcesz, moesz to nazywa metarozwizaniem), ale z pewnoci nie jest rozwizaniem jako takim.
Dobr analogi jest wiat muzyki. Pojcie muzyki klasycznej mona traktowa jak
pewien wzorzec kompozytorski. Identyfikacja muzyki pasujcej do wzorca muzyki klasycznej jest moliwa, poniewa analizowane utwory po prostu brzmi jak muzyka
klasyczna. Nie oznacza to jednak, e poszczeglne dziea tej muzyki s identyczne.
Skoro natura wzorcw jest tak oglna, kopiowanie wzorcw projektowych z kodu jednego
programu i wklejanie ich do kodu innych programw jest niemoliwe (cho w niektrych przypadkach moesz ponownie wykorzysta okrelone rozwizanie pod warunkiem,
e biecy kontekst jest podobny do oryginalnego kontekstu uycia tego rozwizania). Ta
fundamentalna zasada jest rdem wielu nieporozumie wrd osb, ktre funkcjonuj
w wiecie wzorcw projektowych od niedawna. Komentarze, z ktrymi miaem okazj
si zapozna, przegldajc strony internetowe wskazuj, e wielu programistw uwaa,
e jeli jaka ksika nie zawiera tych samych przykadw co ksika Bandy Czworga,
dowodzi to braku kompetencji autora tej ksiki. Moim zdaniem, taka postawa dowodzi

18

Wzorce projektowe. Analiza kodu sposobem na ich poznanie

raczej braku zrozumienia koncepcji wzorcw projektowych po stronie autorw tego rodzaju opinii: myl fragment kodu demonstrujcy jaki wzorzec z samym wzorcem. Majc
to na uwadze, sprbuj zaprezentowa rne przykady dla kadego z omawianych wzorcw, aby mg si przekona, jak odmienne mog by konkretne implementacje oparte
na tym samym wzorcu nie bd te wykorzystywa przykadw Bandy Czworga,
chyba e bd dotyczyy rzeczywistych problemw zwizanych z programowaniem (ten
warunek spenia tylko niewielka ich cz).
Analiz wzorcw projektowych dodatkowo komplikuje fakt, e rzeczywiste obiekty i klasy
nalece do poszczeglnych wzorcw niemal zawsze nale te do innych wzorcw.
Kiedy przyjrzysz si interesujcemu Ci rozwizaniu pod okrelonym ktem, dostrzeesz jeden wzorzec; jednak kiedy spojrzysz na to samo rozwizanie pod innym ktem,
zobaczysz zupenie inny wzorzec. Badania wzorcw bywaj jeszcze trudniejsze w sytuacjach, gdy wiele implementacji wzorcw opiera si na identycznych strukturach statycznych. Kiedy analizujesz przedstawione w ksice Bandy Czworga diagramy struktury
statycznej (zapisane w jzyku UML), wszystkie wygldaj bardzo podobnie wida
tam interfejs, klas klienta i klas implementacji. Rnic pomidzy wzorcami naley szuka w dynamicznym zachowaniu systemu i w celach realizowanych przez programist,
a nigdy w klasach czy w sposobie ich czenia.
Sprbuj zilustrowa wymienione problemy, posugujc si przykadem ze wiata
tradycyjnej architektury budynkw skupi si na dwch dziedzinach: wentylacji
i owietleniu.
Jeli chodzi o wentylacj, nie chc, by pokj robi wraenie dusznego. Jeli przeanalizujemy rozwizanie tego problemu w wielu naprawd komfortowych pomieszczeniach,
dostrzeemy w nich jeden wzorzec, ktry bd nazywa wentylacj przecinajc. Pomieszczenia nalece do tego wzorca maj rdo i ujcie powietrza ustawione naprzeciwko siebie na dwch przeciwlegych cianach, w dodatku na wysokoci okien. Powietrze dostaje si do pomieszczenia poprzez rdo, po czym przechodzi przez cae to
pomieszczenie i opuszcza je przez ujcie. Kiedy ju dysponowaem zidentyfikowanym
(i nazwanym) wzorcem, stworzyem krtki opis nazywany przez Band Czworga intencj ktry podsumowuje zarwno oglny problem, jak i rozwizanie wynikajce
z tego wzorca. W przypadku wentylacji przecinajcej moim celem byo wyeliminowanie poczucia dusznoci i zapewnienie wyszego komfortu przez umoliwienie bezporedniego przepywu powietrza w poziomie, na wysokoci okien. Mechanizmem architektonicznym, ktry ten cel realizuje jest logiczna reifikacja (wyjani to sowo za chwil)
wzorca. (Banda Czworga uywa w tym kontekcie sowa intencja, co jest dosy dziwne.
Sam nie bd si posugiwa tym sowem zbyt czsto, poniewa w moim odczuciu
znacznie lepszym sowem jest cel).
Reifikacja to brzydkie, ale do przydatne sowo w tym kontekcie. Sowo to nie jest
zbyt czsto stosowane w literaturze. Reifikacja znaczy dosownie materializacj, nadanie
czemu okrelonej treci i formy. Reifikacja koncepcji jest wic jej konkretn realizacj, a dla pojedynczej koncepcji mog istnie miliony moliwych reifikacji. Uywam
tego sowa (zamiast ktrego z bardziej popularnych wyrae), aby podkreli czym nie
jest wzorzec. Przykadowo, wzorzec nie jest egzemplarzem czy instancj. Kady
egzemplarz klasy jest identyczny (przynajmniej w sensie struktury) z wszystkimi
pozostaymi egzemplarzami tej samej klasy. Z pewnoci nie jest to cecha wzorca

Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe

19

projektowego. Podobnie, reifikacja nie jest implementacj wzorca reifikacja wzorca


ma posta projektu, nigdy kodu, za dla danego projektu istnieje wiele moliwych (prawidowych) implementacji.
Jakie wic s reifikacje wzorca wentylacji przecinajcej? W pomieszczeniu mog istnie
okna usytuowane naprzeciwko siebie, okno na wprost drzwi, dwoje drzwi naprzeciw
siebie, okno na wprost wentylatora ujemnego (wycigajcego powietrze), dwa wentylatory (wejciowy i wyjciowy) zainstalowane na przeciwlegych cianach lub wielki
miech (obsugiwany przez skaczcego na nim orangutana) zainstalowany przy jednej
ze cian i skierowany w stron przeciwlegej ciany z wybitym otworem. Tak naprawd
w ogle nie s nam potrzebne ciany pomieszczenie bez dwch przeciwlegych cian
doskonale pasuje do tego wzorca. Istnieje nieskoczenie wiele reifikacji tego wzorca.
Poniewa reguy konstruowania reifikacji wzorca i tak s bardzo elastyczne, nie moesz
wybiera tylko tych atrybutw, ktre uwaasz za przydatne. Przykadowo, samo posiadanie wlotw i uj powietrza nie jest warunkiem wystarczajcym, jeli nie s spenione
wymagania odnonie do ich wysokoci i usytuowania naprzeciwko siebie. Umieszczenie wlotu i wylotu powietrza np. na suficie nie bdzie stanowio prawidowej reifikacji
tego wzorca (co moe potwierdzi kady, kto pracuje w budynku z wielkimi przestrzeniami biurowymi z podwieszanymi wentylatorami).
Podsumowujc, celem wentylacji poprzecznej jest wyeliminowanie uczucia dusznoci
i zapewnienie wikszego komfortu przez umoliwienie poziomego przepywu powietrza
w poprzek pokoju, na wysokoci okien. Uczestnicy tego wzorca (mog to by okna,
drzwi czy nawet orangutany) maj przypisane takie role jak rdo i ujcie powietrza.
Przejdmy teraz do problemu owietlenia. Po przeanalizowaniu wielu pomieszcze stwierdziem, e najbardziej urokliwe s pokoje z oknami na dwch przylegajcych cianach.
Dlatego te tak duym powodzeniem ciesz si narone gabinety wielokierunkowe
naturalne rda wiata czyni pomieszczenie znacznie przyjemniejszym. Zdecydowaem, e nazw ten wzorzec projektowy biurem naronym; cel wzorca zdefiniowaem
w nastpujcy sposb: zapewnienie wikszego komfortu przez zastosowanie dwch
rde naturalnego wiata na dwch przylegajcych cianach. Take w tym przypadku reifikacji wzorca jest nieskoczenie wiele: okna na dwch cianach, okno na jednej
cianie i francuskie drzwi na drugiej, francuskie drzwi na dwch cianach itp. Mgby
teraz powiedzie, e take rozwizanie z oknem na jednej cianie i lustrem na cianie
przylegajcej jest reifikacj tego wzorca, poniewa lustro odbijajce naturalne wiato
mona interpretowa jak jego rdo. C, gdybym by Billem Gatesem, mgbym zaliczy do tego wzorca take pomieszczenia z jednym oknem i zawieszonym na cianie
obok telewizorem plazmowym pokazujcym obraz za t cian, ale z pewnoci nie
byaby to prawidowa reifikacja tego wzorca, poniewa telewizor plazmowy nie jest
i nigdy nie bdzie naturalnym rdem wiata. Istnieje oczywicie mnstwo sposobw implementacji wzorcw samych okien i francuskich drzwi.
Przeanalizujmy teraz konkretny projekt plan budynku. Na rysunku 1.1 przedstawiono
reifikacj wzorcw projektowych wentylacji poprzecznej i biura naronego (w ramach
pojedynczego projektu). Wida tam zarwno diagram architektoniczny, jak i rwnowany
diagram UML. Wzorce s identyfikowane za pomoc symbolu wsppracy jzyka UML
w wersji 1.5. Nazwa wzorca jest zapisywana wewntrz elipsy rysowanej przerywan

20

Wzorce projektowe. Analiza kodu sposobem na ich poznanie

Rysunek 1.1.
Poczona
reifikacja wentylacji
poprzecznej
i biura naronego

lini i rozszerzajcej klas majc udzia w danym wzorcu. Linie czce wzorce z klasami s oznaczane nazwami rl odgrywanych przez te klasy w odpowiednich wzorcach.
Okno od strony poudniowo-zachodniej peni funkcj wlotu powietrza dla wentylacji poprzecznej, natomiast drzwi naprzeciwko tego okna s ujciem powietrza. Dwa pozostae
okna nie odgrywaj adnej roli we wzorcu wentylacji poprzecznej, poniewa s rozmieszczone na cianach przylegajcych do ciany poudniowo-zachodniej. Skupmy si teraz na
czym innym okna poudniowo-zachodnie i poudniowo-wschodnie nale do wzorca
biura naronego, poniewa peni funkcj dwch rde naturalnego wiata. Ani drzwi,
ani okno pnocno-zachodnie nie nale do tego wzorca projektowego, poniewa nie
stanowi wystarczajcych rde wiata. Okno od strony poudniowo-zachodniej jest
o tyle interesujce, e naley do dwch wzorcw projektowych jednoczenie peni
funkcj rda powietrza we wzorcu wentylacji poprzecznej oraz rda wiata
we wzorcu biura naronego. Obiekty i klasy wystpujce w rozmaitych wzorcach czsto
wzajemnie si mieszaj w podobny sposb.
Kluczowe znaczenie ma zrozumienie, e identyfikacja wzorcw nie jest moliwa wycznie na podstawie analizowanej struktury. Przykadowo, wentylacja moe by blokowana np. przez meble wwczas adne z okien nie bdzie stanowio rda powietrza.
Podobnie, jedno z okien moe si znajdowa metr od ciany ssiedniego budynku lub
czy gabinet z korytarzem wwczas nie jest wystarczajcym rdem naturalnego
wiata (cho niewykluczone, e sprawdza si w roli rda lub ujcia powietrza). Kiedy
bdziesz analizowa rzeczywiste wzorce, przekonasz si, e do identyfikacji wzorca projektowego w programie komputerowym niezbdna jest wiedza zwizana z kontekstem
(wcznie ze znajomoci zamierze architekta). Nie mona identyfikowa wzorcw
projektowych wycznie na podstawie diagramw UML. Musisz zna docelowe (a wic
zgodne z zamierzeniami architekta) zastosowanie poszczeglnych obiektw lub klas.
W kolejnych rozdziaach bdziesz mia okazj zapozna si z wieloma przykadami
tego niezwykego zjawiska.

Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe

21

Wracajc do problemu kopiowania i wklejania mam nadziej, e rozumiesz ju reguy


rzdzce reifikacj wzorcw i jeste sobie w stanie wyobrazi ich stosowanie w rozmaitych projektach, z ktrych kady mona by zaimplementowa na mnstwo sposobw. Twierdzenie, e mona skopiowa i wklei wzorzec za pomoc odpowiedniego
narzdzia projektowego jest pozbawione sensu. Niezalenie od tego wielu producentw obiektowych narzdzi CASE zachwala swoje programy jako zawierajce bogate
biblioteki wzorcw, z ktrych moesz wybiera gotowe wzorce i wstawia je do swoich projektw. W praktyce biblioteki te zawieraj jedynie gotowe struktury jzyka UML
dla pojedynczych reifikacji wzorca zaprezentowanego w ksice Bandy Czworga. Co
prawda wklejanie niektrych spord tych struktur czasami moe by pewnym uatwieniem, jednak nie naley tej prostej operacji myli z duo bardziej wymagajcym procesem stosowania wzorca w projekcie. Dobry projekt niemal zawsze musi wykorzystywa wasn reifikacj, ktra jest waciwa tylko w okrelonym kontekcie. Bezmylne
kopiowanie i wklejanie ma tyle wsplnego z projektowaniem, co zabawa w malowanie
liczbami z rzeczywistym malowaniem.

Czemu te wzorce maj w ogle suy?


Skoro wzorce nie maj adnego konkretnego ksztatu, czemu tak naprawd su?
Kiedy po raz pierwszy przeczytaem ksik Bandy Czworga, byem rozczarowany. Pocztkowo miaem wraenie, e nie jest to nic innego jak pedagogiczna prezentacja materiau, ktry jest ju doskonale znany wszystkim kompetentnym projektantom oprogramowania (bardzo czsto prbujcym bezskutecznie poszukiwa eleganckich rozwiza
dla problemw, ktrych dotycz wzorce). Przyznaj, e gdybym przeczyta t ksik
kilka lat wczeniej, mj umys by moe nie byby tak obciony pewnymi wtpliwociami i niechci do nowinek zamieszanie wok wzorcw projektowych pocztkowo traktowaem jak szum wok niczego.
Mylaem w ten sposb do momentu, w ktrym musiaem omwi pewien projekt z innym projektantem. Mj rozmwca wskaza fragment projektu i powiedzia: Te interfejsy
tworz most czcy te dwa podsystemy; sam most jest implementowany za pomoc
tego zbioru adapterw (zarzdcw) obiektw. Byem zaenowany tym, co si stao. Tym
jednym zdaniem mj kolega prawdopodobnie oszczdzi dobre p godziny starannych
wyjanie. Pomylaem wtedy, e by moe rzeczywicie w tych wzorcach projektowych kryje si jaka korzy.
Niedugo potem udaem si na prezentacj pierwszej wersji jzyka programowania
Java, podczas ktrej wszystkie skadniki podsystemu AWT byy opisywane wanie
na bazie odpowiednich wzorcw. Prezentacja bya krtka i jednoczenie zupenie klarowna w praktyce bya duo krtsza i prostsza od odpowiedniej prelekcji wygaszanej przez osob, ktra nie posuguje si wzorcami.
Zanim przystpiem do pracy nad nastpnym projektem raz jeszcze przeczytaem ksik
Bandy Czworga, po czym specjalnie prbowaem rozwaa zadania mojego projektu
pod ktem moliwoci stosowania ewentualnych wzorcw. Zaczem sobie zadawa

22

Wzorce projektowe. Analiza kodu sposobem na ich poznanie

pytanie: Co tak naprawd prbuj osign i czy istniej jakie wzorce, ktre mogyby
mi to zadanie uatwi? (do znajdowania waciwych wzorcw wykorzystywaem te
fragmenty dostpnych opisw, ktre byy powicone ich przeznaczeniu). Kiedy odpowied brzmiaa tak, bez zastanowienia uywaem wybranego wzorca. Szybko odkryem, e takie podejcie z jednej strony znacznie skraca czas projektowania, z drugiej
strony przyczynia si do poprawy jakoci projektu. Im lepiej znaem potrzebne wzorce,
tym szybciej mogem pracowa nad kolejnymi projektami. Co wicej, moje pocztkowe
wersje projektw wymagay znacznie mniejszej liczby poprawek ni projekty przygotowywane w sposb tradycyjny.
Zapaem bakcyla.
Wzorce stanowi organizacyjny szkielet, ktry zasadniczo poprawia moliwoci komunikacyjne, bdce przecie w duszej perspektywie prawdziwym celem projektu. Dyskusje, ktre poprzednio wymagay godzin, teraz wanie dziki wzorcom zajmuj
tylko kilka minut, co powoduje, e wszystkie osoby zaangaowane w prace nad projektem mog realizowa swoje zadania szybciej. Swego czasu uwanie czytaem wszystkie
materiay, do ktrych mogem dotrze, i odkryem, e ksika Bandy Czworga dotyczya jedynie warstwy wierzchniej tego gbokiego tematu. W internecie i w literaturze
branowej ukazyway si setki udokumentowanych wzorcw projektowych, z ktrych
wiele miao zastosowanie w wykonywanej przeze mnie pracy. Z czasem doszedem do
przekonania, e wanie solidna wiedza o wzorcach przydatnych w mojej pracy moe
by zasadniczym elementem decydujcym o szybkoci i jakoci wykonywania zada.
(Przez solidn wiedz o wzorcach rozumiem ich znajomo na pami, a wic tak,
ktra nie bdzie wymagaa wertowania ksiek).

Rola wzorcw
w procesie projektowania
Kiedy wzorce zaczy by wykorzystywane w procesach projektowania i jaka jest ich
rola w tych procesach? Odpowied na to pytanie rni si w zalenoci od uytej metodyki (mam nadziej, e uywasz jakiej metodyki), jednak zainteresowanie wzorcami
projektowymi wynika przede wszystkim z codziennych problemw rozwizywanych
na poziomie implementacji, zatem rde popularnoci wzorcw w procesach projektowych naley upatrywa w zblieniu warstw projektowania i implementacji. Waniejsze
jest inne pytanie: Kiedy koczy si analiza (ktra jako taka wie si z dziedzin problemu) i rozpoczyna projektowanie (ktre z natury rzeczy dotyczy implementacji)?
Najlepsz znan mi analogi jest projektowanie i konstruowanie budynkw. Plany
budynkw nie obejmuj wszystkich szczegw konstrukcyjnych. Najczciej pokazuj rozmieszczenie murw, ale nie mwi o sposobie ich wznoszenia. Okrelaj miejsca instalacji wodno-kanalizacyjnej, ale nie definiuj rozmieszczenia wszystkich rur.
Dopiero w czasie konstruowania budynku projektowane s konkretne rozwizania
w zakresie wznoszenia murw i prowadzenia sieci wodno-kanalizacyjnej, jednak proponowane tam rozwizania rzadko s w peni realizowane, poniewa proces implementacji

Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe

23

stawia przed budowniczymi konkretne problemy do rozwizania (czsto nieprzewidywalne w fazie przygotowywania projektu). Przykadowo, ciela moe zastosowa okrelony wzorzec rozmieszczenia rub i gwodzi, ktry zagwarantuje odpowiednio mocn
konstrukcj ciany. Projekt przewanie mwi tylko o miejscu, w ktrym powinna stan ciana, nie wspomina jednak o sposobie jej konstruowania.
Z analogiczn sytuacj mamy do czynienia w wiecie oprogramowania: w przypadku
wikszoci przedsiwzi programistycznych proces projektowania powinien si zakoczy w punkcie, w ktrym dobry programista moe bez trudu ten projekt zaimplementowa. Nie jestem w stanie wyobrazi sobie opisu techniki tworzenia okien w podsystemie graficznego interfejsu uytkownika Swing zawartego w projekcie. Jest to jedno
z tych zada, ktre programista po prostu powinien potrafi zrealizowa; a jeli kod
jest pisany z zachowaniem profesjonalnych standardw (a wic z uwanie dobranymi
nazwami, waciwym formatowaniem, komentarzami w miejscach, gdzie jest to konieczne
itp.), decyzje podejmowane w fazie implementacji i tak powinny zosta odpowiednio
udokumentowane.
W efekcie wzorce projektowe rzadko s szczegowo opisywane w dokumentach tworzonych na etapie projektowania zamiast tego reprezentuj decyzje podejmowane
przez osoby odpowiedzialne za implementacj. Wzorce stosowane przez te osoby niemal nigdy nie s dogbnie dokumentowane, zwykle same nazwy elementw tych wzorcw (lub inne komentarze) powinny w stopniu wystarczajcym identyfikowa podejmowane dziaania. (Przykadowo, klasa WidgetFactory jest reifikacj wzorca klasywytwrni).
Istniej oczywicie wyjtki od reguy oddzielania wzorcw od waciwych projektw.
W wiecie oprogramowania odpowiednik okien wystpujcych we wzorcu naronego
biura moe si pojawia w dokumentach projektowych (ktre wskazuj programistom
miejsca rozmieszczenia okien). Podobnie, bardzo skomplikowane systemy, ktrych projekty wymagaj stosowania znacznie bardziej szczegowych opisw (tak jak plany architektoniczne dla drapacza chmur s duo bardziej szczegowe od planw dla domku
jednorodzinnego), czsto ju w wyniku fazy projektowania maj dogbnie udokumentowane wzorce projektowe.

Napicia pomidzy wzorcami a prostot


Istotnym problemem s dodatkowe komplikacje wprowadzane do systemw wanie
w wyniku stosowania wzorcw projektowych. Jak lepa zgodno jest domen ludzi
nierozgarnitych, tak niepotrzebna zoono jest domen kiepskich programistw. Mali
politycy, filozofowie i duchowni ponad wszystko ceni sobie zgodno, natomiast wielu
maych programistw i architektw sdzi, e wzorce projektowe s ze wszech miar
dobre, i e powinni ich uywa wszdzie tam, gdzie nadarza si taka okazja. Takie bezmylne podejcie niemal zawsze prowadzi do powstawania kruchych, trudnych w konserwacji programw komputerowych. Kady wzorzec ma przecie swoje wady, ktre
przemawiaj za tym, by go nie stosowa.
Proste systemy s atwiejsze w budowie, atwiejsze w konserwacji, mniejsze i szybsze od
systemw skomplikowanych. Prosty system maksymalizuje ilo wykonanej pracy przez

24

Wzorce projektowe. Analiza kodu sposobem na ich poznanie

zwikszanie (w porwnaniu ze skomplikowanymi systemami) iloci pracy, ktra nie


jest wykonana niejako przy okazji. Program musi przecie robi dokadnie to, czego
oczekuje od niego uytkownik. Dodawanie nieproszonych funkcji dramatycznie wydua czas tworzenia systemu i jednoczenie obnia jego stabilno.
Prostota zwykle nie jest celem atwym do osignicia. Programici uwielbiaj zoono
we wszelkich jej postaciach, zatem bardzo czsto wykazuj siln skonno do nadmiernego komplikowania swojej pracy. Z punktu widzenia wielu programistw znacznie
atwiej jest szybko zbudowa dosy skomplikowany system ni zmusi si do pniejszego upraszczania swojego dziea. Programici podejrzewajcy, e otrzymane wymagania bd z czasem rozwijane lub zmieniane maj tendencje do implementowania wymaga, ktre mog si pojawi w przyszoci. Komplikowanie kodu tylko dlatego, e
kto nabra mglistych podejrze o przyszych zmianach jest jednak zupenie niewaciwe.
(Za kadym razem, gdy prbuj przewidzie przyszo, popeniam zasadnicze bdy).
Programici powinni pisa kod w taki sposb, aby dodawanie nowych lub modyfikowanie istniejcych funkcji byo moliwie proste, co nie oznacza, e musz od razu implementowa ca funkcjonalno, jaka przychodzi im do gowy.
Problemem moe by take nadmierne uproszczenie rozwizania, ktre z natury rzeczy
powinno by skomplikowane. Programista, ktry naprawd chce zrobi dokadnie
to, co jest potrzebne (i w deniu do prostoty rezygnujcy z wymaganej funkcjonalnoci), tworzy rozwizanie rwnie ze, jak programista implementujcy niepotrzebne
funkcje. Przykadem nadmiernego uproszczenia jest popularna funkcja cofnij. Alan
Cooper twrca Visual Basica i ceniony specjalista od interfejsw uytkownika
argumentuje, e nigdy nie naley pyta uytkownikw, czy naprawd chc co robi.
Pewnie, e chc przecie gdyby nie chcieli, nie daliby okrelonych funkcji! Ile razy
zrezygnowae z usuwania pliku tylko dlatego, e na ekranie pojawio si to gupkowate
okno dialogowe? Najlepszym rozwizaniem problemu przypadkowego usunicia pliku
(lub innych, rwnowanych problemw) jest wykonanie dania uytkownika i jednoczenie zapewnienie moliwoci cofnicia tej operacji, jeli uytkownik popeni bd.
Funkcja cofania zmian jest obsugiwana np. przez wikszo edytorw tekstu. (Wyobraasz sobie, by edytor wywietla okno dialogowe z pytaniem: Czy naprawd chcesz
usun ten znak?). Funkcja cofania zmian jest jednak trudna do zaimplementowania,
wic dosy czsto mona si spotka z tumaczeniem lenistwa koniecznoci utrzymania prostoty oprogramowania. Kompletny system cofania zmian wprowadza tyle
dodatkowej zoonoci, e znacznie lepszym rozwizaniem wydaje si zastosowanie
tradycyjnych okien dialogowych z potwierdzeniami.
Te trzy wymagania (prostota, kompletno i atwo modyfikacji) niekiedy wzajemnie
si wykluczaj. Wzorce opisane w tej ksice s ogromnym uatwieniem midzy innymi wtedy, gdy konieczna jest zmiana lub dodanie jakiego elementu, jednak udogodnienia w tym wzgldzie s okupione dodatkowym komplikowaniem kodu rdowego.
Niestety, nie istnieje jedna prosta regua opisujca kiedy stosowanie wzorcw jest dobre,
a kiedy nie jest zalecane ocena przydatnoci wzorcw naley do stosujcych je
programistw. Due znaczenie ma tutaj dowiadczenie, ktrego wielu projektantw
i programistw po prostu nie ma (a take jak susznie stwierdzi Ken Arnold, wspautor oryginalnej ksiki o programowaniu w Javie zmysu estetycznego, ktry take
nie jest czst cech wrd programistw). Mona wic pisa fatalne programy, ktre
na kadym kroku wykorzystuj wzorce projektowe. Samo stosowanie wzorcw nie jest
adn gwarancj sukcesu w wiecie oprogramowania.

Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe

25

Z drugiej strony, budowa w kodzie rdowym blokw wzorcw (np. przez czste wykorzystywanie interfejsw) zawsze jest rozwizaniem korzystnym, nawet wtedy, gdy stosowanie w peni rozwinitych wzorcw nie jest uzasadnione. Interfejsy nadmiernie nie
komplikuj kodu rdowego, a ewentualny rozwj tego kodu w przyszoci bdzie
znacznie prostszy, jeli system od pocztku bdzie budowany na podstawie jasnej struktury interfejsw. Koszt takiego dziaania jest stosunkowo niski, natomiast potencjalne
korzyci s bardzo due.

Klasyfikacja wzorcw
W niektrych sytuacjach przydaje si klasyfikacja wzorcw, ktra uatwia wybr waciwych rozwiza. W tabeli 1.1 (pochodzcej z ksiki Bandy Czworga) przedstawiono
jeden ze sposobw podziau wzorcw projektowych. Moesz jednak samodzielnie tworzy podobne tabele, w ktrych bdziesz dzieli wzorce na kategorie wedug dobranych przez siebie kryteriw.
Tabela 1.1. Klasyfikacja wzorcw projektowych wedug Bandy Czworga
Cel

Klasa
Z
a
k
r
e
s

Konstrukcyjne

Strukturalne

Czynnociowe

Metoda wytwrcza

Adapter klas

Interpretator
Metoda szablonowa

Wytwrnia abstrakcji

Adapter obiektu

acuch odpowiedzialnoci

Budowniczy

Most

Polecenie

Prototyp

Kompozyt

Iterator

Singleton

Dekorator

Mediator

Fasada

Memento

Waga pirkowa

Obserwator

Porednik

Stan

Strategia
Wizytator

Banda Czworga podzielia wzorce projektowe na dwa zakresy: wzorce klas wymagaj
reifikacji implementacji mechanizmu dziedziczenia (sowo kluczowe extends), natomiast wzorce obiektw powinny by implementowane wycznie z wykorzystaniem
mechanizmu dziedziczenia interfejsw (sowo kluczowe implements). To nie przypadek, e istnieje znacznie wicej wzorcw klas ni wzorcw obiektw. (Wicej informacji na ten temat znajdziesz w nastpnym rozdziale).
W ramach tych dwch zakresw wzorce s dalej dzielone na trzy kategorie. Wzorce
konstrukcyjne dotycz wycznie tworzenia obiektw. Przykadowo, wzorzec wytwrni
abstrakcji zapewnia mechanizmy tworzenia obiektw bez znajomoci klas, do ktrych te
nowe obiekty nale. (Na tym etapie troch upraszczam rzeczywiste znaczenie tego wzorca, ale zagadnienia te szczegowo wyjani w dalszej czci tej ksiki). Wszystkie

26

Wzorce projektowe. Analiza kodu sposobem na ich poznanie

wzorce strukturalne nale do modelu statycznego obejmuj organizacj strukturaln


programu. Przykadowo, most opisuje sposb oddzielania od siebie dwch podsystemw w taki sposb, aby kady z nich mg by modyfikowany bez koniecznoci zmian
drugiego. Wszystkie wzorce czynnociowe nale do tzw. modelu dynamicznego, a wic
obejmuj techniki wzajemnego oddziaywania obiektw w czasie wykonywania programu. Przykadowo, wzorzec acucha odpowiedzialnoci opisuje taki system przesyania
komunikatw pomidzy obiektami, ktry umoliwia wypenianie tych komunikatw
danymi i ich przetwarzanie przez obiekty potrafice tak przygotowane komunikaty waciwie obsugiwa. Okazuje si, e jeszcze w czasie kompilacji nie musisz wiedzie,
ktre to obiekty; odpowiednie decyzje bd podejmowane dopiero w fazie wykonywania programu.
Wszystkie te wzorce omwi szczegowo (cho w innej kolejnoci) w dalszej czci
tej ksiki, pamitaj jednak, e istnieje wiele innych kategorii wzorcw projektowych
poza tymi, ktre zostay zidentyfikowane w ksice Bandy Czworga. Wzorzec programowania czasu rzeczywistego, wzorzec przetwarzania wielowtkowego czy wzorzec
Enterprise JavaBean (EJB) Javy to tylko kilka z wielu przykadw.
Kolejnym wanym zagadnieniem s wystpujce pomidzy wzorcami wzajemne zalenoci. Przykadowo, podczas lektury dalszej czci tej ksiki przekonasz si, e wzorzec polecenia w takiej czy innej formie wystpuje we wszystkich pozostaych wzorcach
czynnociowych. W ksice Bandy Czworga przedstawiono diagram pokazujcy te relacje zalenociowe, ale, szczerze mwic, ich diagram jest na tyle zawiy, e nie ma zbyt
duej wartoci praktycznej. Najwaniejsze jest zapamitanie, e rozmaite wzorce rzeczywicie s ze sob powizane, czasem w znacznym stopniu, czasem w sposb niejasny.
Jeli masz problem z odrnieniem jednego wzorca od innego, z pewnoci nie jeste
sam. Tego typu nieporozumienia najczciej s wynikiem naturalnych zalenoci pomidzy wzorcami. W takich przypadkach radz si skupia na tych czciach opisu wzorcw, ktre dotycz ich celu (przeznaczenia) pamitaj, e kada reifikacja zgodna
z zamierzeniami projektanta jest dopuszczalna. Sama analiza struktury (naturalna
w przypadku programistw) czsto rodzi niepotrzebne nieporozumienia. Z pewnoci
odkryjesz, e np. wszystkie wzorce strukturalne maj niemal identyczne struktury statyczne, cho szczegowe efekty stosowania tych struktur s bardzo zrnicowane. Struktury te dotycz w rwnym stopniu komunikacji i oprogramowania, zatem nie naley si
skupia wycznie na analizie oprogramowania.

O programowaniu, oglnie
Kolejnym wanym zagadnieniem, ktre musz przynajmniej oglnie omwi jeszcze
przed przystpieniem do analizy samych wzorcw jest projektowanie obiektowe (zorientowane obiektowo).
Po pierwsze, projektowanie obiektowe (ang. Object-Oriented Design OOD) oraz programowanie obiektowe (ang. Object-Oriented Programming OOP) to dwie zupenie
rne dziedziny. Proces projektowania rozpoczyna si od gromadzenia wymaga i wie
si z systematycznym realizowaniem takich zada jak analiza przypadkw uycia
efektem tego procesu jest projekt, na podstawie ktrego programista moe opracowywa

Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe

27

kod rdowy programu. Proces programowania rozpoczyna si od analizy projektu lub


jego czci oraz stosowania takich rozwiza jak wyprowadzanie struktur, hermetyzacja
oraz wzorce projektowe, by ostatecznie stworzy program komputerowy (bdcy realizacj otrzymanego na pocztku projektu). Wielu ludzi myli programowanie z projektowaniem. To, e od szeciu lat programujesz w Javie, rozumiesz mechanizmy wydzielania podklas i potrafisz pisa 1000 linii testowanego na bieco kodu dziennie nie
oznacza jeszcze, e znasz si na projektowaniu obiektowym. Istnieje nawet teoria, zgodnie z ktr wielu nawet najlepszych programistw nie rozumie podstawowych regu
projektowania obiektowego.
Dobr analogi jest brana budowlana. Budynki s projektowane przez architektw, ale
budowane przez wykonawcw. Systemy obiektowe s projektowane przez projektantw
obiektowych i implementowane przez programistw obiektowych. Te dwie role mog co
prawda by czone przez jedn osob, ale takie rozwizanie jest dosy rzadkie. Architekci musz wiedzie, jak konstruuje si budynki w przeciwnym razie nie byliby
w stanie opracowywa waciwych projektw. Z drugiej strony, wykonawcy w ogle nie
musz rozumie metod pracy architektw. (Nie twierdz przy tym, e nie ma architektw, ktrzy bardzo chtnie projektowaliby budynki nienadajce si do budowy lub
do zagospodarowania, lub e nie istniej wykonawcy, ktrzy potrafi bez trudu identyfikowa kiepskie projekty). Najlepsi programici s te dobrymi architektami, a najlepsi
architekci s jednoczenie dobrymi programistami. To wymieszanie umiejtnoci jest
szczeglnie istotne w popularnych obecnie tzw. metodykach lekkich, zwinnych (ang.
agile), ktre przewiduj rwnolege projektowanie i kodowanie. adna z tych metodyk
nie uwzgldnia nadrzdnej roli architekta, ktry pociga za sznurki kierujce dziaaniami programistw.
Mwi si, e wielu programistw jest dowiadczonymi rzemielnikami, ktrzy produkuj co prawda pikny kod, ale w ogle nie rozumiej procesu projektowania s budowniczymi, nie projektantami. Prosz, nie traktuj tego zdania jako przejawu mojej
pogardy dla niesamowitych umiejtnoci budowniczych chodzi tylko o to, e projekty
przygotowywane ad hoc przez programistw s czsto dalekie od ideau.
Ostatni raport zespou Standish Group dotyczcy tysicy projektw programistycznych
powstaych w cigu wielu lat stwierdza, e okoo 72 procent tego typu przedsiwzi
zakoczyo si niepowodzeniem. Za najwaniejsz przyczyn tak wysokiego wspczynnika nieudanych projektw uznano wanie brak odpowiednich projektw i wszystkie jego nastpstwa (a wic np. niewaciwa metodyka gromadzenia informacji o wymaganiach). Oznacza to, e nawet dowiadczeni architekci mog popenia bdy, jeli
zaniechaj stosowania regu rzdzcych procesami architektonicznymi.
Ta ksika jest powicona programowaniu obiektowemu i architekturom obiektowym,
nie samym procesom planowania architektur. Wzorce projektowe dotycz zwykle szczegw implementacyjnych, ktre maj zastosowanie w pracy programistw obiektowych w czasie przekadania otrzymywanych projektw na kod rdowy programw.
Stworzenie dobrego projektu nie jest jednak moliwe bez odpowiednich procesw.
(Z pewnoci takimi procesami s rozwizania proponowane w ramach metodyk
zwinnych). Co wicej, nie jest moliwe tworzenie dobrego kodu bez waciwych projektw (ktre dodatkowo mog ewoluowa w czasie). Proste stosowanie wzorcw
projektowych w kodzie (ad hoc, w nieprzemylany sposb) z pewnoci nie przyczyni

28

Wzorce projektowe. Analiza kodu sposobem na ich poznanie

si do znacznego poprawienia jakoci programu, a w skrajnych przypadkach moe t jako dodatkowo obniy. Niepotrzebne komplikacje a wiele wzorcw projektowych
jest skomplikowanych niczego nie poprawi.
Prosz wic, aby nie myli zagadnie omawianych w tej ksice z procesem projektowania obiektowego jako caoci. Wzorce s tylko niewielk czci tej ukadanki
w niektrych przypadkach ich udzia jest wrcz minimalny. Nie jest to ksika o projektowaniu obiektowym, tylko o przeksztacaniu projektw obiektowych w konkretne
implementacje. Aby jednak skutecznie stosowa wzorce projektowe, musisz wiedzie,
jak projektowa. Musisz zna proces projektowania. Na wspomnianej w przedmowie
stronie internetowej wymieniem wiele ksiek powiconych projektowaniu i zalecam
ich uwane przeczytanie.

Programowanie jzyka FORTRAN w Javie


Skoro niniejsza ksika jest tak cile zwizana z zagadnieniami programowania obiektowego, warto chyba powici chwil na omwienie rnic pomidzy technik obiektow a proceduralnym programowaniem systemw (przynamniej na poziomie strukturalnym). Proceduralne podejcie do programowania mona scharakteryzowa jako
ukierunkowane na dane, gdy struktura programu proceduralnego koncentruje si wok przepywu danych pomidzy funkcjami (procedurami), ktre te dane przetwarzaj
lub sprawdzaj. Centralnym elementem projektw tego typu programw zwykle jest baza
danych; w praktyce bardzo wiele programw proceduralnych sprowadza si do prezentowania tabel bazy danych za porednictwem odpowiedniego interfejsu uytkownika.
Systemy proceduralne s zwykle mocno zhierarchizowane, koncentruj si wok pojcia globalnej kontroli. Element globalny (majcy zwykle posta funkcji lub procedury
na szczycie tej hierarchii) przetwarza dane zebrane z innych rde albo funkcji na
niszych poziomach hierarchii, albo utworzonych wczeniej danych globalnych. Gwn
wad systemw proceduralnych s utrudnienia w diagnostyce i konserwacji. Wspdzielone dane tworz relacje sprzgajce (niepodane zalenoci) pomidzy poszczeglnymi funkcjami systemu. Kiedy zostanie zmieniona jedna funkcja, wprowadzona modyfikacja bdzie miaa wpyw na dziaanie pozostaych. W skrajnych przypadkach efektem
z pozoru banalnej zmiany w jednej z funkcji moe by konieczno powicenia caych
miesicy na oczyszczenie i naprawienie kodu caego systemu.
Z drugiej strony, systemy obiektowe s w istocie siatk wsppracujcych agentw, ktre
wzajemnie si komunikuj za porednictwem jakiego systemu przesyania komunikatw. Obiekty s rwnowane nie istnieje obiekt zwierzchni, ktry mgby wydawa
dyrektywy pozostaym obiektom. Co prawda waciwoci dobrze zaprojektowanych
obiektw bd szczegowo omawia w dalszej czci tego rozdziau, warto jednak ju
teraz wprowadzi kilka najwaniejszych zagadnie. Patrzc na obiekt z zewntrz, nie
powinnimy mie pojcia o sposobie jego implementacji. Powinna wic istnie moliwo wymiany caej implementacji bez koniecznoci modyfikowania ktregokolwiek
z obiektw klienckich (obiektw wykorzystujcych obiekt, ktry wanie zosta zmieniony). Mimo e obiekty czasami przekazuj pomidzy sob inne obiekty, przepyw
danych jest zupenie inny ni w systemach proceduralnych. Obiekt pilnie strzee swoich
danych i wykonuje na nich operacje w odpowiedzi na otrzymywane komunikaty. Obiekty

Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe

29

nie przekazuj danych do innych obiektw, chyba e jest to absolutnie konieczne (nawet
wwczas przekazywane dane s hermetycznie zamykane w ramach tworzonych w tym
celu obiektw). Te dwie koncepcje (ukrywanie implementacji i abstrakcja danych) w wiecie systemw obiektowych maj kluczowe znaczenie.
Dobrym sposobem odrniania systemw obiektowych od systemw proceduralnych
jest analiza efektw wprowadzanych zmian. W systemach proceduralnych wszelkie zmiany wpywaj na pozostae skadniki programu, a due zmiany w dziaaniu programu wymagaj zwykle bardzo gbokich i rozlegych zmian w caym kodzie. W systemach obiektowych niezbdne modyfikacje mona wprowadza tylko w wybranych punktach.
Pojedyncza zmiana w kodzie systemu obiektowego moe powodowa znaczne zmiany
w zachowaniu caego programu. Przykadowo, jeli decydujesz si na zmian formatu
danych wykorzystywanego do przechowywania informacji, w przypadku systemu proceduralnego musiaby zmieni kod w wielu miejscach, poniewa dane s przetwarzane
przez wiele procedur; w systemie obiektowym zmiany bd dotyczyy wycznie obiektu
odpowiedzialnego za skadowanie danych.
Oczywicie takie reguy programowania obiektowego jak zapewnianie abstrakcji danych
(ukrywanie sposobu dziaania zestawu funkcji przez chowanie struktur danych przed
uytkownikami tych funkcji) istniay od dawna i stanowiy podstaw technik tworzenia
wysokiej jakoci oprogramowania w rozmaitych rodowiskach i jzykach take
proceduralnych. Przykadowo, zarwno system obsugi operacji wejcia-wyjcia na
plikach w jzyku C, jak i biblioteka Curses Kena Arnolda s rozwizaniami obiektowymi.
System proceduralny moe miejscami wyglda jak system obiektowy. System czysto
obiektowy charakteryzuje si przede wszystkim konsekwentnym i skrupulatnym stosowaniem pewnych regu (np. wspominanej ju abstrakcji danych).
Systemy obiektowe odrnia od systemw proceduralnych jeszcze jeden kluczowy
element. Przykadowo, systemy obiektowe w wikszoci przypadkw odwzorowuj (modeluj) procesy wiata rzeczywistego. Ten tok mylenia moe Ci doprowadzi do oglnej koncepcji procesu projektowania obiektowego; poniewa jednak ta ksika w gwnej mierze dotyczy struktury obiektowej, nie bd powica tym zagadnieniom zbyt
wiele czasu.
Wiele osb, ktre wychoway si w wiecie systemw proceduralnych sdzi, e obiektowe podejcie do problemw informatycznych jest czym niewaciwym. Zawsze jestem
zdumiony, kiedy czytam o kontrowersjach wywoanych przez moje artykuy o technikach
programowania obiektowego. Kiedy opublikowaem (w internetowym magazynie JavaWorld) wstpny zarys tej ksiki, doznaem prawdziwego szoku, czytajc obelywe
opinie o rzekomym oderwaniu prezentowanych koncepcji od rzeczywistoci koncepcji, ktre przewijaj si w literaturze informatycznej od trzydziestu lat. Byem nazywany dyletantem, szukajcym po omacku, niegodziwcem, gupkiem i opisywany
kilkoma innymi epitetami, ktrych nie powinienem tutaj przytacza. Moje artykuy byy
przez niektrych okrelane jako durne i pozbawione najmniejszego sensu. Jeden
z czytelnikw posun si nawet do fizycznych grb, rozpoczynajc swj napastliwy list
(szybko usunity przez administratora witryny) w nastpujcy sposb: TEEMU [sic]
AUTOROWI KTO WRESZCIE POWINIEN ROZWALI EB PRTEM!

30

Wzorce projektowe. Analiza kodu sposobem na ich poznanie

Nie naley utosamia tego, co jest nam znane, z tym, co jest prawidowe. Wielu programistw zakada, e biblioteki, ktre regularnie wykorzystuj w swojej pracy s waciwe; a jeli ktra z nich wykonuje pewne operacje w okrelony sposb, programici
ci uwaaj, e wanie ta technika realizacji tego typu operacji jest standardem. Takie
postrzeganie swojego rodowiska mona obserwowa szczeglnie czsto u osb, ktre
uczyy si programowania z podrcznikw opisujcych rozwizywanie konkretnych
problemw i realizacj cile wybranych zada. Jeli jedyn architektur, z ktr kiedykolwiek mieli do czynienia, byo EJB lub Struts, bd klasyfikowali jako niewaciwe
wszelkie rozwizania nieprzypominajce ani EJB, ani Struts. To, e w przeszoci realizowalimy zadania w okrelony sposb, wcale nie oznacza, e by to sposb najlepszy;
gdyby rzeczywicie tak byo, nadal programowalibymy w jzykach asemblerowych.
Wiele lat temu odbyem bardzo interesujc rozmow z osob, ktra pracowaa w firmie
Microsoft nad rodowiskiem programowania w jzyku C++ i nad bibliotek Foundation Class (MFC). Kiedy stwierdziem, e MFC nie jest bibliotek obiektow, odpowiedzia, e doskonale zdaje sobie z tego spraw, ale wikszo ludzi programujcych dla
systemw firmy Microsoft po prostu nie rozumie regu rzdzcych wiatem obiektw.
Powiedzia te, e uczenie ludzi programowania obiektowego nie jest zadaniem Microsoftu. Efekt by taki, e Microsoft celowo stworzy system proceduralny w jzyku C++,
poniewa wanie taki system by atwiejszy do zrozumienia. Teoria o trudnym do
zrozumienia programowaniu obiektowym jest w firmie Microsoft zakorzeniona tak gboko, e nawet struktura interfejsw API technologii .NET jest w istocie proceduralna,
a np. jzyk programowania C# zawiera mechanizmy wrcz zachcajce do mylenia proceduralnego. Nic dziwnego, e wiele aplikacji firmy Microsoft jest tworzonych wbrew
podstawowym reguom rzdzcym systemami obiektowymi. Wielu spord programistw tej firmy podchodzi bardzo nerwowo do wszelkich technik obiektowych, ktre
nie s zgodne z rozwizaniami zastosowanymi w ramach technologii .NET. Myl to,
co jest im znane, z tym, co prawidowe.
Nie prbuj do systemw obiektowych przykada miary typowej dla systemw proceduralnych, nie krytykuj te opisywanych przeze mnie technik obiektowych tylko dlatego, e nie s zgodne z koncepcj programowania proceduralnego. Wiele popularnych
poj zwizanych z programowaniem obiektowym mogo w ogle nie wystpowa
w istniejcym kodzie, ktry miae okazj analizowa. Twierdzenie, e ktra z technik
kodowania jest niewykonalna w systemach obiektowych wcale nie musi oznacza, e
dana technika w ogle nie ma zastosowa. Bd o tym przypomina za kadym razem,
gdy bd analizowa obiektowe podejcie do jakiego problemu.
I wreszcie pamitaj, e rozwizanie czysto obiektowe nie zawsze jest konieczne ani
najwaciwsze. Tak jak w przypadku wikszoci decyzji podejmowanych w fazie projektowania rozwiza, koncepcja projektowania obiektowego ma pewne wady i moe
by rdem dodatkowego ryzyka. Przykadowo, prosta witryna internetowa zbudowana
na podstawie serwletw, ktra stanowi tzw. cienki fronton bazy danych, prawdopodobnie nie musi by tworzona zgodnie z surowymi reguami programowania obiektowego.
W tym przypadku ryzyko sprowadza si do nadmiernego komplikowania kodu (nawet
uniemoliwiajcego waciwe zarzdzanie) w zwizku z naturaln ewolucj programu.
Podobnie, wielu programistw nie rozumie koncepcji programowania obiektowego,
jeli wic Twj system nie ma okrelonych dugoterminowych wymaga w zakresie konserwacji, i jeli mona przyj, e wymagania biznesowe nie ulegn zmianie, przypisanie

Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe

31

do realizacji tego zadania programisty, ktry bdzie potrafi szybko zaimplementowa


rozwizanie proceduralne wcale nie musi by zym posuniciem. Istnieje oczywicie ryzyko, e czas ycia tego programu bdzie duszy od oczekiwanego, lub e nastpi istotne
zmiany regu biznesowych, ktre sprawi, e wyrzucenie oryginalnego kodu do kosza
bdzie mniej kosztowne od jego modyfikowania. Stosowanie rozwiza proceduralnych
nie jest z gruntu ze, powiniene jednak podejmowa odpowiednie decyzje z pen wiadomoci przyjmowanego ryzyka.

Programowanie z otwartymi oczami


Porozmawiajmy teraz o oglnej filozofii projektowania.
Projekt jest szeregiem odpowiednio uzasadnionych wyborw i ustpstw, a take analiz ryzyka. Jeli nie rozumiesz obu stron rozwizywanego problemu, nie moesz dokonywa rozsdnych wyborw i efektywnie zarzdza ryzykiem; w praktyce, jeli nie masz
wiadomoci wszystkich nastpstw podejmowanych decyzji, tak naprawd niczego nie
projektujesz, a jedynie bdzisz po omacku. To nie przypadek, e kady rozdzia ksiki
Bandy Czworga zawiera podrozdzia Skutki, w ktrym opisano (wraz z uzasadnieniem) sytuacje, w ktrych stosowanie danego wzorca projektowego jest niewaciwe.
Co wicej, w wiecie projektw pojcia dobra i za nie s bezwarunkowe. Dobra
decyzja w jednym kontekcie moe by z decyzj w innym kontekcie. Kada decyzja ma swoje dobre i ze strony jest podejmowana w kontekcie wszelkich, zdefiniowanych z koniecznoci kryteriw. Ocena decyzji rzadko moe by wyraana w skali
binarnej. Bardzo czsto mamy do czynienia z odcieniami susznoci (konsekwencji dokonanego wyboru), ktre w wielu przypadkach oznaczaj, e adna z rozwaanych moliwoci nie moe by jednoznacznie uznana za najlepsz. Co wicej, decyzje, ktre
wygldaj na suszne, za kilka miesicy mog by postrzegane zupenie inaczej.
Twierdzenie, e ktra z funkcji jzyka lub jedno z popularnych rozwiza programistycznych stwarza pewne problemy, wcale nie jest rwnoznaczne z tez, e nigdy, w adnych okolicznociach nie naley z tej funkcji lub z tego rozwizania korzysta. Podobnie, sama popularno pewnej funkcji lub rozwizania nie oznacza, e zawsze naley
z nich korzysta. Bardzo wiele programw jest pisanych przez niedoinformowanych
i niekompetentnych programistw, poniewa samo zatrudnienie w takich firmach jak
Sun, Microsoft czy IBM nie powoduje nagej poprawy umiejtnoci programowania
i projektowania. W pakietach Javy mona znale mnstwo doskonaego kodu, ale take
niemao kodu, w przypadku ktrego trudno znale programist gotowego si przyzna
do autorstwa (co wcale mnie nie dziwi).
Projektowanie jest dodatkowo utrudniane przez fakt, e pewne cechy projektw s promowane wycznie z przyczyn marketingowych lub politycznych. Zdarza si, e to
programista podejmuje z decyzj, ale zatrudniajca go firma koniecznie chce sprawdzi moliwoci wykorzystywanej technologii i celowo dezawuuje waciwe rozwizania. To chyba najbardziej jaskrawy przykad nieodpowiedniego podejcia do problemu
projektowania systemw informatycznych. W tym kontekcie wdraanie wszelkich technik programowania tylko dlatego, e tak to si teraz robi jest oczywicie przejawem
wyjtkowej lekkomylnoci. Doskonaym dowodem na powane ryzyko wynikajce

32

Wzorce projektowe. Analiza kodu sposobem na ich poznanie

z takiego podejcia jest ogromna liczba nieudanych projektw opartych na technologii


EJB. Jeli technologia ta jest stosowana waciwie, moe by bardzo przydatna; jeli
jednak jest wykorzystywana w sposb bezmylny, moe w krtkim czasie doprowadzi do bankructwa jej bezkrytycznych propagatorw.
Tak naprawd prbuj Ci tylko zniechci do lepego, bezmylnego stosowania popularnych rozwiza. Rozumienie potencjalnych zagroe wynikajcych ze stosowania
danej funkcji lub danego rozwizania stawia Ci w znacznie lepszej pozycji podczas
decydowania o wykorzystaniu funkcji lub rozwizania w swoim programie. Podejmujc
decyzj w tym zakresie, powiniene korzysta z caej dostpnej wiedzy i jednoczenie
wykazywa pragmatyzm tylko w ten sposb moesz przynajmniej ograniczy ryzyko
dokonywania zych wyborw. Dlatego te podjem trud napisania tej ksiki; chciaem, aby potrafi si porusza w wiecie programowania z otwartymi oczami.

Czym jest obiekt?


Co tak naprawd oznacza popularne dzisiaj zorientowanie na obiekty (zorientowanie
obiektowe)?
Omawiane w tej ksice wzorce projektowe s skadnikami systemw obiektowych
(zorientowanych obiektowo). Jeli jaki system jako cay nie jest obiektowy, stosowanie
wzorca obiektowego tylko w jednym z jego skadnikw nie przyniesie oczekiwanych
korzyci. Odkryem, e wielu programistom (take tym, ktrzy latami pracowali w takich
jzykach jak C++ czy Java) brakuje wiedzy o tym, co dokadnie czyni system informatyczny obiektowym, zatem musz si ju teraz upewni, e mamy w tym wzgldzie
cakowit jasno.

Nonsens!
Bjarne Stroustrup, twrca jzyka programowania C++, scharakteryzowa kiedy programowanie obiektowe jako programowanie zorientowane na bekot techniczny i z pewnoci jednym z najczciej naduywanych (lub przynajmniej uywanych w sposb zupenie bezmylny) sw-bekotw jest sam obiekt. Poniewa wanie koncepcja obiektu
ma charakter centralny, precyzyjne wyjanienie, czym faktycznie jest obiekt, ma zasadnicze znaczenie dla rozumienia systemw obiektowych i ich potrzeb.
Po pierwsze, system obiektowy powiniene traktowa jak stado inteligentnych zwierzt
(zbir obiektw) zamknitych w Twoim komputerze, ktre rozmawiaj, przesyajc
pomidzy sob komunikaty. Wyobra sobie te obiekty. Klasy s tutaj nieistotne
maj jedynie charakter struktur pomocniczych stworzonych z myl o atwiejszej pracy
kompilatora. Zwierzta tworzce taki system mog by klasyfikowane (grupowane),
jeli maj podobne waciwoci (jeli np. potrafi obsugiwa te same komunikaty, co
inne obiekty nalece do tej samej klasy). Programici mwicy o klasach tak naprawd
maj na myli klasy obiektw. Oznacza to, e obiekty majce te same waciwoci tworz jedn klas obiektw. S to rozwaania na poziomie jzykowym (nie jest to wic

Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe

33

techniczny bekot) i wanie ten sposb jest najwaciwsz form mwienia o systemach
obiektowych. Zawsze mwimy o projektowaniu obiektowym, nigdy o projektowaniu
klasowym.
Najwaniejszym aspektem caej koncepcji projektowania obiektowego jest abstrakcja
danych. Wanie w abstrakcji danych tkwi tajemnica projektowania tego typu programw. Wszystkie informacje s ukrywane. Poszczeglne obiekty nie maj pojcia
o zawartoci pozostaych obiektw nie wiedz o nim wicej ni Ty o woreczku
ciowym swojego wspmaonka. (Zarwno w przypadku obiektw, jak i wspomnianego woreczka ciowego zapewne adna ze stron nawet nie chce tej brakujcej
wiedzy zdoby).
By moe miae kiedy okazj czyta ksik, ktrej autor dowodzi, e obiekt jest
struktur danych poczon ze zbiorem funkcji, nazywanych metodami, przetwarzajcych
t struktur danych. Nonsens! Bzdura!

Obiekt jest zbiorem zdolnoci


Po pierwsze i najwaniejsze, obiekt jest definiowany przez to, co moe robi, nie przez
to, jak to robi. W sensie praktycznym oznacza to, e obiekt jest definiowany przez
komunikaty, ktre moe otrzymywa i wysya. Metody obsugujce te komunikaty
tworz jedynie interfejs czcy ten obiekt ze wiatem zewntrznym. Nacisk naley ka
na to, co obiekt moe robi (na jego zdolnoci), nie na to, jak oferowane czynnoci zostay zaimplementowane. Przetwarzane dane nie maj wikszego znaczenia. Wikszo
projektantw programw obiektowych spdza mnstwo czasu na planowaniu okrelonych mechanizmw w oderwaniu od danych (bdcych tylko jednym ze skadnikw
obiektu). Zdecydowana wikszo obiektw bdzie oczywicie wymagaa jakich danych
potrzebnych do zaimplementowania wykonywanych czynnoci, ale sama struktura tych
danych jest a przynajmniej powinna by nieistotna z punktu widzenia projektanta.
Naczelna zasada systemw obiektowych mwi:
Nigdy nie pro obiektu o informacje, ktrych potrzebujesz do wykonania
jakiej czynnoci; zamiast tego pro obiekt zawierajcy te informacje
o wykonanie tych czynnoci za Ciebie.
Ken Arnold wyraa t zasad w sposb nastpujcy: Pro o pomoc, nie o informacje.
Powysz zasad wyjani za chwil, najpierw jednak skupi si na kilku wanych
reguach wynikajcych wprost z tej zasady na podstawie tych regu moesz ocenia,
czy rzeczywicie masz do czynienia z projektem systemu obiektowego (na razie przedstawi je w moliwie zwizy sposb; wicej szczegw znajdziesz w dalszej czci
tego rozdziau):
Obiekty s definiowane na zasadzie kontraktw. Obiekty nigdy nie ami

swoich kontraktw.
Wszystkie dane s prywatne. Kropka. (Ta regua powinna by stosowana

do wszystkich szczegw implementacyjnych, nie tylko do danych).

34

Wzorce projektowe. Analiza kodu sposobem na ich poznanie


Musi istnie moliwo wprowadzania dowolnych zmian w sposobie

implementacji obiektu (niezalenie od tego, jak istotne s te zmiany)


przez modyfikowanie pojedynczej klasy, ktra ten obiekt definiuje.
Funkcje zwracajce i ustawiajce (get i set) s ze, jeli stosuje si je

bezmylnie (jeli stanowi tylko wyszukany sposb upubliczniania danych


przechowywanych w obiekcie). Zagadnienie to dodatkowo rozwin
w podrozdziale Metody zwracajce i ustawiajce s ze.
Jeli analizowany przez Ciebie system nie spenia tych regu, moesz by pewien, e nie
masz do czynienia z systemem obiektowym. To bardzo proste. Nie twierdz przy tym,
e systemy nieobiektowe s ze istnieje mnstwo doskonaych systemw proceduralnych. Tak czy inaczej, ukrywanie danych jest w projektowaniu obiektowym zasad
absolutnie podstawow; jeli wic naruszysz t regu, popenisz niewybaczalny bd.
To samo dotyczy systemw obiektowych jeli system informatyczny amie zasad
ukrywania danych, zgodnie z definicj nie jest systemem obiektowym, a jedynie jak
dziwaczn hybryd, ktra moe, ale nie musi dziaa prawidowo. Kiedy taki system
popadnie w tarapaty i pocignie za sob Twoj firm, nie wi za to techniki obiektowej. Warto jednak pamita, e systemy obiektowe mog by pisane w jzykach proceduralnych (i odwrotnie). To kwestia regu stosowanych przez programist, nie wykorzystywanego jzyka programowania.
Nie daj si zwie marketingowym hasom o systemach opartych na technice obiektowej lub o istnieniu wielu rnych sposobw definiowania obiektw. Tego typu zapewnienia moesz tumaczy w nastpujcy sposb: Nasz produkt tak naprawd nie jest
obiektowy my o tym wiemy, ale takiej wiedzy najprawdopodobniej nie ma Twj
szef (ktry podejmuje decyzje o zakupach oprogramowania), wic prbujemy zaciemni obraz w nadziei, e nikt tej mistyfikacji nie zauway. Microsoft posun si jeszcze
dalej wprowadzi wasn definicj systemw obiektowych, ktra pasowaa do produktw oferowanych przez t firm. Przykadowo, w przeszoci jzyk Visual Basic nie
by jzykiem obiektowym i nawet teraz, kiedy inynierowie i specjalici od marketingu
robi wszystko, by VB by postrzegany jako jzyk obiektowy, wikszo programw
pisanych w tym jzyku i tak nie jest obiektowa, poniewa biblioteki Microsoftu nie s
obiektowe. (Ilu programistw Microsoftu jest potrzebnych do wkrcenia arwki?
aden zdefiniujmy ciemno jako nowy standard przemysu informatycznego).
Przejdmy teraz do omawiania wymienionych przed chwil regu, ktre rzdz systemami obiektowymi.
W pierwszej kolejnoci musimy wyjani pojcie kontraktu. Kontrakt obiektu definiuje
sposb prezentowania jego zachowa dla wiata zewntrznego. Uytkownicy tego obiektu
zakadaj, e zachowanie to nie bdzie podlegao zmianom. Czci tego kontraktu s
nie tylko interfejsy implementowane przez obiekt (nie moesz wic lekk rk zmienia
np. argumentw metod czy typw zwracanych przez nie wartoci), ale take gwarancje
w zakresie wydajnoci, ograniczenia rozmiarw itp. Implementacja obiektu nigdy nie
naley do jego kontraktu, zatem zawsze powiniene mie moliwo jej modyfikowania
wedug uznania.
Wszystkie pozostae wymienione reguy tak naprawd maj na celu jedynie wymuszenie
zgodnoci obiektu z kontraktem. Odkrycie szczegw implementacyjnych w praktyce

Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe

35

oznaczaoby wczenie tych szczegw do kontraktu obiektu, a wic wykluczenie moliwoci modyfikowania implementacji (np. w odpowiedzi na wykrycie bdu lub pojawienie si nowych wymaga biznesowych).
Podobnie, regu zapewniania prywatnoci wszystkich danych obiektu naley interpretowa w nastpujcy sposb jeli ktry ze skadnikw obiektu nie jest prywatny,
automatycznie staje si czci kontraktu, zatem nie moe by modyfikowany. W pewnych (rzadkich) sytuacjach decyzja o publicznym deklarowaniu pola obiektu moe by
poprawna, ale jej nastpstwa zawsze s bardzo powane.
Pojcie kontraktu odgrywa te istotn rol w trzeciej z wymienionych regu. Idealnym
rozwizaniem jest ograniczenie zakresu zmian do pojedynczej klasy, jednak w wikszoci
przypadkw uniknicie wzajemnych zalenoci jest po prostu niemoliwe. Przykadowo, klasa HashMap wymaga od przechowywanych obiektw implementowania metody
hashCode(). To i podobne wymagania musz by czci kontraktw przechowywanych
obiektw.

Jak nie naley tego robi?


Zasadniczym argumentem przemawiajcym za stosowaniem regu wymienionych w poprzednim punkcie jest znaczne uatwienie konserwacji kodu rdowego, poniewa wszelkie zmiany zwizane z usuwaniem usterek bd dodawaniem nowych funkcji s dokonywane w jednym miejscu. Nawiasem mwic, nie naley myli atwoci konserwacji
kodu z jego prostot. Systemy obiektowe s zwykle bardziej skomplikowane od systemw proceduralnych, ale jednoczenie atwiejsze w konserwacji. Idea programowania
obiektowego sprowadza si do odpowiedniego organizowania nieuniknionej zoonoci
(bdcej naturalnym elementem rzeczywistych programw komputerowych), nie do jej
eliminowania projektanci systemw obiektowych doskonale zdaj sobie spraw
z faktu, e cel ten jest niemoliwy do osignicia.
Przykadowo, podczas tworzenia systemu, ktry musi pobiera nazwisko od uytkownika moesz ulec pokusie uycia kontrolki TextField, z ktrej odczytasz obiekt klasy
String takie rozwizanie nie bdzie jednak funkcjonowao prawidowo we wszystkich moliwych sytuacjach. Co bdzie, jeli system bdzie wykorzystywany w Chinach? (Twrcy Unicode zbioru znakw wykorzystywanego w Javie s obecnie
bliscy osignicia reprezentacji wszystkich znakw ideograficznych stosowanych w jzyku
chiskim). Co bdzie, jeli kto zechce wpisa swoje imi za pomoc pira (lub uy
mechanizmu rozpoznawania mowy) zamiast tradycyjnej klawiatury? Co bdzie, jeli wykorzystywana przez Ciebie baza danych nie zezwala na umieszczanie w niej znakw
Unicode? Co powiniene zrobi, jeli za rok bdziesz musia zmodyfikowa program
w taki sposb, aby pobiera jednoczenie nazwisko i identyfikator pracownika wszdzie
tam, gdzie do tej pory byo pobierane lub wywietlane samo nazwisko? W systemie
proceduralnym rozwizanie tych kwestii bdzie si wizao z powanymi problemami
w zakresie konserwacji kodu rdowego (co w przypadku tego typu systemw jest zupenie normalne). Po prostu nie istnieje atwy sposb rozwizywania nawet z pozoru
bardzo prostych problemw, a zupenie banalne modyfikacje wymagaj zwykle ogromnego wysiku.

36

Wzorce projektowe. Analiza kodu sposobem na ich poznanie

Rozwizanie obiektowe jest prb hermetycznego zamknicia tych skadnikw oprogramowania, ktre najprawdopodobniej bd podlegay zmianom w ten sposb modyfikowana cz programu nie bdzie miaa wpywu na jego pozostae skadniki. Przykadowo, typowym rozwizaniem obiektowym dla opisanego przed chwil problemu
jest zastosowanie klasy Name, ktrej obiekty bd wiedziay nie tylko jak wywietla
swoje informacje, ale take jak si inicjalizowa. Wywietlanie nazwiska bdzie wwczas wymagao uycia polecenia Wywietl swoje informacje w tym miejscu i przekazania obiektu klasy Graphics (a by moe take obiektu klasy Container, za porednictwem ktrego obiekt nazwiska bdzie mg umieci przechowywane dane w panelu
JPanel). Obiekt klasy Name moe oczywicie wybra rozwizanie polegajce na utworzeniu kontrolki TextField, ale decyzja w tym wzgldzie naley wycznie do niego.
Ciebie, programist, po prostu nie interesuje sposb, w jaki obiekt nazwiska bdzie si
inicjalizowa (przynajmniej do czasu, kiedy ta inicjalizacja przebiega prawidowo). Implementacja tego obiektu moe nie tworzy adnych elementw interfejsu uytkownika moe np. pobiera warto pocztkow przez wykonanie odpowiedniego dania
na bazie danych lub za porednictwem sieci komputerowej.
Wracajc do mojej krytyki jzyka Visual Basic sprzed kilku akapitw, przyjrzyjmy si
typowemu, strukturalnemu sposobowi generowania elementw interfejsu uytkownika
przez ten jzyk (lub niezliczone systemy podobne do Visual Basica): tworzysz klas
Frame, ktrej zadaniem jest gromadzenie komunikatw przychodzcych z kontrolek
(z obiektw kontrolek) w odpowiedzi na dziaania podejmowane przez uytkownika.
Klasa Frame przekazuje te komunikaty dalej do systemu obiektw. Zbudowany w ten
sposb kod zazwyczaj przyjmuje nastpujc posta:
1. Wyciganie jakiej wartoci za pomoc metody get.
2. Umieszczanie jakiej wartoci w obiekcie biznesowym za porednictwem
metody set.

Takie rozwizanie jest czsto nazywane architektur modelu-widoku-kontrolera (ang.


Model-View-Controler, w skrcie MVC) kontrolki odgrywaj rol widoku, obiekt
klasy Frame peni funkcj kontrolera, natomiast dziaajcy w tle system jest modelem.
Architektura MVC sprawdza si znakomicie w przypadku implementacji maych elementw, np. przyciskw, ale kompletnie zawodzi na poziomie caych aplikacji, poniewa koncepcja MVC przewiduje, e kontroler wie znacznie wicej o sposobie implementacji obiektw na poziomie modelu ni np. w koncepcji programowania obiektowego.
W systemie zbudowanym na podstawie tej architektury zbyt duo danych jest przesyanych pomidzy warstwami, aby bya moliwa jego atwa konserwacja.
Zamiast wierzy mi na sowo, sam przeanalizuj kilka typowych problemw konserwacyjnych (opisywanych ju w tym rozdziale), ktre wystpi w przypadku prb tworzenia duych programw na podstawie architektury modelu-widoku-kontrolera. Jeli
np. wemiemy wspomniany ju problem dodania identyfikatora pracownika do kadego
okna wywietlajcego jego nazwisko, w architekturze typowej dla jzyka Visual Basic
musiaby rcznie zmodyfikowa wszystkie te okna (przez zmian lub dodanie kontrolek obsugujcych nowe pole identyfikatora). Musiaby te doda nowe elementy do
klasy Employee, aby umoliwi ustawianie tego identyfikatora, a take skrupulatnie przeanalizowa kod kadej klasy wykorzystujcej obiekty klasy Employee, aby upewni si,

Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe

37

e wprowadzenie identyfikatora nie rodzi adnych dodatkowych komplikacji. (Przykadowo, operacje porwnywania dwch obiektw klasy Employee musz teraz uwzgldnia pole ID, zatem konieczne bdzie zmodyfikowanie kodu zawierajcego takie operacje). Gdyby zawar identyfikator pracownika w ramach klasy Name, mgby unikn
tego typu problemw. Obiekty tej klasy byyby po prostu wywietlane w nowy sposb.
Dwa obiekty klasy Name mona by nawet porwnywa na podstawie informacji na
temat identyfikatora, ale np. kod w postaci fred.compareTo(ginger) lub fred.equals
(ginger) nie wymagaby wprowadzania adnych modyfikacji.
Nie moesz nawet zautomatyzowa procesu aktualizacji (dostosowywania) kodu, poniewa
caa ta funkcjonalno WYSIWYG, o ktrej tak czsto mona przeczyta w materiaach marketingowych, ukrywa proces generowania kodu. Jeli automatycznie modyfikujesz kod wygenerowany przez komputer, wprowadzone zmiany zostan utracone
w momencie, w ktrym kto inny uyje tego samego generatora. Nawet jeli jeste pewien, e narzdzie to nie zostanie ponownie zastosowane, modyfikowanie wygenerowanego kodu jest zawsze ryzykowne, poniewa wikszo narzdzi z rodziny Visual
Basica wykazuje bardzo niewiele tolerancji dla kodu wygldajcego inaczej ni ten,
ktry zazwyczaj generuj. A jeli Twoje modyfikacje bd polegay na wprowadzeniu
jakich nieoczekiwanych elementw, stosowane narzdzie moe si pogubi do tego
stopnia, e odmwi wykonywania na tym kodzie wszelkich dalszych operacji wtedy,
gdy bdziesz tego naprawd potrzebowa. Co wicej, automatycznie generowany kod
zwykle jest do kiepski, poniewa tworzce go narzdzia nie uwzgldniaj takich
czynnikw jak efektywno, zwizo, czytelno itp.
Bodaj najwiksz wad architektury MVC jest koncepcja kontrolki siatki powizanej
z danymi (ang. data-bound grid control), czyli takiego formantu-tabeli, ktry reprezentuje jednoczenie kod jzyka SQL potrzebny do wypenienia jego komrek zawartoci bazy danych. Co si dzieje, kiedy sownik danych ulega zmianie? Ot cay ten
osadzony kod SQL-a nadaje si wwczas do kosza lub przynajmniej wymaga zasadniczych modyfikacji. Bdziesz musia dokadnie przeanalizowa wszystkie te okna swojego systemu, ktre zawieraj kontrolki powizane z danymi i modyfikowa je za pomoc
swojego wizualnego narzdzia. Zastosowanie architektury trjwarstwowej, w ktrej
warstwa interfejsu uytkownika komunikuje si z warstw zawierajc kod jzyka SQL,
a ta z kolei wsppracuje z warstw bazy danych, nie tylko nie rozwie problemu,
ale wrcz pogorszy Twoj sytuacj, poniewa kod, ktry bdziesz musia zmodyfikowa
zostanie dodatkowo rozproszony pomidzy warstwy. Tak czy inaczej, jeli do budowy
warstwy rodkowej wykorzystasz automatyczny generator kodu (co w dzisiejszych czasach jest zjawiskiem powszechnym), to przydatno tej warstwy z punktu widzenia osb
odpowiedzialnych za konserwacj kodu bdzie minimalna.
Cae to zamieszanie zwizane z koniecznoci rcznego modyfikowania wszystkich okien
przynajmniej dla mnie stanowi wystarczajcy argument, by nie stosowa tego typu
rozwiza. Jakiekolwiek oszczdnoci czasowe wynikajce ze stosowania generatora
kodu prdzej czy pniej zostan zrwnowaone przez znacznie wysze koszty konserwacji systemu.
Systemy tego typu przycigaj programistw przede wszystkim tym, e s konstruowane
za pomoc znanych im technik. Generatory kodu uatwiaj programowanie w nieznanym
jzyku obiektowym, oferujc przyjazne lub wrcz intuicyjne mechanizmy proceduralne.

38

Wzorce projektowe. Analiza kodu sposobem na ich poznanie

Podejcie, ktre mona streci zdaniem: potrafi programowa w jzyku FORTRAN,


stosujc dowolny inny jzyk, w praktyce wyklucza moliwo osigania rzeczywistych korzyci w zakresie konserwacji systemw obiektowych. Uwaam, e uywanie
Javy kompletnie mija si z celem, jeli nie implementuje si naprawd obiektowego projektu. Java jest prosta, ale tylko w porwnaniu z jzykiem C++. Jeli chcesz pisa
systemy proceduralne, lepiej od razu wybierz ktry z naprawd prostych jzykw proceduralnych. (Nie zgadzam si z wieloma zwolennikami Javy, ktrzy twierdz, e takie mechanizmy jak bezpieczestwo typw, dynamiczne wczytywanie itp. usprawiedliwiaj pisanie w tym jzyku kodu proceduralnego).
Z drugiej strony, jeli rzeczywicie realizujesz projekt obiektowy, stosowanie jzyka zaprojektowanego specjalnie z myl o tego typu systemach (a wic np. Javy) moe znacznie uatwi proces implementacji. Wielu programistw jzyka C prbuje programowa
w Javie zupenie tak, jakby pracowali w C i implementuje w jzyku Java systemy proceduralne zamiast systemw obiektowych (dla ktrych ten jzyk stworzono). Do takich
praktyk skania programistw sam jzyk programowania, ktrego skadnia niestety imituje skadni jzykw C i C++, wcznie z takimi usterkami jak nieuporzdkowany
system poprzedzania operatorw bitowych. Java troch agodzi negatywne skutki takiego
podejcia, poniewa wykazuje wicej cech jzyka czysto obiektowego ni np. C++.
Ominicie mechanizmw obiektowych jest w tym jzyku duo trudniejsze, jeli w ogle
moliwe. Zdeterminowany programista potrafi jednak stworzy fatalny kod w kadym
jzyku.

Jak zatem naley to robi prawidowo?


Poniewa obiektowe podejcie do projektowania i programowania systemw informatycznych jest niezbdne, ale te po prostu nieznane, przeanalizujmy jeszcze jeden przykad zego (i dobrego) sposobu budowania tego typu systemw z perspektywy projektowania obiektowego. Na potrzeby tego przykadu posu si terminalami ATM
(podobnie jak autorzy wielu innych ksiek) nie dlatego, e ktokolwiek implementuje
jeszcze systemy ATM, tylko dlatego, e ATM doskonale nadaje si do analizy zarwno
technik obiektowych, jak i architektur klient-serwer. Przyjmijmy, e mamy centralny
komputer banku (w roli obiektu serwera) oraz terminal ATM (w roli obiektu klienta).
Wikszo programistw proceduralnych baz danych bdzie traktowaa taki serwer jak
repozytorium z danymi, natomiast tego typu klient bdzie postrzegany wycznie jak
rdo da tych danych. Programici podzielajcy ten pogld prawdopodobnie rozwi problem transakcji ATM w nastpujcy sposb:
1. Uytkownik podchodzi do terminala, wkada kart i wpisuje swj kod PIN.
2. Terminal ATM formuuje zapytanie w postaci: podaj mi kod PIN przypisany

do tej karty, wysya to zapytanie do centralnej bazy danych i sprawdza, czy


zwrcona warto jest zgodna z danymi podanymi przez uytkownika. Terminal
ATM wysya kod PIN do serwera w formie acucha (bdcego czci zapytania
jzyka SQL), ale zwracana liczba jest skadowana w postaci 16-bitowej liczby
cakowitej, ktra uatwia operacj porwnania.
3. Uytkownik da operacji wypaty rodkw.

Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe

39

4. Terminal ATM formuuje kolejne zapytanie; tym razem ma ono posta: podaj

mi stan konta tego uytkownika. Po otrzymaniu odpowiedzi zwrcone saldo


jest umieszczane w odpowiednio przeskalowanej 32-bitowej liczbie cakowitej.
5. Jeli ilo rodkw na koncie jest wystarczajca, terminal wydaje gotwk

i wysya do serwera nastpujcy komunikat: zaktualizuj stan konta tego


uytkownika.
(Nawiasem mwic, rzeczywisty sposb funkcjonowania bankomatw jest inny.)
Jakie s wady takiego podejcia do problemu dziaania terminali ATM? Rozpocznijmy
nasz analiz od zwracanego stanu konta. Co bdzie, jeli Bill Gates uda si do banku,
zechce otworzy konto czekowe i wpaci tam wszystkie swoje pienidze? Nie chcesz
przecie odesa go z kwitkiem, ale masz wiadomo, e wedug ostatnich szacunkw
jego majtek to jakie 100 gigadolarw. Niestety, wykorzystywana do skadowania
stanu konta 32-bitowa liczba cakowita moe reprezentowa najwyej 20 megadolarw
(4 gigadolary podzielone najpierw przez 2 w zwizku z koniecznoci reprezentowania
znaku oraz dalej podzielone przez 100 w zwizku z potrzeb reprezentowania centw).
Podobnie, 16-bitowa liczba cakowita uywana do reprezentowania kodw PIN moe
zawiera co najwyej 4 cyfry dziesitne. Co bdzie, jeli Bill zayczy sobie kodu GATES
(piciocyfrowego)? Ostatnim problemem s formuowane przez terminal ATM zapytania do centralnej bazy danych. Jeli wykorzystywany sownik danych ulegnie zmianie
(jeli np. zmodyfikujemy pole nazwiska), wszystkie te zapytania jzyka SQL po prostu
przestan dziaa. (Mimo e przedstawiony przykad jest z oczywistych wzgldw nonsensowny, warto cho przez chwil pomyle o komplikacjach zwizanych z rezygnacj
z woskich lirw na rzecz wsplnej waluty europejskiej).
Proceduralne rozwizanie wszystkich tych problemw wie si z koniecznoci takiego
zmodyfikowania pamici ROM wszystkich terminali ATM na wiecie (przecie nigdy
nie wiadomo, z ktrego Bill zechce skorzysta), aby obsugiway 64-bitowe liczby zmiennoprzecinkowe podwjnej precyzji zamiast 32-bitowych liczb cakowitych (aby umoliwi
przechowywanie wyjtkowego stanu konta Billa) oraz 32-bitowych liczb cakowitych do
przechowywania 5-cyfrowych kodw PIN. Bdzie to oczywicie powany problem
dla organizacji, ktra odpowiada za konserwacj tych terminali.
Wrmy na chwil do problemw rzeczywistego wiata, w ktrym koszt wdraania oprogramowania jest jednym z najwaniejszych skadnikw budetw dziaw IT. W architekturach klient-serwer odpowiednikiem zmodyfikowania pamici ROM wszystkich
terminali ATM jest wdroenie nowej wersji aplikacji klienta, co w wikszoci przypadkw wymaga ogromnych nakadw. Podobne problemy konserwacyjne ujawniaj
si w wikszoci programw proceduralnych, nawet tych, ktre nie korzystaj z baz
danych. Zmiana definicji kilku centralnych typw danych lub zmiennych globalnych
(odpowiadajcych w tym przypadku centralnemu sownikowi danych) moe wymaga
ponownego napisania wszystkich funkcji i procedur programu. Wanie tego rodzaju
koszmarw konserwacji i rozwoju oprogramowania mona unikn dziki technikom
obiektowym.
Aby przekona si, jak podejcie obiektowe moe eliminowa tego typu problemy,
sprbujmy przeanalizowa moliwe rozwizanie wspomnianego ju przykadu terminali

40

Wzorce projektowe. Analiza kodu sposobem na ich poznanie

ATM z wykorzystaniem technik obiektowych, a wic traktujc cay system jak zbir
wsppracujcych obiektw, ktre oferuj okrelone zdolnoci (moliwo wykonywania
pewnych czynnoci). Pierwszym krokiem kadego procesu projektowania obiektowego
jest sformuowanie definicji problemu, czyli prezentacji problemu, ktry prbujemy
caociowo rozwiza w ramach czego, co nazywa si czsto dziedzin problemu.
W tym przypadku dziedzin problemu jest bankowo. Definicja problemu opisuje sam
problem, nie program komputerowy. Analizowany problem mona opisa w nastpujcy sposb:
Klient przychodzi do banku, bierze od kasjera w okienku blankiet potwierdzenia
wypaty i wypenia go, wpisujc numer rachunku i kwot wypaty. Klient wraca
do kasjera, potwierdza swoj tosamo i przekazuje wypeniony dokument.
(Kasjer weryfikuje tosamo klienta, sprawdzajc zapisy rejestru bankowego).
Nastpnie kasjer zwraca si o odpowiednie upowanienie do urzdnika bankowego
i wypaca pienidze klientowi.
Dysponujc nawet tak prost definicj problemu, moesz bez trudu zidentyfikowa kilka
potencjalnych kluczowych abstrakcji (klas) wraz z odpowiednimi operacjami (patrz
tabela 1.2). W tym celu posu si formatem karty CRC stworzonym przez Warda Cunninghama (ktry szczegowo omwi w dalszej czci tego rozdziau).
Tabela 1.2. Udziaowcy przypadkw uycia wymienieni w formacie karty CRC
Klasa

Odpowiedzialno

Obiekty wsppracujce

Rejestry bankowe

Tworzy potwierdzenia wypaty.


Sprawdza, czy klient jest tym,
za kogo si podaje

Kasjer: da blankietu potwierdzenia


wypaty

Urzdnik bankowy

Upowania kasjera do wypaty


gotwki

Kasjer: da upowanienia

Potwierdzenie wypaty

Rejestruje ilo gotwki danej


przez kasjera

Rejestry bankowe: tworz potwierdzenia


Urzdnik bankowy: autoryzuje wypat
Kasjer: przedstawia potwierdzenie
klientowi

Kasjer

Pobiera potwierdzenie wysokoci


depozytu z rejestru bankowego
i przekazuje je urzdnikowi
bankowemu do zatwierdzenia

Rejestry bankowe: tworz potwierdzenia


wysokoci depozytw
Urzdnik bankowy: autoryzuje transakcj

W przedstawionym modelu serwerem jest tak naprawd obiekt urzdnika bankowego,


ktrego zasadnicz rol jest autoryzowanie (zatwierdzanie) transakcji proponowanych
przez kasjera. Bank, ktry take powinien mie posta obiektu dziaajcego po stronie
serwera, na danie kasjera tworzy blankiety potwierdze wypaty. Strona klienta jest
reprezentowana przez obiekt kasjera, ktrego gwn rol jest pobieranie z obiektu
banku blankietw dokumentw i przekazywanie ich bezporednio klientowi. Co ciekawe,
klient (Bill) jest dla tego systemu elementem zewntrznym i dlatego nie zosta uwzgldniony w przedstawionym modelu. (Banki z pewnoci maj klientw, ale klient nie
jest ju atrybutem banku, tak jak nie s jego atrybutami usugi portierw. Atrybutami
banku z pewnoci mogyby by rachunki klientw, ale nie sami klienci. Przykadowo,

Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe

41

zapewne nie definiujesz si jako cz banku, ktrego jeste klientem). Obiektowy system terminali ATM modeluje tylko przedstawion wczeniej definicj problemu. Oto
przewidywany przepyw komunikatw:
1. Bill podchodzi do terminala ATM, wkada swoj kart, podaje kod PIN i wydaje

dyspozycj wypaty gotwki.


2. Obiekt Kasjer wysya do dziaajcego po stronie serwera obiektu RejestryBankowe

nastpujce zapytanie: Czy ta karta i podany kod PIN s prawidowe?


3. Obiekt RejestryBankowe zwraca odpowied tak lub nie.
4. Obiekt Kasjer prosi obiekt RejestryBankowe o pusty obiekt PotwierdzenieWypaty.

dany obiekt bdzie egzemplarzem pewnej klasy implementujcej interfejs


PotwierdzenieWypaty i zostanie przekazany przez obiekt RejestryBankowe
obiektowi Kasjer przez warto, za porednictwem mechanizmu zdalnego

wywoywania metod (ang. Remote Method Invocation RMI). To bardzo


wane. Wiedza obiektu Kasjer o obiekcie PotwierdzenieWypaty sprowadza si
do znajomoci implementowanego przez ten obiekt interfejsu implementacja
(plik .class) dociera do obiektu Kasjer wraz z samym obiektem, zatem Kasjer
nie ma moliwoci okrelenia, jak otrzymany obiekt bdzie przetwarza
przekazywane mu komunikaty. Taka abstrakcja jest o tyle korzystna,
e ewentualne zmiany funkcjonowania obiektu PotwierdzenieWypaty nie bd
wymuszay zmian w definicji obiektu Kasjer.
5. Obiekt Kasjer da od obiektu PotwierdzenieWypaty wywietlenia interfejsu
uytkownika. (Obiekt PotwierdzenieWypaty realizuje to zadanie, generujc

interfejs na ekranie terminala ATM na podstawie podsystemu graficznego AWT).


6. Bill wypenia potwierdzenie wypaty.
7. Obiekt Kasjer rejestruje zakoczenie operacji uwierzytelniania klienta (by moe

metod monitorowania przycisku Akceptuj na klawiaturze terminala) i przekazuje


wypeniony obiekt PotwierdzenieWypaty dziaajcemu po stronie serwera
obiektowi UrzdnikBankowy (take przez warto, za porednictwem mechanizmu
RMI) w formie argumentu przesyanego komunikatu: Czy mam upowanienie
do wypaty tak duej iloci gotwki?
8. Obiekt UrzdnikBankowy zwraca odpowied tak lub nie.
9. Jeli terminal ATM otrzyma odpowied tak, gotwka jest natychmiast wypacana

klientowi. (Dla uproszczenia nie bd ju analizowa tego procesu).


Nie jest to oczywicie jedyne (ani nawet najlepsze) rozwizanie problemu, ale uwierz
mi jest pod wieloma wzgldami lepsze od wczeniejszego rozwizania proceduralnego.
Wan cech przedstawionego protokou, na ktr warto zwrci uwag, jest to, e caa
wiedza na temat sposobu skadowania stanu rachunku i kodu PIN, a wic midzy innymi
na temat procedur podejmowania decyzji o moliwoci wypacenia pienidzy, jest ukryta
wewntrz rnych obiektw. Takie rozwizanie jest moliwe, poniewa serwer jest teraz
obiektem, ktry implementuje funkcj autoryzacji. Zamiast da danych niezbdnych
do autoryzowania transakcji, obiekt Kasjer zleca wykonanie tego zadania (dziaajcemu

42

Wzorce projektowe. Analiza kodu sposobem na ich poznanie

po stronie serwera) obiektowi UrzdnikBankowy, ktry dysponuje wszystkimi potrzebnymi


informacjami. adne dane (ani stan rachunku, ani kod PIN) nie s odsyane do terminala ATM, zatem ewentualne zmiany kodu serwera nie bd si wizay z koniecznoci modyfikowania kodu tego terminala.
Warto te zwrci uwag na fakt, i obiekt Kasjer nie wie nawet, jak w caym systemie reprezentowane s pienidze. Oznacza to, e dana kwota jest w caoci hermetyzowana w ramach obiektu PotwierdzenieWypaty. W efekcie, ewentualne zmiany mechanizmu reprezentowania kwot pieninych po stronie serwera s cakowicie transparentne
z perspektywy dziaajcego po stronie klienta obiektu Kasjer. Osoba odpowiedzialna za
utrzymywanie tego systemu bdzie moga spokojnie drzema w swoim biurze zamiast
jedzi po caym wiecie i modyfikowa pamici ROM wszystkich terminali ATM.
Gdyby europejskie terminale byy implementowane w ten sposb, przejcie na wspln
walut euro byoby banalnie proste wymagaoby tylko zmiany definicji klasy
PotwierdzenieWypaty (lub klasy Pienidze) po stronie serwera. Wszelkie dalsze dania
dotyczce obiektw PotwierdzenieWypaty generowane przez terminale ATM powinny
skutkowa zwracaniem odpowiedzi dostosowanych do nowej sytuacji.

Automat komrkowy
Rozszerzmy teraz nasze wyobraenie koncepcji obiektowej w taki sposb, aby obejmowao pojcie interfejsu posu si kolejnym przykadem, ktry wybrukuje drog do
zrozumienia programu Gra w ycie (ang. The Game of Life), z ktrego bdziemy korzysta w dalszej czci tej ksiki.
Dobrym przykadem naturalnego systemu obiektowego jest klasa programw nazywana
automatem komrkowym. Tego typu programy rozwizuj skomplikowane problemy
w sposb bardzo obiektowy ogromny problem jest rozwizywany przez zbir maych,
identycznych obiektw (komrek), z ktrych kady implementuje prosty zbir regu
i komunikuje si ze swoimi bezporednimi ssiadami. Poszczeglne komrki nie maj
co prawda adnej wiedzy o wikszym problemie, ale komunikuj si z innymi komrkami w taki sposb, e z ich perspektywy problem ten rozwizuje si sam.
Typowym przykadem automatu komrkowego jest modelowanie ruchu ulicznego (ktrego omwienie znacznie wykraczaoby poza zakres tematyczny tej ksiki). Problem
przewidywania ruchu ulicznego jest wyjtkowo trudny jest klasycznym problemem
teorii chaosu. Tak czy inaczej, warto pamita, e mona modelowa ruch pojazdw
w taki sposb, by obserwacja jego symulacji umoliwiaa przewidywanie pewnych zdarze
wycznie na podstawie zachowania badanego modelu. Przewidywanie i symulowanie
ruchu ulicznego to oczywicie dwa rne problemy automaty komrkowe sprawdzaj
si doskonale wanie w symulowaniu z pozoru chaotycznych procesw.
Powic kilka kolejnych stron na omawianie problemu ruchu ulicznego nie tylko dlatego, e problem ten tak dobrze demonstruje mechanizm automatw komrkowych,
ale take dlatego, e przykad ten doskonale ilustruje wiele podstawowych zagadnie

Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe

43

zwizanych z projektowaniem obiektowym, co do ktrych chciabym mie pewno,


e s zrozumiae, zanim jeszcze przystpi do analizy takich systemw obiektowych
jak Gra w ycie.
Wikszo programw dziaa przez implementowanie pewnych algorytmw pojedynczych (cho w wielu przypadkach skomplikowanych) wzorw, ktre pracuj w dobrze
udokumentowany, przewidywalny sposb pod warunkiem, e otrzymaj znany zbir
danych wejciowych. Jakiekolwiek rozwizanie prbujce modelowa ruch uliczny na
poziomie caego miasta, opierajc si na pojedynczym (skomplikowanym) algorytmie jest
po prostu zbyt trudne do zaimplementowania. Podobnie jak w przypadku wikszoci
problemw teorii chaosu, najzwyczajniej w wiecie nie wiadomo, jak naley pisa algorytmy rozwizujce problem ruchu ulicznego.
Automat komrkowy radzi sobie z tym problemem, omijajc go. W tego typu rozwizaniach nie stosuje si algorytmw jako takich, a jedynie pewne mechanizmy modelowania zachowa tych czci systemu, ktre mona stosunkowo atwo ledzi. Przykadowo, zamiast modelowa ruch uliczny w caym miecie, automat komrkowy rozbija
pen sie ulic na mniejsze fragmenty i skupia si na ich modelowaniu. Kady odcinek
drogi moe si komunikowa z ssiadujcymi odcinkami, ale aden z tych odcinkw nie
ma wiedzy na temat caej sieci ulic w danym miecie.
Modelowanie zachowa na niewielkim wycinku ulicy jest stosunkowo atwe. Kady taki
fragment ma okrelon pojemno (wyraajc si zwykle liczb pasw ruchu) i inne,
rwnie proste atrybuty. Fragment drogi ma oczywicie swoj dugo oraz prdko maksymaln zalen od pojemnoci i stosowanych na tym odcinku ogranicze. To wszystko.
Samochody pojawiaj si na jednym kocu odcinka i po jakim czasie opuszczaj go
na drugim kocu. Do wykoczenia systemu bdziemy jeszcze potrzebowali dwch dodatkowych obiektw (reprezentujcych samochd i map), ktre take charakteryzuj
si atwym do modelowania zachowaniem. (Oba dodatkowe obiekty omwi dokadniej za chwil).
Rozmaite obiekty w ramach tego systemu musz si wzajemnie komunikowa za porednictwem precyzyjnie zdefiniowanych interfejsw. (Na rysunku 1.2 przedstawiono cay
proces konwersacji, ktry za chwil omwi).
Interfejs Road skada si z dwch metod:
1. Czy moesz przyj N samochodw?
boolean canYouAcceptCars(int n, Road fromThisRoad)

2. Przeka mi N samochodw.
car[] giveMeCars(int n)

Ssiadujce odcinki drogi komunikuj si w bardzo prosty sposb. Kiedy biecy odcinek decyduje, e musi si pozby kilku samochodw, pyta odcinek ssiadujcy, czy ten
moe je przyj (pierwszy komunikat). Zapytany odcinek moe te samochody przyj
prosi o ich przekazanie ssiedni odcinek, ktry zainicjowa konwersacj (drugi komunikat).

44

Wzorce projektowe. Analiza kodu sposobem na ich poznanie

Rysunek 1.2. Diagram sekwencji UML dla modelu ruchu ulicznego

Z podobn komunikacj dwustronn bdziemy mieli do czynienia w rozdziale 3. Pocztkowe danie musi zawiera referencj do generujcego je obiektu Road, ktr docelowy obiekt Road bdzie mg wykorzysta podczas dania kolejnych samochodw;
w przeciwnym razie obiekt docelowy nie wiedziaby, ktry odcinek rdowy nadesa
dane danie. Odcinek (fragment drogi) w rodku takiego bloku komunikuje si z dwoma ssiadami (z dwoma przylegajcymi odcinkami drogi, obiektami Road), obiekt reprezentujcy skrzyowanie ma czterech bezporednich ssiadw itd. (Odpowiednie poczenia s ustawiane w momencie tworzenia sieci ulic i mog by implementowane
w formie argumentw konstruktora).
Odcinek drogi (obiekt Road) musi wewntrznie wykorzystywa kilka regu, na bazie
ktrych bdzie decydowa, kiedy moe przyj kolejne samochody. Przykadowo, rednia
efektywna prdko samochodu (obiektu Car), czyli rnica pomidzy momentem wjazdu
samochodu na reprezentowany odcinek a momentem opuszczenia tego odcinka, moe
by funkcj natenia ruchu liczby samochodw przebywajcych na danym odcinku.
Rne typy drg (autostrady, uliczki itp.) mog implementowa te reguy w zrnicowany
sposb. Reguy te s jednak znane tylko obiektom Road. Podobnie jak w przypadku
wszystkich innych systemw obiektowych, tego typu reguy mog by zasadniczo zmieniane bez wpywu na otaczajcy kod, poniewa modyfikacje regu nie bd wpyway
na ksztat interfejsu odcinka drogi (obiektu Road).

Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe

45

Nastpnym potrzebnym elementem jest obiekt reprezentujcy samochd (Car). Kady


obiekt Road peni funkcj stranika swoich obiektw Car. Poniewa ograniczenie prdkoci i dugo odcinka drogi s atrybutami obiektu Road, obiekt ten moe atwo okreli,
jak dugo bdzie goci poszczeglne samochody bez koniecznoci komunikowania si
z odpowiednimi obiektami Car.
Pewnym utrudnieniem s skrzyowania. Obiekt Road musi przecie wiedzie, do ktrego ze swoich ssiadw skierowa dany obiekt Car. Rozwizaniem tego problemu jest
drugi, rwnie prosty interfejs (implementowany przez obiekt Car i wykorzystywany
przez obiekt Road).
1. Jeste tutaj; w ktr stron chcesz teraz jecha?
Direction whichWayWouldYouLikeToTurn(Location here)

Take w tym przypadku obiektu Road nie interesuje sposb generowania odpowiedzi
przez obiekt Car na tak postawione pytanie wane jest tylko uzyskanie odpowiedniej
odpowiedzi. W fazie diagnozowania kodu mona wymusi na metodzie obsugujcej ten
komunikat wywietlanie zapytania na konsoli i wykonywanie wpisywanych polece.
Rzeczywisty system bdzie oczywicie wymaga zautomatyzowanego rozwizania, ale
zmiana rcznego funkcjonowania klasy Car na dziaanie automatyczne bdzie wymagaa
zmodyfikowania tylko tej jednej klasy nie bdzie to miao wpywu na reszt systemu.
Warto zwrci uwag na fakt, e obiekt Car nie musi dokadnie wiedzie, gdzie si
znajduje (przypomina to troch rzeczywist sytuacj, ktr modelujemy). Inaczej jest
w przypadku obiektu Road, ktry zna swoje pooenie (obiekt Location), zatem to wanie
obiekt Road przekazuje obiekt Location obiektowi Car. Poniewa obiekt Location stale
si zmienia, obiekt Car nawet nie prbuje tego obiektu wewntrznie utrwala. Obiekt Car
musi zawiera tylko jeden atrybut reprezentujcy cel podry.
Obiekt Car musi dysponowa mechanizmem, ktry umoliwi mu odpowiadanie na pytanie o kierunek jazdy, powinnimy wic stworzy jeszcze jeden obiekt: Map. Obiekt
ten bdzie implementowa jeszcze jeden interfejs przesyania komunikatw.
2. Jestem tutaj i chc jecha tam; w ktrym kierunku powinienem si uda?
Direction whichWayShouldITurn(Location here, Location there)

Obiekt Car, ponownie, nie ma wiedzy na temat techniki udzielania odpowiedzi na zadane
pytanie przez obiekt mapy wystarcza mu samo uzyskiwanie potrzebnych informacji.
(Problem wyboru trasy jest najtrudniejsz czci tego systemu, ale zosta ju rozwizany
przez wszystkie dostpne na rynku urzdzenia nawigacyjne oparte na systemie GPS.
Oznacza to, e odpowiednie rozwizanie mona po prostu kupi). Warto zwrci uwag
na sposb przekazywania pooenia (na podstawie obiektu Map) samochodu do reprezentujcego go obiektu Car. Proces ten, nazywany delegacj, jest typowym rozwizaniem w systemach obiektowych. Obiekt rozwizuje pewien problem, delegujc go do
zawieranego przez siebie obiektu i przekazujc tam wszelkie niezbdne informacje
z zewntrz. Komunikaty przekazywane przez obiekt delegujcy do obiektu delegowanego s zwykle opatrywane dodatkowymi argumentami.

46

Wzorce projektowe. Analiza kodu sposobem na ich poznanie

Ostatnim elementem tej ukadanki jest okrelenie sposobu, w jaki samochody trafiaj na
drog (obiekt Road). Z punktu widzenia systemu modelujcego ruch uliczny, prawdziwym kocem drogi jest dom kierowcy (nazywany wjazdem). Podobnie, miejsce pracy
kierowcy take moe stanowi obiekt Road (nazwany parkingiem). Obiekty domu i miejsca pracy musz nie tylko implementowa interfejs Road, ale take zna odcinki drogi,
z ktrymi te obiekty s poczone. Przydomowy gara i parking w miejscu pracy powinny
te wiedzie, jak wypuszcza samochody do systemu i akceptowa ich przyjmowanie
o okrelonych porach dnia implementacja tego elementu bdzie prosta, poniewa
bdzie wymagaa jedynie uycia odpowiedniego interfejsu obiektw Road.
Dodajmy teraz interfejs uytkownika. Klasyczne wymaganie odnonie do systemw
obiektowych mwi, e obiekt nie powinien ujawnia szczegw swojej implementacji.
Naszym celem jest maksymalizacja moliwoci konserwacji tworzonego oprogramowania. Jeli wszystkie informacje na temat implementacji s zazdronie strzeonym sekretem obiektu, ewentualne zmiany tej implementacji nie bd miay wpywu na funkcjonowanie kodu wykorzystujcego dany obiekt. Oznacza to, e modyfikacja implementacji
pojedynczego obiektu nie wywraca reszty systemu. Poniewa wszystkie zmiany koncentruj si zwykle wok definicji pojedynczej klasy, konserwacja systemw obiektowych jest wyjtkowo atwa, ale tylko wtedy, gdy zachowana jest zasada hermetycznoci. (W niektrych sytuacjach mog istnie istotne przesanki do rezygnacji z tej reguy,
jednak decyzja w tym zakresie powinna by podejmowana z uwzgldnieniem przyszych
utrudnie w konserwacji systemu).
Wymaganie hermetycznoci oznacza, e dobrze zaprojektowany obiekt bdzie przynajmniej w jakim stopniu odpowiada za tworzenie wasnego wycinka interfejsu uytkownika. Oznacza to, e dobrze zaimplementowana klasa nie bdzie udostpniaa metod
zwracajcych i ustawiajcych jej pola, poniewa takie metody stanowiyby furtk do
szczegw implementacji, a wic w konsekwencji prowadziy do powanych problemw w zakresie konserwacji systemu zbudowanego na ich bazie. Jeli implementacja
obiektu zostanie zmodyfikowana w taki sposb, e niezbdna bdzie zmiana np. typu
lub zakresu wartoci zwracanych przez tego typu metod, bdziesz musia przebudowa nie tylko obiekt definiujcy t metod, ale take cay kod, ktry z niej porednio
lub bezporednio korzysta. Wicej uwagi temu zagadnieniu oraz technikom projektowania systemw bez metod zwracajcych i ustawiajcych powic za chwil.
W biecym systemie moemy zbudowa interfejs uytkownika, dodajc pojedyncz
metod do interfejsu Road.
3. Narysuj swoj reprezentacj wzdu tej linii:
drawYourself(Graphics g, Point begin, Point end);

Interfejs uytkownika obiektw Road wywietla redni prdko samochodw na reprezentowanym odcinku drogi (ktry moe si rni w zalenoci od natenia ruchu)
za pomoc zmieniajcych si kolorw rysowanych linii. W wyniku zastosowania tego
mechanizmu powinnimy otrzyma plan miasta, na ktrym kolory ulic bd wskazyway
na przewidywan prdko pokonywania poszczeglnych odcinkw. Obiekt Map musi
oczywicie wiedzie o wszystkich obiektach Road, aby waciwie kierowa procesem
wizualizacji planu i w razie koniecznoci delegowa dania rysowania do poszczeglnych obiektw Road. Poniewa obiekty te same odpowiadaj za swoj wizualizacj, nie

Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe

47

ma potrzeby tworzenia mnstwa metod zwracajcych i proszenia ich o informacje niezbdne dla zewntrznych generatorw interfejsu uytkownika przykadowo, taka
metoda jak getAverageSpeed() bdzie zupenie zbdna.
Teraz, kiedy ju wykonalimy podstawowe zadania, moemy wprawi nasz system
w ruch. Musimy wiza ze sob wszystkie drogi, wjazdy i parkingi za kadym razem,
gdy kompilujemy nasz system. Musimy te umieci w tym systemie kilka pojazdw
(take w czasie kompilacji) i rozpocz obserwacj przygotowanego modelu. Z kadym
tykniciem zegara wszystkie odcinki drg decyduj o liczbie samochodw, ktrych
musz si pozby, i przekazuj je do ssiednich odcinkw. Kady odcinek (obiekt Road)
automatycznie aktualizuje swj wycinek interfejsu uytkownika, aby odzwierciedla
obserwowane zmiany redniej prdkoci. Voil! Mamy model ruchu ulicznego.
Kiedy ju zaprojektujesz system przesyania komunikatw, staniesz przed koniecznoci
wycignicia waciwych wnioskw z diagramu modelu statycznego. Ewentualne powizania wystpuj wycznie pomidzy klasami, ktrych obiekty wzajemnie si komunikuj definiowane s tylko naprawd potrzebne komunikaty. Na rysunku 1.3 przedstawiono odpowiedni diagram UML. Warto pamita, e rozpoczcie projektowania od
tworzenia i analizy takiego diagramu byoby strat czasu. Zanim zrozumiesz relacje pomidzy klasami, musisz przecie wiedzie, jaki powinien by przepyw komunikatw.
Rysunek 1.3.
Diagram modelu
statycznego UML
dla systemu
modelujcego
ruch uliczny

Jeli chcesz zdoby dowiadczenie w zakresie eksperymentowania z tego typu symulatorami ruchu ulicznego, powiniene si przyjrze popularnej grze SimCity firmy Maxis
Software. Poniewa nie widziaem kodu rdowego tego programu, tak naprawd nie
wiem, czy SimCity rzeczywicie zaimplementowano w formie automatu komrkowego,
bybym jednak zdumiony, gdyby okazao si, e jest inaczej. Pewne jest, e na poziomie
interfejsu uytkownika gra SimCity dziaa podobnie jak nasz system modelowania ruchu
ulicznego. Maxis oferuje darmow wersj tego produktu (zatytuowan SimCity Classic)
na swojej witrynie internetowej (patrz http://www.maxis.com).

48

Wzorce projektowe. Analiza kodu sposobem na ich poznanie

Metody zwracajce i ustawiajce s ze


Jak ju wspomniaem, fundamentaln regu rzdzc systemami obiektowymi jest ukrywanie przez obiekty ich szczegw implementacyjnych. Stosowanie tej zasady umoliwia modyfikowanie implementacji obiektw bez koniecznoci wprowadzania zmian
w kodzie, ktry te obiekty wykorzystuje. Oznacza to, e powiniene unika stosowania
funkcji zwracajcych i ustawiajcych, ktre z jednej strony niczego nie uatwiaj, a jednoczenie zapewniaj dostp do szczegw implementacyjnych (pl) w systemach obiektowych. Warto zwrci uwag na fakt, i ani przykad systemu terminali ATM, ani
przykad modelu ruchu ulicznego nie wykorzystywa metod zwracajcych i ustawiajcych pola obiektw.
Nie twierdz przy tym, e Twoje funkcje nigdy nie powinny zwraca wartoci, lub e
funkcji get i set nigdy nie naley stosowa. Obiekty czasami po prostu musz przekazywa swoje dane, aby cay system mg funkcjonowa prawidowo. Tak czy inaczej,
funkcje get i set czsto s wykorzystywane w sposb niewaciwy, a wic w roli
rodkw zapewniajcych dostp do pl, ktre w przeciwnym razie byyby prywatne,
zatem ich uywanie moe prowadzi do powanych utrudnie. To, co rozumiem przez
waciwe zastosowania tych metod omwi na kocu tego podrozdziau. Obecno metod zwracajcych i ustawiajcych (nazywanych czsto akcesorami i mutatorami, cho
zdarza si, e sowo akcesor jest uywane w odniesieniu do obu tych metod) zwykle oznacza brak jasnoci oraz niewaciwe podejcie do rozwizywanego problemu. Programici
czsto umieszczaj te metody w definicjach klas, poniewa najzwyczajniej w wiecie
chc unikn mylenia o sposobie komunikowania si obiektw klas w czasie wykonywania systemu. Uycie metod zwracajcych pozwala odwlec moment analizy technik
komunikacji do czasu waciwego kodowania. Takie podejcie jest przejawem czystego
lenistwa; z pewnoci nie jest to oznaka programowania dla elastycznoci.
Przeanalizujmy banalny przykad, ktry dobrze pokazuje, dlaczego naley unika metod
zwracajcych. W Twoim programie moe wystpowa tysic wywoa metody getX(),
z ktrych kade zakada, e warto zwracana przez t metod naley do okrelonego typu.
Warto zwracana przez metod getX() moe by skadowana np. w zmiennej lokalnej,
zatem typ tej zmiennej musi odpowiada typowi zwracanej wartoci. Jeli bdziesz
musia zmieni implementacj obiektu w taki sposb, e zmieni si typ zmiennej X,
popadniesz w powane tarapaty. Jeli dotychczasowym typem zmiennej X by int,
a nowym typem jest long, po wprowadzeniu odpowiedniej zmiany otrzymasz tysic
bdw kompilacji. Jeli rozwiesz ten problem w sposb niewaciwy, a wic zastosujesz operacj rzutowania typu zwracanej wartoci na typ int, kod bdzie co prawda
kompilowany, ale z pewnoci nie bdzie dziaa prawidowo (zwracana warto bdzie
obcinana). Aby unikn negatywnych skutkw tej zmiany, bdziesz musia zmodyfikowa kod otaczajcy kade z tysica wywoa metody getX(). Zdecydowanie nie chciabym si znale w podobnej sytuacji.
Przyjrzyjmy si teraz klasie Money. Poniewa klasa ta bya pocztkowo pisana wycznie
z myl o obsudze dolarw amerykaskich, zdefiniowano w niej metod getValue(),
ktra zwraca liczb zmiennoprzecinkow typu double, oraz setValue(), ktra ustawia
now warto. Pierwszy problem polega na tym, e takie rozwizanie umoliwia zupenie nonsensowne dziaania na reprezentowanych kwotach pieninych, co ilustruje
poniszy fragment kodu:

Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe

49

Money a, b, c;
//
a.setValue( b.getValue() * c.getValue() );

Co waciwie oznacza pomnoenie dwch dolarw przez pi dolarw?


Drugi problem jest jeszcze powaniejszy: moe zaistnie konieczno wprowadzenia do
aplikacji rozwiza midzynarodowych, a wic umoliwiajcych midzy innymi obsug
wielu rnych walut. Taka zmiana bdzie wymagaa dodania pola nazwanego currency,
ktre bdzie miao wewntrznie przypisywane takie wartoci jak US_DOLLAR, YEN, LEU,
ZLOTY czy HRYVNA. Stosunkowo drobna zmiana wielkie problemy. Co w tej sytuacji
bdzie zwracaa metoda getValue()? Metoda ta nie moe po prostu zwraca wartoci typu
double, poniewa sama warto niewiele Ci mwi. Musisz przecie wiedzie, z jak
walut masz do czynienia. Nie jest te moliwe normalizowanie zwracanej wartoci,
np. do dolarw, poniewa przeliczniki stosowane na rynku walutowym zmieniaj si co
minut. Warto si te zastanowi, co naleaoby zrobi z otrzyman wartoci. Nie mona
ich przecie tak po prostu wywietli, poniewa wymagaoby to opatrzenia ich symbolem odpowiedniej waluty. Moesz oczywicie do istniejcej metody getValue() doczy now metod getCurrency(), ale od tej chwili cay kod wykorzystujcy t warto bdzie musia dodatkowo pobiera informacj o walucie i lokalnie normalizowa
uzyskiwane wartoci do standardowej waluty. Powielenie tego rozwizania w tysicu
miejsc istniejcego kodu rdowego wymagaoby mnstwa pracy. Musiaby te znale wszystkie okna swojego systemu, w ktrych wywietlane s kwoty pienine
logika odpowiednich elementw graficznych musiaaby zosta przebudowana do postaci
obsugujcej rne waluty. Ta prosta zmiana byskawicznie staje si rdem ogromnych komplikacji.
Oto kolejny przykad: przeanalizuj wszystkie problemy zwizane z polami System.in,
System.out oraz System.err po wprowadzeniu do Javy klas Reader i Writer. Wszystkie
trzy pola byy publiczne, co samo w sobie byo przeklestwem tego rozwizania. Samo
zastosowanie odpowiednich otoczek (np. metody System.getOut(), ktra zwracaa pole
System.out) oczywicie nie rozwizywao problemu pola System.out i System.err
musiay by (opartymi na formacie Unicode) obiektami klasy Writer, nie (opartymi
na formatowaniu bajtowym) obiektami klasy PrintStream. To samo dotyczyo pola
System.in i klasy Reader. Zmiana zadeklarowanych typw obiektw zawierajcych pola
System.out nie jest rozwizaniem wystarczajcym. Obiekty klasy Writer s wykorzystywane w nieco inny sposb ni strumienie wyjciowe. Oba mechanizmy rni si
przecie semantyk i udostpniaj rne metody. W efekcie musisz wic zmieni (lub
przynajmniej sprawdzi) cay kod otaczajcy, wykorzystujcy pole System.out do wywietlania na konsoli danych wyjciowych. Jeli Twj program np. stosowa formatowanie Unicode dla danych tekstowych wywietlanych za porednictwem pola System.out, bdziesz musia uy a dwch wywoa metody write() do zapisania na konsoli pojedynczego znaku. Co wicej, bdziesz zmuszony do wprowadzenia do swojego
programu dodatkowego kodu wycigajcego bardziej i mniej znaczce bajty znakw
i wywietlajcego je osobno. Cay ten kod bdzie jednak trzeba usun w wersji opartej
na klasie Writer.
Opisany problem jest efektem siy przyzwyczajenia. Kiedy programici proceduralni zaczynaj korzysta z Javy, prbuj budowa kod, ktry bdzie cho troch przypomina ich dotychczasowe dokonania. Jzyki proceduralne nie oferuj moliwoci

50

Wzorce projektowe. Analiza kodu sposobem na ich poznanie

wykorzystywania klas, ale zawieraj zwykle rozwizania podobne do struktur jzyka C


(czyli w praktyce klasy bez metod i z samymi polami publicznymi). Dla takich programistw naladowanie struktur jzyka C jest zupenie naturalne prbuj budowa definicje klas pozbawionych metod i udostpniajcych wycznie publiczne pola.
Kiedy taki programista proceduralny usyszy gdzie, e naley stosowa pola prywatne,
odpowiednio zmienia ich deklaracje i tworzy publiczne metody get i set. Takie rozwizanie nie jest jednak niczym wicej ni niepotrzebnym komplikowaniem publicznego
dostpu do danych. Programici stosujcy tego typu dziaania z pewnoci nie tworz
systemw obiektowych.
Programici proceduralni bd argumentowali, e publiczne akcesory otaczajce pola
prywatne s o tyle lepsze od dostpnych bezporednio pl publicznych, e daj znacznie wiksz kontrol nad operacjami modyfikowania wartoci pl. Programista obiektowy stwierdzi natomiast, e kady dostp kontrolowany czy nie jest rdem
potencjalnych problemw konserwacyjnych. Dostp kontrolowany moe by co prawda
lepszy od dostpu swobodnego, ale nie zmienia to faktu, e takie rozwizanie jest z wielu
wzgldw niewaciwe. Argument o wyszoci akcesorw nad bezporednim dostpem
w ogle nie uwzgldnia najwaniejszej saboci obu rozwiza zdecydowana wikszo klas i tak nie potrzebuje metod akcesorw (ani mutatorw). Oznacza to, e dobrze
zaprojektowany system przesyania komunikatw (o sposobach jego projektowania za
chwil) w wikszoci przypadkw pozwala cakowicie wyeliminowa metody get i set
oraz w konsekwencji tworzy klasy, ktrych konserwacja bdzie duo atwiejsza.
Nie twierdz, e zwracanie wartoci jest ze, lub e powiniene wyeliminowa ze swojego
programu wszystkie metody get to po prostu niemoliwe. Minimalizowanie udziau
tego typu funkcji w definicjach klas spowoduje jednak znaczne uatwienia w konserwacji kodu.
Z czysto praktycznej perspektywy, czste stosowanie metod get i set sprawia, e kod
jest nie tylko bardziej skomplikowany, ale take mniej elastyczny. Przeanalizuj typow
wszechmogc klas proceduraln, ktra z innych obiektw zbiera informacje niezbdne do wykonania jakiego zadania. Implementacja tej klasy pena jest wywoa
zewntrznych metod get. Co jednak powiniene zrobi, kiedy okae si, e odpowiednie zadania s ju wykonywane przez jeden z obiektw udostpniajcych swoje dane t
drog? Czy mona przenie kod wykonujcy waciwe zadania z jednej, wszechmogcej klasy w miejsca, w ktrych skadowane s niezbdne dane? Wywoania akcesorw przestaj by potrzebne, a cay kod jest duo prostszy.
Stosowanie metod get i set powoduje te, e program staje si nieelastyczny (nie mona
w jego ramach atwo implementowa nowych wymaga biznesowych) i wyjtkowo
trudny w konserwacji. Prawdopodobnie najwaniejsz zasad systemw obiektowych
jest abstrakcja danych, a wic cise ukrywanie implementacji mechanizmw obsugi
komunikatw przed innymi obiektami. To tylko jeden z powodw, dla ktrych wszystkie
Twoje zmienne klasowe (pola klasy niebdce staymi) powinny by prywatne (deklarowane ze sowem kluczowym private). Jeli stworzysz publiczn zmienn klasow, nie
bdziesz mg zmienia tego pola wraz z ewolucj caej klasy, poniewa w ten sposb
uniemoliwiby prawidowe funkcjonowanie kodu zewntrznego, ktry z tego pola
korzysta. Z pewnoci nie masz ochoty na przeszukiwanie tysica zastosowa jakiej
klasy tylko dlatego, e wprowadzie drobn zmian w jej definicji.

Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe

51

Bezmylne stosowanie metod zwracajcych i ustawiajcych jest niebezpieczne z tych


samych wzgldw, ktre decyduj o zagroeniach zwizanych z uywaniem pl publicznych take metody get i set zapewniaj zewntrzny dostp do szczegw implementacyjnych. Co bdzie, jeli zostaniesz zmuszony do zmiany typu udostpnianego
w ten sposb pola? Przecie bdziesz wwczas musia zmieni take typ zwracany przez
metod akcesora. Jeli warto ta jest uywana w wielu miejscach, bdziesz musia
zmieni odpowiednie fragmenty w caym kodzie. Ja wolabym jednak ograniczy zakres
koniecznych zmian do definicji pojedynczej klasy. Nie chc, by prosta modyfikacja przenosia si na cay program.
Na podstawie reguy ukrywania szczegw implementacji mona stworzy wiarygodny
test systemw obiektowych. Czy moesz dokonywa istotnych zmian w definicji pojedynczej klasy (wcznie z wyrzuceniem caych fragmentw kodu i wstawieniem w ich
miejsce zupenie nowej implementacji) bez koniecznoci modyfikowania kodu wykorzystujcego obiekty tej klasy? Tak gboka modularyzacja oprogramowania bardzo
uatwia konserwacj oprogramowania i peni zasadnicz funkcj w ocenie jego obiektowoci. Nieprzestrzeganie zasady ukrywania szczegw implementacji bardzo ogranicza
moliwo stosowania pozostaych mechanizmw obiektowych.
Poniewa metody akcesorw naruszaj regu hermetyzacji, mona bez trudu wykaza,
e systemy, w ktrych czsto lub niewaciwie stosuje si tego typu rozwizania po
prostu nie s systemami obiektowymi. Co wicej, jeli skrupulatnie przeprowadzisz
proces projektowania (w przeciwiestwie do kodowania ad hoc), szybko stwierdzisz,
e Twj program nie musi zawiera niemal adnych metod akcesorw. Proces ten jest
wic bardzo wany.
Zapewne zauwaye, e w przedstawionym przykadzie modelowania ruchu ulicznego
w ogle nie stosowano metod zwracajcych i ustawiajcych. Obiekty klasy Car nie udostpniaj metody getSpeed(), take obiekty klasy Road nie definiuj metody getAverageSpeed(). Nie potrzebujemy metod getLocation() czy setLocation() w obiektach
klasy Car, poniewa skadujemy informacje o lokalizacji pojazdw w obiektach klasy
Road reprezentujcych odcinki drg, na ktrych te pojazdy aktualnie przebywaj. Nie
potrzebujemy te metody setAverageSpeed() w obiektach klasy Road, poniewa obiekty
te same obliczaj redni prdko pojazdw. Brak metod zwracajcych i ustawiajcych
nie oznacza, e jakie potrzebne dane nie mog by przekazywane pomidzy poszczeglnymi moduami tego systemu przykadowo, obiekt Road przekazuje informacje
o lokalizacji do obiektw klasy Car. Tak czy inaczej, podczas projektowania systemw
obiektowych naley moliwie gboko minimalizowa przepywy danych. Doskonaym papierkiem lakmusowym wskazujcym na suszno zastosowanych mechanizmw jest nastpujca regua: Nie pro obiektu o informacje, ktrych potrzebujesz
do wykonania jakiej czynnoci; zamiast tego pro obiekt zawierajcy te informacje o wykonanie tych czynnoci za Ciebie.
Przykadowo, nie powiniene stosowa przedstawionego wczeniej wyraenia:
Money a, b, c;
//
a.setValue( b.getValue() * c.getValue() );

52

Wzorce projektowe. Analiza kodu sposobem na ich poznanie

Zamiast tego zadaj od obiektu klasy Money wykonania odpowiednich dziaa za Ciebie:
Money a, b, c;
//
a.increaseBy( b );

Nie mwisz ju: daj mi ten atrybut, abym mg go wywietli. Teraz dasz czego
zupenie innego: daj mi moliw do wywietlenia wizualizacj tego atrybutu lub
wywietl swoj zawarto.
Kolejnym wyznacznikiem realizacji tej reguy jest szczegowo operacji. Operacje obszerne sprowadzaj si do jednorazowego dania od obiektw wykonania duych zada.
Szczegowe operacje daj od obiektw realizacji stosunkowo niewielkich zada. Oglnie, wol obszerne metody, poniewa ich stosowanie upraszcza kod i w wielu przypadkach eliminuje konieczno stosowania metod zwracajcych i ustawiajcych.
Metody akcesorw i mutatorw mona eliminowa dopiero na etapie budowy modelu
systemu, poniewa bez dobrze przemylanego modelu dynamicznego wiarygodne
przewidywanie przyszych mechanizmw komunikacji obiektw klas jest po prostu
niemoliwe. Oznacza to, e w przypadku braku tego modelu musisz zapewnia jak najwicej technik udostpniania danych, poniewa nie jeste w stanie przewidzie, ktre
z tych technik faktycznie bd konieczne. Taka strategia projektowania przez zgadywanie jest w najlepszym przypadku nieefektywna, poniewa w praktyce oznacza strat
czasu na pisanie niepotrzebnych metod (lub dodawanie zbdnych mechanizmw do implementowanych klas). Jeli postpujesz w myl zasady, e w pierwszej kolejnoci naley budowa model statyczny, musisz by przygotowany na to, e stracisz mnstwo
czasu na tworzenie nieprzydatnych lub zbyt elastycznych metod. Co wicej, jeli niewaciwy model statyczny wymusi zbyt due nakady na tworzenie niepotrzebnych rozwiza, cay projekt moe si zakoczy niepowodzeniem; a jeli mimo to zdecydujesz si
na jego kontynuowanie, koszt konserwacji kodu bdzie przewysza koszt jego ponownego napisania. Wrmy teraz do przykadu modelowania ruchu ulicznego, w ktrym
uyem modelu statycznego do analizy relacji odkrytych w fazie planowania systemu
przesyania komunikatw. Nie projektowaem modelu statycznego, by pniej prbowa na jego podstawie budowa model dynamiczny (z uwzgldnieniem ogranicze narzuconych przez przygotowany wczeniej model statyczny).
Uwane projektujc i skupiajc si na tym, co chcesz osign (nie na tym, jak to
zrobi), moesz wyeliminowa ze swojego programu znaczn cz metod zwracajcych
i ustawiajcych.

Sam wizualizuj swoje dane


Prawdopodobnie najbardziej zdumiewajcym posuniciem w procesie projektowania
systemu modelujcego ruch uliczny byo zdefiniowanie metody drawYourself() w ramach klasy Road. Umieciem kod interfejsu uytkownika w logice biznesowej aplikacji!
Warto si jednak zastanowi, jakie bd nastpstwa ewentualnej zmiany wymaga odnonie do interfejsu uytkownika. Przykadowo, mog zechcie reprezentowa odcinek drogi w formie podwjnej linii, ktrej oba kierunki jazdy bd oznaczone rnymi
kolorami. Mog te zdecydowa o rysowaniu na tych liniach kropek reprezentujcych

Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe

53

samochody. Skoro obiekty klasy Road same odpowiadaj za swoj reprezentacj graficzn, wszystkie te zmiany powinny dotyczy wycznie definicji tej klasy. Co wicej,
rne typy obiektw klasy Road (np. parkingi) mog realizowa zadanie wizualizacji
w rny sposb. Wad takiego rozwizania jest oczywicie zamieszanie w samej klasie
Road, ale cay kod zwizany z obsug interfejsu uytkownika mona skupi w jednej
klasie wewntrznej, aby zapewni przejrzysto systemu.
Warto te pamita, e aden kod interfejsu uytkownika tak naprawd nie zosta umieszczony w logice biznesowej systemu. Do opracowania warstwy interfejsu uytkownika
uyem podsystemu AWT lub Swing oba stanowi dodatkowe warstwy abstrakcyjne.
Waciwy kod interfejsu znajduje si wic w implementacji tych podsystemw. Na
tym wanie polega zaleta warstw abstrakcyjnych umoliwia izolowanie logiki biznesowej od odpowiednich mechanizmw podsystemu. Mog atwo przenie swj
system do innego rodowiska graficznego bez koniecznoci zmiany kodu, zatem jedynym problemem jest skomplikowany kod. Z tym utrudnieniem mog sobie poradzi,
umieszczajc wszystkie operacje zwizane z wizualizacj obiektw w wewntrznej klasie
(lub stosujc wzorzec fasady, ktry omwi za chwil).
Warto pamita, e tylko najprostsze klasy mog obsugiwa swoj wizualizacj za pomoc pojedynczej metody drawYourself(). W wikszoci przypadkw programista bdzie potrzebowa wikszej kontroli. Zdarza si, e te same obiekty musz prezentowa
swoj zawarto na wiele sposobw (jzyk HTML, kontrolka JLabel podsystemu Swing
itp.); rwnie czsto bdziesz musia wizualizowa tylko kilka spord wielu atrybutw obiektu.
Co wicej, izolacja implementacji obiektu od reszty programu wcale nie wymaga fizycznego rysowania reprezentowanych przez ten obiekt danych na ekranie. Tak naprawd
potrzebujesz jedynie jakiego uniwersalnego (przynajmniej na poziomie programu) mechanizmu graficznego reprezentowania danych. Obiekt moe np. przekazywa wizualizacj swoich danych w formie dokumentu XML do podsystemu wywietlania. Klasa
pomocnicza uyta wraz z wyraeniami java.text.NumberFormat moe przeksztaca t
reprezentacj zgodnie z okrelonymi ustawieniami regionalnymi. Wspominana klasa
Money moe zwraca w formacie Unicode odpowiedni wizualizacj acucha (obiektu
klasy String), ktra bdzie czya kwot pienin z symbolem waluty. Mona nawet
zwraca obraz w formacie .gif lub kontrolk JLabel.
Zmierzam do tego, e jeli reprezentacje tych atrybutw s obsugiwane w sposb waciwy, moliwe jest modyfikowanie wewntrznych mechanizmw klasy bez koniecznoci dostosowywania kodu uywajcego tych reprezentacji. (Reprezentacj jakiego
obiektu bd atrybutu, ktra jest prezentowana w sposb umoliwiajcy wywietlanie,
ale nie modyfikowanie, mona traktowa jak pewn odmian wzorca memento patrz
dalsza cz tego rozdziau. Istnieje te moliwo wykorzystywania innych wzorcw
projektowych, w szczeglnoci wzorca budowniczego, do wymuszania na obiektach
samodzielnego prezentowania swoich danych i jednoczenie izolowania kodu tworzcego interfejs uytkownika od waciwych obiektw. Wicej informacji na temat tego
wzorca znajdziesz w rozdziale 4.).

54

Wzorce projektowe. Analiza kodu sposobem na ich poznanie

JavaBeans i Struts
Ale moesz protestowa co z JavaBeans, Struts i innymi bibliotekami, ktre
wykorzystuj przecie zarwno akcesory, jak i mutatory? No wanie, co z nimi?
Istnieje doskonay sposb budowy komponentw JavaBean bez metod zwracajcych
i ustawiajcych wanie w tym celu stworzono klasy BeanCustomer, BeanInfo i BeanDescriptor. Projektanci specyfikacji komponentw JavaBean uwzgldnili metody get
i set tylko dlatego, e sdzili, i uatwi to przygotowywanie komponentw niedowiadczonym programistom (mieli nadziej, e tacy programici bd mogli tworzy komponenty i jednoczenie uczy si, jak naley to robi prawidowo). Przewidywania
twrcw tej technologii niestety si nie sprawdziy.
Ludzie maj skonno do nadmiernego przywizywania si do biblioteki JavaBeans
(i wszelkich innych bibliotek wykorzystujcych jakie elementy proceduralne). Programici zdaj si zapomina, e biblioteki te pocztkowo opracowywano z myl o rozwizywaniu konkretnych problemw napotykanych przez programistw. Niektrzy programici s szczeglnie zwizani z technikami programowania proceduralnego, inni nie
mog zapomnie o technikach obiektowych. Cz projektantw celowo ogupia
swoje interfejsy, poniewa wie, e w przeciwnym razie wielu ludzi po prostu by tych
interfejsw nie zrozumiao.
Przykadem takiego wanie podejcia s metody get i set zastosowane w technologii
JavaBeans. Metody te wprowadzono wycznie w celu zapewnienie dostpu do pewnych
waciwoci midzy innymi z poziomu narzdzi budowania interfejsw uytkownika.
Projektanci tej biblioteki nie przypuszczali, e programici bd te metody wywoywali
samodzielnie. Jedynym celem ich zdefiniowania byo umoliwienie interfejsom API
introspekcji klasy Class, sprawdzanie istnienia poszczeglnych waciwoci (w ramach zautomatyzowanych narzdzi, w tym rodowisk implementowania interfejsw
uytkownika) na podstawie analizy samych nazw metod. Takie podejcie nie sprawdzio
si w praktyce. Wraz z metodami zwracajcymi i ustawiajcymi wprowadzono do definicji klas mnstwo niepotrzebnego kodu, ktry uczyni ca technologi zdecydowanie
zbyt skomplikowan i zbyt proceduraln. Programici, ktrzy nie rozumieli idei abstrakcji danych, wywoywali te metody, powodujc, e kod tworzony na podstawie biblioteki
JavaBeans sta si niezwykle trudny w konserwacji. Midzy innymi z tego wzgldu
w wersji 1.5 Javy wprowadzono mechanizm metadanych, dziki ktremu mona
wyeliminowa z kodu nastpujce wyraenia:
private int property;
public int getProperty (
){ return property; }
public void setProperty (int value){ property = value; }

i zastpi je jedn deklaracj w postaci:


private @property int property;

Narzdzia wspomagajce budow interfejsw uytkownika mog teraz uywa interfejsw API introspekcji do odnajdywania waciwoci zamiast analizowa nazwy metod i na ich podstawie wnioskowa o istnieniu lub braku waciwoci o okrelonej nazwie.
Co wicej, nowy mechanizm eliminuje konieczno zamiecania kodu metodami akcesorw.

Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe

55

Wracajc do biblioteki Struts, z pewnoci nie jest to przykad architektury obiektowej


i jego projektanci nigdy nie prbowali tego celu osign. Zastosowana w bibliotece
Struts architektura MVC po prostu zmusza programistw do stosowania metod get
i set. Moesz oczywicie argumentowa, e to uniwersalny charakter biblioteki Struts
wyklucza jego obiektowo; okazuje si jednak, e inne architektury interfejsu uytkownika s znacznie lepiej odizolowane od logiki biznesowej ni w przypadku MVC. (By
moe najlepszym rozwizaniem jest po prostu unikanie technologii interfejsu uytkownika zbudowanych na podstawie koncepcji MVC. Architektura MVC powstaa blisko
30 lat temu i od tamtego czasu nasza wiedza na temat systemw informatycznych znacznie
si poszerzya.) Istnieje jeden istotny argument przemawiajcy za stosowaniem technologii Struts biblioteka ta zawiera mnstwo kodu, ktrego nie musisz pisa, i ktry
jest wystarczajco dobry dla bardzo wielu zastosowa. Jeli wystarczajco dobry
oznacza, e jako tego kodu rzeczywicie wystarczy, po co szuka lepszych rozwiza?
Podsumowujc, wielu programistw mwio mi, e podstawowe koncepcje decydujce
o obiektowoci systemw (np. ukrywanie szczegw implementacyjnych) s niepotrzebne tylko dlatego, e nie zostay urzeczywistnione w bibliotekach uywanych na
co dzie przez tych programistw (JavaBeans, Struts, .NET itp.). Moim zdaniem niepotrzebne s tego typu dyskusje.

Dostrajanie
Syszaem jeszcze jeden argument przemawiajcy za stosowaniem metod akcesorw
i mutatorw, zgodnie z ktrym takie rodowiska programowania jak Eclipse i pokrewne
do tego stopnia uatwiaj dostrajanie definicji metod do zwracania okrelonych typw,
e w praktyce nie ma si czym przejmowa. Ja jednak nadal si przejmuj.
Po pierwsze, mechanizm dostrajania kodu w rodowisku Eclipse obejmuje tylko istniejcy projekt. Jeli wic Twoja klasa jest wykorzystywana w wielu projektach, bdziesz
musia dostroi kady z nich osobno. Firmy, ktre prawidowo korzystaj z techniki
wielokrotnego uywania gotowych klas, zatrudniaj zwykle wiele grup programistw
pracujcych rwnolegle nad rnymi projektami programici z poszczeglnych zespow nie bd zadowoleni, kiedy powiesz im o daleko idcych zmianach w kodzie wspuytkowanej klasy, wynikajcych z jakiej, skdind susznej, obserwacji.
Po drugie, automatyczne dostrajanie sprawdza si tylko w przypadku stosunkowo niewielkich i prostych zmian, nie jest wic waciwym rozwizaniem w przypadku powanych modyfikacji. Konsekwencje takich zmian s zwykle zbyt rozlege, aby mogo sobie
z nimi poradzi zautomatyzowane narzdzie. Moesz np. zmieni skrypty jzyka SQL
i porednio wpyn na metody wywoywane w otoczeniu zmodyfikowanych skryptw.
I wreszcie po trzecie, warto jeszcze raz odnie si do wspomnianych zmian w klasie
Money i polu System.out. Prosta modyfikacja typw kilku zwracanych wartoci nie
wystarczy do waciwej obsugi omwionych przeze mnie zmian. Konieczna bdzie
przebudowa kodu otaczajcego wywoania odpowiednich metod zwracajcych. Trudno
oczywicie dowie, e dostrajanie kodu jest czym zym, moesz sam si przekona,
e zmian, o ktrych mwi po prostu nie da si wprowadzi za pomoc zautomatyzowanego narzdzia.

56

Wzorce projektowe. Analiza kodu sposobem na ich poznanie

Osoby wykorzystujce w dyskusji argument o moliwoci automatycznego dostrajania


kodu zwykle nie rozumiej czego znacznie waniejszego: Naduywanie akcesorw
i mutatorw na poziomie kluczowej abstrakcji jest wyznacznikiem kiepsko zaprojektowanego systemu przesyania komunikatw. Innymi sowy, zbudowany w ten sposb kod
ma prawdopodobnie tak niedopracowan struktur, e jego konserwacja bdzie si wizaa z mnstwem niepotrzebnych utrudnie, niezalenie od potencjalnych moliwoci
w zakresie automatycznego dostrajania. W takim przypadku konieczne jest ponowne
zaprojektowanie systemu, nie jego dostrajanie.
Wrmy do wczeniejszego przykadu pola System.out, ktry jest do charakterystyczny wyobra sobie, e zaprojektowae jzyk Java od pocztku, i e obiekty
klasy String s wywietlane na konsoli w sposb nastpujcy:
String s = "witaj wiecie";
s.print( String.TO_CONSOLE );

natomiast wczytywanie acuchw z konsoli odbywa si tak:


s.load( String.FROM_CONSOLE );

W takim przypadku wszystkie problemy zwizane z reprezentacj bajtow i formatem


Unicode zostayby rozwizane wewntrz implementacji klasy String. Take wszelkie
zmiany operacji wejcia-wyjcia (np. przeksztacenia reprezentacji bajtowej na reprezentacj znakow) przestayby stanowi problem. Poniewa tak naprawd jedynym zadaniem
interfejsw Reader i Writer jest wczytywanie i zapisywanie acuchw, przedstawione
powyej rozwizanie umoliwioby cakowit rezygnacj z tych interfejsw. Operacje
wejcia-wyjcia mogyby by obsugiwane przez odpowiednie przecienia metod
print() i load().
Oczywicie moesz si ze mn spiera, czy wspomniane problemy powinny by rozwizywane w ten sposb. Moesz te krytykowa pomys wprowadzenia skadowej TO_CONSOLE do klasy String lub File. Tak czy inaczej, zaproponowany projekt cakowicie eliminuje konieczno stosowania pola System.out i wszystkich jego akcesorw. Mona
rozwaa mnstwo operacji na acuchach i sugerowa, e adna z nich nie powinna by
czci klasy String, ale zapewniam Ci, e rzeczywistym rozwizaniem tego problemu
s wzorce projektowe (wzorzec wizytatora, strategii itd.).

ycie bez metod get i set


Jak mona projektowa systemy obiektowe bez metod zwracajcych i ustawiajcych
(odpowiednio get i set)? Jak naley projektowa systemy przesyania komunikatw,
ktre bd minimalizoway udzia metod akcesorw i mutatorw? Problem tkwi we
waciwym projektowaniu, nie w kodowaniu. Nie ma prostej reguy, wedug ktrej naleaoby umieci ten kod w tym miejscu, poniewa problem dotyczy raczej waciwego
podejcia do mechanizmw komunikowania si obiektw. Nie wystarczy po prostu
wyrzuci z kodu metody get i set naley przebudowa kod od podstaw przy uyciu
innej struktury.

Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe

57

Proces projektowania systemw obiektowych koncentruje si wok tzw. przypadkw


uycia, czyli autonomicznych zada realizowanych przez uytkownika kocowego
i pozwalajcych wygenerowa jakie przydatne dane wyjciowe. Przykadowo, logowanie do systemu nie jest przypadkiem uycia, poniewa nie prowadzi do wytworzenia jakichkolwiek przydatnych wynikw w dziedzinie problemu. Przypadkiem uycia
jest natomiast generowanie czeku. W przykadzie powiconym terminalom ATM
skupiem si na przypadku uycia polegajcym na wypacaniu gotwki.
System obiektowy jest wic implementacj czynnoci niezbdnych do realizacji okrelonych scenariuszy, ktre wynikaj z uprzednio przeanalizowanych przypadkw uycia. Obiekty czasu wykonywania aplikacji, ktre maj przypisane role w danym przypadku
uycia wykonuj swoje zadania, wymieniajc si komunikatami z innymi obiektami.
Warto jednak pamita, e nie wszystkie komunikaty s takie same. Budowa programu
proceduralnego, ktry wykorzystuje obiekty i klasy nie daje wikszych korzyci.
Kiedy w roku 1989 Kent Beck i Ward Cunningham prowadzili zajcia powicone projektowaniu systemw obiektowych, napotykali na powany opr ze strony programistw, ktrzy nie byli skonni rezygnowa ze swoich przyzwyczaje (w szczeglnoci
stosowania metod get i set). Scharakteryzowali ten problem w nastpujcy sposb:
Najwikszym problemem podczas nauki programowania obiektowego jest
skonienie osoby nauczanej do odrzucenia przyzwyczaje do globalnej wiedzy
o kontroli, ktra jest moliwa w programach proceduralnych, i skupienia si
na lokalnej wiedzy poszczeglnych obiektw, ktra take pozwala osiga
wyznaczone cele. Niedowiadczeni projektanci wykazuj skonno do mylenia
globalnego stosuj niepotrzebne zmienne globalne, wskaniki i w sposb
nieuzasadniony uzaleniaj funkcjonowanie swoich obiektw od implementacji
innych obiektw.
Mwic o globalnej wiedzy o kontroli Beck i Cunningham tak naprawd opisywali
wspominan w tym rozdziale wszechmogc klas klas, ktrej obiekty gromadz informacje ze wszelkich moliwych rde i samodzielnie przetwarzaj wszystkie
zebrane w ten sposb dane (zamiast pozwoli na przetwarzanie danych obiektom, ktre
je przechowuj). Zdanie o nieuzasadnionym uzalenianiu funkcjonowania obiektw
od implementacji innych obiektw dotyczy midzy innymi wywoa metod akcesorw
i mutatorw.
Cunningham opracowa metodyk nauczania, ktra w przystpny sposb demonstruje
proces projektowania tzw. kart CRC. Idea karty CRC sprowadza si do budowy tabel 46 cali, ktre s dzielone na nastpujce trzy czci:
Klasa

Nazwa klasy obiektw.

Odpowiedzialno

Co te obiekty mog robi? Zakres odpowiedzialnoci


obiektw jednej klasy powinien dotyczy pojedynczego
obszaru wiedzy specjalistycznej.

Klasy wsppracujce Pozostae klasy obiektw, z ktrymi bieca klasa


obiektw moe si komunikowa. Zbir klas
wsppracujcych powinien by moliwie niewielki.

58

Wzorce projektowe. Analiza kodu sposobem na ich poznanie

Pierwszy cykl opracowywania kart CRC zawsze jest obarczony powanymi bdami
pewne czynniki bd jeszcze ulegay zmianie.
W czasie swoich zaj Beck i Cunningham prezentowali przypadek uycia i namawiali
suchaczy do odgadywania, ktre obiekty bd wymagane do realizacji opisanego scenariusza. Analiza rozpoczynaa si zwykle od dwch obiektw, do ktrych z czasem
dodawano kolejne w odpowiedzi na demonstrowany rozwj scenariusza. Uczestnicy
wicze byli wyznaczani do roli poszczeglnych obiektw i otrzymywali kopie przypisanych im kart CRC. Jeli niezbdne byo uycie wielu obiektw pojedynczej klasy,
wyznaczano wiele osb (po jednej dla kadego z tych obiektw). Studenci dosownie
wystawiali sztuk opart na przypadku uycia. Oto kilka regu, ktrych staram si
przestrzega podczas gry w przypadku uycia z kartami CRC:
Wykonuj czynnoci odpowiadajce roli danego obiektu w przypadku uycia,

rozmawiajc z innymi obiektami.


Mog rozmawia wycznie z obiektami klas wsppracujcych. Jeli musz

si skontaktowa z obiektem spoza tej grupy, rozmawiam z obiektem klasy


wsppracujcej, ktry moe si komunikowa z tamtym obiektem. Jeli taka
porednia komunikacja nie jest moliwa, dodaj do swojej karty CRC kolejn
klas wsppracujc.
Nie mog pyta o informacje, ktre s mi potrzebne do realizacji jakiego

zadania. Zamiast tego powinienem prosi te obiekty klas wsppracujcych,


ktre dysponuj odpowiednimi danymi o wykonanie tego zadania dla mnie.
W takim przypadku mona oczywicie przekazywa tym obiektom informacje
niezbdne do realizacji zleconego zadania, jednak zakres przekazywanych
danych powinien by ograniczony do minimum.
Jeli istnieje zadanie, ktrego nikt nie jest w stanie zrealizowa, naley utworzy

now klas (wraz z odpowiedni kart CRC) lub rozszerzy zakres


odpowiedzialnoci jednej z istniejcych klas (zmodyfikowa jej kart CRC).
Jeli karta CRC jest pena, naley stworzy jeszcze jedn klas (z now kart

CRC), ktra przejmie cz obowizkw dotychczasowej, przecionej klasy.


Zoono poszczeglnych klas jest ograniczona rozmiarem karty CRC.
Staram si utosamia z dziedzin problemu (obsug konta bankowego,

zakupami internetowymi itp.) zarwno na poziomie sownictwa, jak i procesw.


Oznacza to, e prbuj modelowa zdarzenia dokadnie tak, jak prawdziwi
eksperci w danej dziedzinie rozwizuj analizowany problem. Udaj, e nie
istnieje co takiego jak komputer. Trzeba przyzna, e dosy rzadko spotyka
si na takich kursach osoby, ktre zwracaj si do siebie, stosujc polecenia
getX; w praktyce metody get i set s wic niemal zupenie zbdne.
Kiedy ju zakoczysz konwersacj rozwizujc dany problem, wcz dyktafon i sprbuj
j powtrzy lub odtwrz j z pamici na kartce papieru. Otrzymany tekst lub nagranie
bdzie swoistym modelem dynamicznym programu. Ostateczny zbir kart CRC jest
modelem statycznym programu. Po wykonaniu wielu prb i naniesieniu odpowiednich
poprawek mona w ten sposb rozwizywa niemal wszystkie problemy.

Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe

59

Opisany przeze mnie proces tak naprawd jest procesem projektowania obiektowego,
tyle e uproszczonym na potrzeby rodowiska zaj dydaktycznych. Niektrzy projektuj nawet rzeczywiste programy z wykorzystaniem kart CRC, ale technika ta nie sprawdza si w przypadku wikszych, niebanalnych rozwiza. Znacznie czciej projektanci
buduj modele dynamiczne i statyczne w jzyku UML, stosujc ktry z formalnych
procesw (RUP, Crystal, a nawet pewne odmiany Extreme Programming). Kluczowe znaczenie ma fakt, i system obiektowy jest w istocie konwersacj pomidzy obiektami. Jeli
przez chwil si nad tym zastanowisz, dojdziesz do przekonania, e metody get i set
najzwyczajniej w wiecie nie s potrzebne w takiej konwersacji. Z tego samego wzgldu
metody get i set nie powinny wystpowa w Twoim kodzie, jeli przed przystpieniem
do jego budowy przeprowadzie proces projektowania z naleyt starannoci.
Tak dugo, jak dugo to tylko moliwe modelowanie nie powinno wykracza poza dziedzin problemu (zgodnie z tym, o czym wspomniaem w ostatniej regule). Tym, co wpdza wikszo ludzi w kopoty jest przekonanie o modelowaniu dziedziny w czasie,
gdy tak naprawd proces modelowania ma miejsce na poziomie implementacji. Jeli Twj
system przesyania komunikatw nie korzysta ze sownictwa typowego dla danej dziedziny problemu jeli Twj jzyk nie jest zrozumiay dla przecitnego uytkownika
tego programu to tak naprawd modelujesz ten system na poziomie implementacji.
W takich rodowiskach jak komputery (lub, co gorsza, bazy danych lub narzdzia wspomagajce budow interfejsw uytkownika) nie ma miejsca na ten poziom modelowania.
Przykadowo, w modelowaniu CRC wymagane jest utrzymywanie konwersacji w obszarze dziedziny problemu wanie przez stosowanie takiego sownictwa i procesw, ktrymi
posugiwaliby si prawdziwi uytkownicy kocowi. W ten sposb mona zapewni wierne
odwzorowanie modelowanej dziedziny przez system przesyania komunikatw. Baza
danych jest tylko elementem wewntrznym, ktry jest wykorzystywany przez niektre
klasy w roli mechanizmu utrwalania danych, i ktry w ogle nie powinien wystpowa w pocztkowym modelu.
Jeli utrzymasz struktur komunikatw w obszarze dziedziny problemu, najprawdopodobniej uda Ci si wyeliminowa zdecydowan wikszo metod get i set, poniewa
tego typu polecenia po prostu nie wystpuj w komunikacji pomidzy ekspertami w danej
dziedzinie, ktrzy rozwizuj rzeczywiste problemy.

Kiedy stosowanie akcesorw i mutatorw


jest uzasadnione?
Jeli musisz przekazywa informacje pomidzy obiektami, powiniene hermetycznie zamyka te informacje w innych obiektach. Funkcja get, ktra zwraca obiekt klasy Money,
jest kompletnie nieprzydatna z punktu widzenia obiektu oczekujcego wartoci double.
Najlepszym rozwizaniem byoby oczywicie stosowanie metod zwracajcych obiekty,
ktre implementuj okrelone interfejsy, poniewa takie interfejsy izolowayby obiekty
wywoujce od ewentualnych zmian implementacji klasy. Tego typu metod (zwracajcych referencje do interfejsw) nie naley traktowa jak typowych metod zwracajcych,
poniewa nie peni one funkcji mechanizmw zapewniajcych dostp do okrelonych

60

Wzorce projektowe. Analiza kodu sposobem na ich poznanie

pl. Jeli zmieni si wewntrzna implementacja klasy udostpniajcej dane, konieczna


bdzie taka modyfikacja definicji zwracanego obiektu, ktra uwzgldni wprowadzone
zmiany. Moliwe jest nawet zwracanie obiektw innych klas, pod warunkiem, e nowe
obiekty implementuj ten sam (oczekiwany po stronie wywoujcego) interfejs. Zewntrzny kod, ktry wykorzystuje taki obiekt za porednictwem jego interfejsu jest wic
skutecznie chroniony.
Oglnie, staram si jednak unika nawet tych stosunkowo niegronych form akcesorw
do zwracania tylko egzemplarzy klas stanowicych kluczowe abstrakcje systemu. (Jeli
nazwa klasy lub interfejsu odpowiada opisowi problemu na poziomie dziedzinowym, to
traktuj t klas lub interfejs jak kluczow abstrakcj systemu).
Komunikaty powinny zawiera (w postaci swoich argumentw) moliwie niewielkie
iloci danych, lepszym rozwizaniem jest jednak upychanie danych w obiektach ni
ich wyciganie z obiektw. Innymi sowy, lepiej gdy delegujemy zadanie do innego
obiektu (przekazujc mu kilka niezbdnych informacji), ni wywoujemy jedn z jego
metod do uzyskania potrzebnych danych (ktre naley jeszcze przetworzy). Nie twierdz,
e zwracanie wartoci jest czym zym, ale dopki jest to moliwe, powiniene zwraca albo
obiekty z cile ukrywajce swoje implementacje, albo wartoci logiczne, ktre nie mwi absolutnie niczego na temat implementacji. W przypadku terminala ATM lepszym
rozwizaniem bdzie skierowanie do bazy danych pytania: Czy mog wypaci Billowi
20 dolarw? (dla ktrego centrala moe wygenerowa odpowied logiczn), ni wysanie
dania: Podaj mi stan rachunku Billa i podjcie decyzji w warunkach lokalnych.
Istnieje jeden powany wyjtek od reguy unikania metod zwracajcych i ustawiajcych.
Wszystkie systemy obiektowe zawieraj co, co nazywam graniczn warstw proceduraln. Przykadowo, zdecydowana wikszo programw obiektowych dziaa w proceduralnych systemach operacyjnych lub komunikuje si z proceduralnymi bazami danych.
Interfejsy czce te zewntrzne podsystemy proceduralne z natury rzeczy s uniwersalne. Projektant biblioteki JDBC nie ma przecie pojcia o tym, co bdziesz robi ze
swoj baz danych, zatem zaprojektowane przez niego klasy musz by wysoce elastyczne. Innym przykadem biblioteki warstwy granicznej s klasy obsugujce interfejsy uytkownika (np. nalece do biblioteki Swing jzyka Java). Projektanci biblioteki
Swing nie wiedz, jak ich klasy bd uywane, zatem stworzone przez nich rozwizania
s bardzo uniwersalne. W normalnych warunkach zapewnianie elastycznoci (a wic
tworzenie niepotrzebnych funkcji) jest o tyle niewaciwe, e wymaga znacznych nakadw
czasowych. Tak czy inaczej, elastyczno jest nieodczn cech interfejsw granicznych, zatem klasy tej warstwy pene s metod akcesorw i mutatorw. Projektanci tego
typu rozwiza rzeczywicie nie maj wyboru.
W praktyce problem nieznajomoci przyszych zastosowa kodu dotyczy wszystkich
pakietw Javy. Wyeliminowanie wszystkich akcesorw i mutatorw jest niezwykle trudne,
jeli nie s znane przysze zastosowania obiektw danej klasy. Gdy wemie si pod
uwag t istotn przeszkod, trzeba przyzna, e projektanci Javy wykonali kawa dobrej
roboty, ukrywajc tyle szczegw implementacyjnych, ile byo moliwe. Nie twierdz,
e wszystkie decyzje podjte podczas projektowania biblioteki JDBC maj zastosowanie
w Twoim kodzie. Tak nie jest. Warto jednak pamita, e Twoja sytuacja jest duo
lepsza wiesz, jak poszczeglne klasy bd wykorzystywane, zatem nie musisz traci
czasu na zapewnianie niepotrzebnej elastycznoci.

Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe

61

Musz te wspomnie o wartociach staych, ktre czsto s udostpniane bezporednio


jako publiczne skadowe obiektw. Oto moje rady w tej kwestii:
Nie rb tego, jeli nie musisz. Na przykad lepszym rozwizaniem jest skalowanie

listy w sposb dostosowujcy jej rozmiar do zawartoci ni udostpnianie staej


MAX_SIZE.
Uywaj nowego (wprowadzonego w JDK 1.5) mechanizmu enum wszdzie tam,

gdzie jest to moliwe. Takie rozwizanie jest duo lepsze od kategorycznego


deklarowania i inicjalizowania wartoci typu static final int. Alternatywnym
rozwizaniem jest stosowanie wzorca typw wyliczeniowych zapewniajcego
bezpieczestwo typw, ktry opisano w ksice Efektywne programowanie
w jzyku Java autorstwa Joshuy Blocha (Helion, 2002).
Zasadniczym problemem jest zdefiniowanie typu wyliczeniowego w nastpujcy
sposb:
private static class Format{ private Format(); }
public static final Format SINGLE_LINE = null;
public static final Format POPUP_DIALOG = new Format();
public static final Format PANEL
= new Format();
public displayYourselfAs( Format how )
{
// Wywietla biec warto kalendarza we
// wskazanym formacie.
}

Poniewa argumentem metody displayYourselfAs() jest obiekt klasy Format,


i poniewa mog istnie tylko dwa egzemplarze tej klasy (i trzy referencje
do tych egzemplarzy), nie moesz przekaza do tej metody bdnej wartoci.
Gdyby jednak uy nastpujcego, zdecydowanie bardziej popularnego
rozwizania opartego na wyliczeniu typu int:
public static final int SINGLE_LINE = 0;
public static final int POPUP_DIALOG = 1;
public static final int PANEL
= 2;
public displayYourselfAs( int how )
{
//
}

mgby przekazywa w formie argumentu metody displayYourselfAs()


wartoci zupenie nonsensowne (np. 1). Bloch powici temu zagadnieniu
dziesi stron, wic nie pozostaje mi nic innego jak odesa Ci do jego ksiki.
Jeli musisz udostpni sta, upewnij si, e rzeczywicie jest to staa. Dostpne
w Javie sowo kluczowe final gwarantuje, e dana referencja nie bdzie

modyfikowana, zatem nigdy nie wskae na inn struktur, co jednak wcale


nie oznacza, e rwnie dobrze chroniony jest obiekt wskazywany przez t
referencj. Jeli dany obiekt jest wykorzystywany w roli staej, musisz napisa
odpowiedni klas w taki sposb, aby modyfikowanie jej obiektw po prostu
nie byo moliwe. (W Javie o takich klasach mwi si, e s niezmienne, jednak
poza deklarowaniem wszystkich pl takiej klasy ze sowem kluczowym final
jzyk ten nie oferuje adnych dodatkowych mechanizmw gwarantowania
niezmiennoci. Jedynym sposobem jest programowanie klas w taki wanie
sposb).

62

Wzorce projektowe. Analiza kodu sposobem na ich poznanie

Przeanalizujmy teraz dostpn w Javie klas Color. Kiedy ju utworzysz obiekt tej klasy,
nie moesz zmieni reprezentowanego przez niego koloru, poniewa klasa Color nie udostpnia adnych metod zmieniajcych kolor. Przyjrzyj si nastpujcemu fragmentowi:
public static final Color background = Color.RED;
//
c.darken();

Wywoanie metody darken() nie modyfikuje obiektu wskazywanego przez sta background; zamiast tego metoda zwraca obiekt klasy Color, ktrego odcie jest ciemniejszy od
koloru oryginalnego. Powyszy kod tak naprawd niczego nie robi, poniewa zwrcony obiekt klasy Color nie jest nigdzie umieszczany, zatem nie moesz teraz wykona
operacji:
background = c.darken();

poniewa pole background zostao zadeklarowane ze sowem kluczowym final.


I wreszcie zdarza si, e jeden obiekt peni funkcj opiekuna wielu innych obiektw.
Przykadowo, dostpna w Javie klasa Collection zawiera cay zbir obiektw, ktre
zostay do niej przekazane z zewntrz. Poniewa sowa get i set s czsto wykorzystywane w nazwach metod odpowiedzialnych wycznie za umieszczanie obiektw w obiekcie opiekuna oraz wyciganie tych obiektw z tego zbiorczego obiektu, metody te nie
odsaniaj informacji o sposobie dziaania obiektu. Rozwizanie to jest wic zupenie
poprawne. Oglnie, jeli przekazujesz jakie dane do zbiorczego obiektu, oczekiwanie
odnonie do moliwoci ponownego pobrania zapisanych danych z tego obiektu jest
czym normalnym.
Skrajnymi przykadami opiekunw informacji s bazy danych, cho twrcy ich interfejsw poszli jeszcze dalej w kierunku metod get i set, poniewa baza danych w zasadzie staa si rozwizaniem proceduralnym (wielkim zbiorem informacji) i istotnym elementem wspominanej ju warstwy granicznej. Efektem takiego podejcia jest brak
moliwoci uzyskiwania dostpu do proceduralnych baz danych przy uyciu technik
typowych dla rozwiza obiektowych. Stosowanie metod get i set jest nieuniknione.
Moesz jednak (i powiniene) zamyka wywoania proceduralne kierowane do warstwy
bazy danych w funkcjonujcych na wyszym poziomie obiektach dziedzinowych, po czym
pisa swj kod na bazie interfejsw tych obiektw. W kodzie obiektw poredniczcych moesz umieci dowoln potrzebn liczb wywoa get i set kierowanych do
bazy danych. Wiksza cz Twojego programu nie bdzie jednak miaa dostpu do
mechanizmu obsugujcego baz danych, poniewa w komunikacji z tym proceduralnym podsystemem bd poredniczyy specjalne obiekty wyszego poziomu.

Podsumowanie problematyki metod zwracajcych


i ustawiajcych
Sprbujmy teraz podsumowa nasze wnioski i obserwacje: Nie twierdz, e zwracanie
wartoci jest ze, e informacje nie mog by przekazywane pomidzy poszczeglnymi
skadnikami systemu, lub e mona wyeliminowa z programu wszystkie akcesory i mu-

Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe

63

tatory. Informacje musz przepywa w systemie, poniewa brak takiego przepywu


oznaczaby cakowity parali programu. Wszelkie przekazywane informacje powinny
jednak by hermetycznie zamykane w obiektach, ktre ukrywaj swoje implementacje
przed pozostaymi elementami programu.
Oto najwaniejsze wnioski:
Moliwoci konserwacji programu s odwrotnie proporcjonalne do iloci

danych przekazywanych pomidzy obiektami.


Ujawnianie szczegw implementacyjnych negatywnie wpywa na moliwo

konserwacji kodu. Zanim dodasz do definicji klasy metod akcesora lub mutatora,
upewnij si, e jest ona rzeczywicie niezbdna.
Klasy, ktre bezporednio modeluj system na poziomie dziedzinowym

s niekiedy nazywane obiektami biznesowymi i prawie nigdy nie


potrzebuj akcesorw i mutatorw. Program mona traktowa jak szeroki
zbir uniwersalnych bibliotek, ktre naley poczy zgodnie z zaleceniem
o braku metod zwracajcych i ustawiajcych, oraz klas dziedzinowych,
ktre powinny zawiera i izolowa ca implementacj. Obecno metod
get i set na tym poziomie pokazuje, e proces projektowania nie zosta
przeprowadzony prawidowo (w szczeglnoci, zabrako naleytej starannoci
w fazie modelowania dynamicznego).
Utrzymywanie procesu projektowania w zakresie dziedziny problemu (w ramach

okrelonego rodowiska biznesowego) tak dugo, jak dugo to tylko moliwe,


pozwala na zaprojektowanie systemu przesyania komunikatw pozbawionego
metod zwracajcych i ustawiajcych, poniewa w wikszoci przypadkw
polecenia get i set po prostu nie wystpuj w dziedzinie problemu.
Im bardziej zbliysz si do proceduralnej granicy systemu obiektowego (interfejsu

bazy danych, klas obsugujcych interfejs uytkownika itp.), tym trudniej Ci bdzie
ukry szczegy implementacyjne swojego kodu. Rozwanym posuniciem jest
w takim przypadku uycie akcesorw i mutatorw w specjalnie utworzonej
warstwie granicznej.
Take uniwersalne biblioteki i klasy nie mog w peni ukrywa swoich

implementacji, zatem zawsze oferuj akcesory i mutatory.


Czasem nie warto traci czasu na pene (hermetyczne) izolowanie implementacji.
Wystarczy wspomnie tak banalne klasy jak Point czy Dimension. Podobnie,

model izolowania prywatnych klas, ktre s tworzone w ramach implementacji


innych klas (np. klasa Node zdefiniowana jako prywatna klasa wewntrzna klasy
Tree), czsto moe by znacznie mniej restrykcyjny. Z drugiej strony, warto
pamita o problemach powodowanych przez pola System.in, System.out
i System.err, kiedy wprowadzono klasy Reader i Writer. Czy potrafiby np.
doda jednostki (centymetry, metry, itp.) do klasy Dimension?
Na konferencji JavaOne (myl, e w roku 1991) poproszono Jamesa Goslinga o podzielenie si z suchaczami moliwie krtk rad na temat programowania. Gosling odpowiedzia (nie jest to cytat), e moliwo konserwacji kodu jest odwrotnie proporcjonalna

64

Wzorce projektowe. Analiza kodu sposobem na ich poznanie

do iloci danych przemieszczajcych si pomidzy obiektami. Oznacza to, e cho nie


moesz cakowicie wyeliminowa przesyania danych (szczeglnie w rodowiskach
z wieloma obiektami, ktre stale ze sob wsppracuj celem wykonania pewnych zada), powiniene przynajmniej prbowa minimalizowa przepyw informacji.
Kiedy ju musz przekaza jak informacj z jednego obiektu do drugiego, staram si
to robi z uwzgldnieniem nastpujcej pary regu:
Przesyaj obiekty (najlepiej opierajc si na implementowanych przez nie

interfejsach), nie surowe dane.


Stosuj model upychania danych, zamiast modelu wycigania danych.

Przykadowo, obiekt moe delegowa zadanie do jednego ze swoich obiektw


wsppracujcych (przekazujc mu niewielk ilo informacji niezbdnych
do realizacji zleconego zadania). Rozwizanie alternatywne, w ktrym obiekt
wsppracujcy wyciga informacje od obiektu delegujcego za porednictwem
metod zwracajcych, jest z wielu wzgldw gorsze. Wzorzec wagi pirkowej
bazuje wanie na modelu upychania.
Przeksztacenie systemu komunikacji z modelu upychania w model wycigania czsto jest tylko kwesti odpowiedniego przekierowania komunikatw.
Ocena moliwoci konserwowania kodu nie jest binarna osobicie przywizuj do
tego problemu bardzo du wag, poniewa konserwacja programu tak naprawd rozpoczyna si na kilka sekund po jego napisaniu. Kod zbudowany z uwzgldnieniem moliwoci jego konserwowania zwykle jest nie tylko bardziej zrozumiay, ale te bardziej
niezawodny.
Tak czy inaczej, musisz zdecydowa, w ktrym miejscu rozlegej skali moliwoci konserwacji chcesz usytuowa swj program. Biblioteki Javy (a przynajmniej ich zdecydowana wikszo) s dobrym przykadem kompromisu pomidzy takimi moliwociami
a stosunkowo uniwersaln funkcjonalnoci. Autorzy pakietw jzyka programowania
Java ukryli tyle szczegw implementacyjnych, ile tylko zdoali (warto pamita, e
ich biblioteki musiay zapewnia nie tylko wyjtkow elastyczno, ale take odpowiednie graniczne interfejsy proceduralne). Cen, jak musieli zapaci, s utrudnienia
we wprowadzaniu strukturalnych zmian do takich bibliotek jak Swing, poniewa zbyt
wiele istniejcych programw ju teraz jest uzalenionych od szczegw implementacyjnych tych bibliotek.
Nie wszystkie biblioteki Javy odkrywaj swoje implementacje. Wystarczy wspomnie
o interfejsach API biblioteki Crypto (w pakiecie javax.crypto) oraz klasach URL i URLConnection, ktre nie odsaniaj niemal adnych informacji i jednoczenie s wyjtkowo
elastyczne. Take klasy serwletw s dobrym przykadem hermetycznego zamknicia implementacji, co i tak nie wyklucza przekazywania informacji (cho twrcy tych klas
mogli pj jeszcze krok dalej i opracowa dodatkow warstw abstrakcji umoliwiajc
budow kodu HTML).
Kiedy wic widzisz metody, ktrych nazwy rozpoczynaj si od sw get lub set,
powiniene si zastanowi, co to waciwie oznacza. Zadaj sobie pytanie, czy taki prze-

Rozdzia 1. Wstp: programowanie obiektowe i wzorce projektowe

65

pyw danych jest rzeczywicie konieczny. Czy moesz tak zmodyfikowa system przesyania komunikatw, aby informacje byy przekazywane w formie mniej szczegowych
struktur, co mogoby wyeliminowa ten tryb przenoszenia danych? Czy moesz przekazywa te same informacje w postaci argumentw komunikatw, zamiast w postaci
osobnych komunikatw? Czy alternatywna architektura rzeczywicie umoliwi lepsze
ukrywanie implementacji? Jeli jednak nie ma alternatywy dla istniejcego rozwizania,
musisz si z tym pogodzi.

Vous aimerez peut-être aussi