Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00126 008225 10493965 na godz. na dobę w sumie
Wzorce projektowe. Analiza kodu sposobem na ich poznanie - książka
Wzorce projektowe. Analiza kodu sposobem na ich poznanie - książka
Autor: Liczba stron: 464
Wydawca: Helion Język publikacji: polski
ISBN: 83-7361-948-8 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> programowanie >> wzorce projektowe
Porównaj ceny (książka, ebook, audiobook).

Opanuj zasady stosowania wzorców projektowych na praktycznych przykładach

Wzorce projektowe to zapisane w sposób formalny sposoby rozwiązywania najczęstszych problemów, z jakimi borykają się twórcy oprogramowania stosujący języki obiektowe. Najczęściej stosowane wzorce zostały skatalogowane i przedstawione w postaci diagramów UML, jednak do poprawnego ich wykorzystywania niezbędna jest wiedza praktyczna. Przystępując do implementacji wzorca projektowego, należy poznać zakres jego zastosowania. Taką wiedzę najlepiej zdobywa się, analizując przykłady kodów źródłowych.

Dzięki książce 'Wzorce projektowe. Analiza kodu sposobem na ich poznanie' poznasz wzorce w taki właśnie sposób -- badając programy, w których je zastosowano. Każdy z omawianych w książce wzorców zaprezentowany jest w oparciu o dwie implementacje szczegółowo wyjaśniające zasadę jego działania. Dzięki takim opisom wzorców opanujesz tę technologię znacznie szybciej niż w przypadku nauki teoretycznych podstaw oraz prób ich samodzielnego wdrażania we własnych aplikacjach. Unikniesz typowych błędów i dowiesz się, jak prawidłowo wykorzystywać każdy z wzorców.

Książka zawiera również zestawienie najczęściej wykorzystywanych wzorców projektowych wraz z opisem ich zastosowań.

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

IDZ DO IDZ DO PRZYK£ADOWY ROZDZIA£ PRZYK£ADOWY ROZDZIA£ SPIS TREŒCI SPIS TREŒCI KATALOG KSI¥¯EK KATALOG KSI¥¯EK KATALOG ONLINE KATALOG ONLINE ZAMÓW DRUKOWANY KATALOG ZAMÓW DRUKOWANY KATALOG TWÓJ KOSZYK TWÓJ KOSZYK DODAJ DO KOSZYKA DODAJ DO KOSZYKA CENNIK I INFORMACJE CENNIK I INFORMACJE ZAMÓW INFORMACJE ZAMÓW INFORMACJE O NOWOŒCIACH O NOWOŒCIACH ZAMÓW CENNIK ZAMÓW CENNIK CZYTELNIA CZYTELNIA FRAGMENTY KSI¥¯EK ONLINE FRAGMENTY KSI¥¯EK ONLINE Wydawnictwo Helion ul. Chopina 6 44-100 Gliwice tel. (32)230-98-63 e-mail: helion@helion.pl Wzorce projektowe. Analiza kodu sposobem na ich poznanie Autor: Allen Holub ISBN: 83-7361-948-8 Tytu³ orygina³u: Holub on Patterns: Learning Design Patterns by Looking at Code Format: B5, stron: 464 Opanuj zasady stosowania wzorców projektowych na praktycznych przyk³adach (cid:127) Dowiedz siê, czym s¹ wzorce projektowe (cid:129) Zaimplementuj wzorce we w³asnych programach (cid:129) Poznaj rodzaje wzorców projektowych Wzorce projektowe to zapisane w sposób formalny sposoby rozwi¹zywania najczêstszych problemów, z jakimi borykaj¹ siê twórcy oprogramowania stosuj¹cy jêzyki obiektowe. Najczêœciej stosowane wzorce zosta³y skatalogowane i przedstawione w postaci diagramów UML, jednak do poprawnego ich wykorzystywania niezbêdna jest wiedza praktyczna. Przystêpuj¹c do implementacji wzorca projektowego, nale¿y poznaæ zakres jego zastosowania. Tak¹ wiedzê najlepiej zdobywa siê, analizuj¹c przyk³ady kodów Ÿród³owych. Dziêki ksi¹¿ce „Wzorce projektowe. Analiza kodu sposobem na ich poznanie” poznasz wzorce w taki w³aœnie sposób — badaj¹c programy, w których je zastosowano. Ka¿dy z omawianych w ksi¹¿ce wzorców zaprezentowany jest w oparciu o dwie implementacje szczegó³owo wyjaœniaj¹ce zasadê jego dzia³ania. Dziêki takim opisom wzorców opanujesz tê technologiê znacznie szybciej ni¿ w przypadku nauki teoretycznych podstaw oraz prób ich samodzielnego wdra¿ania we w³asnych aplikacjach. Unikniesz typowych b³êdów i dowiesz siê, jak prawid³owo wykorzystywaæ ka¿dy z wzorców. (cid:129) Zastosowanie wzorców projektowych (cid:129) Klasyfikacja wzorców (cid:129) Podstawowe pojêcia z dziedziny obiektowoœci (cid:129) Interfejsy i wzorce konstrukcyjne (cid:129) Implementacja wzorców obserwatora i fasady (cid:129) Wykorzystanie wzorców projektowych w aplikacjach bazodanowych Ksi¹¿ka zawiera równie¿ zestawienie najczêœciej wykorzystywanych wzorców projektowych wraz z opisem ich zastosowañ. Spis treści O Autorze ......................................................................................... 9 Przedmowa ..................................................................................... 11 Rozdział 1. Wstęp: programowanie obiektowe i wzorce projektowe .................... 15 Wzorce kontra cechy języków programowania .............................................................. 16 Czym właściwie jest wzorzec projektowy? .................................................................... 17 Czemu te wzorce mają w ogóle służyć? ......................................................................... 21 Rola wzorców w procesie projektowania ....................................................................... 22 Napięcia pomiędzy wzorcami a prostotą .................................................................. 23 Klasyfikacja wzorców .................................................................................................... 25 O programowaniu, ogólnie ....................................................................................... 26 Programowanie języka FORTRAN w Javie ............................................................. 28 Programowanie z otwartymi oczami ........................................................................ 31 Czym jest obiekt? ........................................................................................................... 32 Nonsens! .................................................................................................................. 32 Obiekt jest zbiorem zdolności .................................................................................. 33 Jak nie należy tego robić? ........................................................................................ 35 Jak zatem należy to robić „prawidłowo”? ................................................................ 38 Automat komórkowy ...................................................................................................... 42 Metody zwracające i ustawiające są złe ............................................................................. 48 Sam wizualizuj swoje dane ...................................................................................... 52 JavaBeans i Struts .................................................................................................... 54 Dostrajanie ............................................................................................................... 55 Życie bez metod get i set .......................................................................................... 56 Kiedy stosowanie akcesorów i mutatorów jest uzasadnione? .................................. 59 Podsumowanie problematyki metod zwracających i ustawiających ........................ 62 Rozdział 2. Programowanie z interfejsami i kilka wzorców konstrukcyjnych ........ 67 Dlaczego słowo kluczowe extends jest złe ..................................................................... 67 Interfejsy kontra klasy .................................................................................................... 69 Utrata elastyczności ................................................................................................. 69 Sprzęganie ................................................................................................................ 71 Problem ułomnej klasy bazowej ............................................................................... 73 Dziedziczenie wielokrotne ....................................................................................... 80 Szkielety ................................................................................................................... 81 Wzorce metody szablonowej i metody wytwórczej ................................................. 82 Podsumowanie problemu ułomnych klas bazowych ................................................ 88 6 Wzorce projektowe. Analiza kodu sposobem na ich poznanie Kiedy stosowanie słowa extends jest uzasadnione ......................................................... 90 Eliminowanie relacji extends ......................................................................................... 93 Wzorce wytwórni i singletonów ............................................................................... 94 Singleton .................................................................................................................. 96 Problem z przetwarzaniem wielowątkowym w przypadku singletonów .................. 98 Blokowanie DCL (nigdy tego nie rób) ................................................................... 100 Zabijanie singletonu ............................................................................................... 101 Wytwórnia abstrakcji ............................................................................................. 103 Pomieszanie wzorców ............................................................................................ 107 Dynamiczne tworzenie obiektów w ramach wytwórni ........................................... 109 Polecenie i strategia ............................................................................................... 112 Podsumowanie ............................................................................................................. 116 Rozdział 3. Gra w życie .................................................................................. 119 Zdobywanie życia ........................................................................................................ 120 Sporządzanie 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 piórkowa ............................................................................................................ 210 Pula wagi piórkowej ............................................................................................... 215 Memento ...................................................................................................................... 217 Dokończenie ................................................................................................................. 220 Podsumowanie ............................................................................................................. 225 Rozdział 4. Implementacja osadzonego interpretera języka SQL ....................... 227 Wymagania .................................................................................................................. 228 Architektura .................................................................................................................. 229 Warstwa składowania danych ...................................................................................... 231 Interfejs Table ........................................................................................................ 231 Wzorzec mostu ....................................................................................................... 237 Tworzenie tabeli: wzorzec projektowy wytwórni abstrakcji .................................. 240 Tworzenie i zapisywanie tabel: pasywne iteratory i wzorzec projektowy budowniczego ................................................................... 243 Wypełnianie tabeli danymi ..................................................................................... 255 Przeszukiwanie tabeli: wzorzec projektowy iteratora ............................................ 258 Implementacja transakcji (funkcji cofania) przy użyciu wzorca projektowego polecenia ....................................................... 267 Modyfikowanie tabeli: wzorzec projektowy strategii ............................................ 273 Selekcja i złączenia ................................................................................................ 276 Rozmaitości ............................................................................................................ 282 Odmiany interfejsu Table: wzorzec dekoratora ...................................................... 290 Spis treści 7 Rozbudowa całego systemu o obsługę języka SQL ..................................................... 300 Struktura modułu języka SQL ................................................................................ 301 Podział danych wejściowych, ponowne omówienie wzorca wagi piórkowej i analiza wzorca łańcucha odpowiedzialności ...................................................... 301 Skaner: wzorzec łańcucha odpowiedzialności ....................................................... 311 Klasa ParseFailure .................................................................................................. 319 Klasa Database ............................................................................................................. 321 Stosowanie klasy Database .................................................................................... 322 Wzorzec pośrednika ............................................................................................... 325 Zbiór tokenów i pozostałe stałe .............................................................................. 330 Wzorzec interpretatora ................................................................................................. 337 Obsługiwana część języka SQL ............................................................................. 337 Analiza funkcjonowania interpretera języka SQL .................................................. 359 Warstwa JDBC ............................................................................................................. 366 Wzorzec stanu i klasa JDBCConnection ...................................................................... 373 Wyrażenia .............................................................................................................. 379 Wzorzec adaptera (zbiory wynikowe) .................................................................... 380 Wykańczanie kodu ................................................................................................. 385 Kiedy mosty nie zdają egzaminu ............................................................................ 386 Uff! ............................................................................................................................... 387 Dodatek A Krótki podręcznik o wzorcach projektowych ................................... 389 Wzorce konstrukcyjne .................................................................................................. 391 Wytwórnia abstrakcji ............................................................................................. 392 Budowniczy ........................................................................................................... 394 Metoda wytwórcza ................................................................................................. 396 Prototyp .................................................................................................................. 398 Singleton ................................................................................................................ 400 Wzorce strukturalne ..................................................................................................... 403 Adapter ................................................................................................................... 404 Most ....................................................................................................................... 406 Kompozyt ............................................................................................................... 408 Dekorator ............................................................................................................... 410 Fasada .................................................................................................................... 412 Waga piórkowa ...................................................................................................... 414 Pośrednik ............................................................................................................... 416 Wzorce czynnościowe .................................................................................................. 419 Łańcuch odpowiedzialności ................................................................................... 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. Wstęp: programowanie obiektowe i wzorce projektowe W zwykłych okolicznościach tego typu książka powinna się rozpoczynać cytatem z Chri- stophera Alexandra, architekta (budynków, nie oprogramowania), który jako pierwszy wprowadził termin wzorca projektowego. Odkryłem, że chociaż Alexander jest wspa- niałym człowiekiem i pisze naprawdę świetne książki, jego dzieła mogą być zrozumiałe nie dla wszystkich, zatem pozwolę sobie w tym miejscu pominąć „obowiązkowy” cytat. Warto jednak pamiętać, że tezy prezentowane przez Alexandra stanowią podstawowe źródło współczesnych koncepcji wzorców projektowych. Podobnie, prawdziwym przełomem w kwestii zastosowań wzorców projektowych w świecie oprogramowania była książka Design Patterns: Elements of Reusable Object- -Oriented Software autorstwa Gammy, Helma, Johnsona i Vlissidesa (Addison-Wesley, 1995). (Czterej autorzy tej książki są przez większość projektantów dowcipnie nazywani Bandą Czworga.) Moja książka nie mogłaby powstać, gdyby nie powstała książka Bandy Czworga, zatem ja sam (podobnie jak chyba wszyscy programiści stosujący techniki obiektowe) jestem winien autorom tej książki ogromne podziękowania. Tak czy ina- czej, wspomniana przeze mnie książka jest akademicką prezentacją wzorców, która dla większości początkujących programistów z natury rzeczy będzie po prostu niezrozu- miała. Postanowiłem podjąć ryzyko zatracenia tej akademickiej precyzji i stworzyć coś bardziej przystępnego. 16 Wzorce projektowe. Analiza kodu sposobem na ich poznanie Wzorce kontra cechy języków programowania Nasze rozważania powinniśmy rozpocząć od zgłębienia samego pojęcia wzorca przez analizę prostych własności języków programowania. Wiele wzorców projektowych jest wykorzystywanych tak powszechnie, jest tak głęboko zakorzenionych w umysłach tylu programistów, że nie są już traktowane jak wzorce, tylko jak cechy poszczególnych ję- zyków programowania. Wzorce te nie są uważane za coś specjalnego — stanowią po prostu typowe „sposoby rozwiązywania określonych problemów”. Niektórzy odróżniają wzorce od cech językowych wyłącznie na podstawie ich zastosowania (przykładowo, wzorzec, w przeciwieństwie do cech językowych, jest reprezentowany w sposób for- malny). Dla mnie taki podział nie jest jednak wiarygodny. Cecha (własność) językowa to po prostu wzorzec, którego stosowanie nabrało powszechnego wymiaru. Doskonałym przykładem ewolucji wzorców do postaci cech językowych jest model wy- prowadzania struktur danych. We wczesnych latach osiemdziesiątych ubiegłego wieku, kiedy królował język programowania C, wyprowadzanie struktur danych było typowym wzorcem projektowym. W języku C istniało wówczas wiele przykładów relacji „rozsze- rzania” jednych struktur w inne, bardziej szczegółowe. Przykładowo, standardowa im- plementacja funkcji malloc() wykorzystuje nagłówek (klasę bazową), który jest rozsze- rzany do postaci innej struktury (klasy potomnej), a ta z kolei dziedziczy metodę free() z klasy bazowej. Także funkcje abstrakcyjne należały do wzorca wyprowadzania. W kodzie języka pro- gramowania C często można się było natknąć na przekazywanie tablic wskaźników do funkcji, które były inicjalizowane w różny sposób dla różnych „klas”. W języku C++ w taki sam sposób zaimplementowano zarówno metody abstrakcyjne, jak i mechanizm dziedziczenia interfejsów (które w świecie języka C nie zostały nawet nazwane). Wyprowadzanie struktur jako takie nie było wbudowane w język programowania C i większość programistów tego języka nie stosowała obiektów, zatem wyprowadzania struktur z pewnością nie można uznać za cechę języka C — był to więc wzorzec. Było to coś, co można było bez trudu znaleźć w wielu programach, które z rozmaitych wzglę- dów musiały rozwiązywać podobne problemy, nie było to jednak działanie naturalne z perspektywy przeciętnego programisty języka C. Mechanizmy wyprowadzania (dziedziczenia) i interfejsów są obecnie wbudowanymi elementami języków programowania, można więc przyjąć, że stały się cechami tych języków. Rozdział 1. ♦ Wstęp: programowanie obiektowe i wzorce projektowe 17 Czym właściwie jest wzorzec projektowy? Po pierwsze, wzorce projektowe są, co bardzo ważne, odkrywane, nie są więc wynalaz- kami. Kiedy Christopher Alexander analizował konstrukcje wielu udanych budynków, za każdym razem skupiał się tylko na jednym ich aspekcie (np. starał się ocenić, co decyduje o „przytulności” pomieszczeń), co w dłuższej perspektywie prowadziło do cie- kawych odkryć właśnie w kwestii odpowiednich wzorców. Udane, „przytulne” pomiesz- czenia rozwiązują określone klasy problemów (np. związane z oświetleniem) w bardzo podobny sposób. Podobnie, kiedy analizujesz kod wielu programów napisanych przez różnych programistów, i kiedy skupiasz się na konkretnym problemie implementa- cyjnym rozwiązywanym przez te programy z konieczności (np. problem izolacji pod- systemów), również możesz dostrzec pewne wzorce. Odkrywasz wówczas, że wielu programistów niezależnie od siebie stosuje podobne techniki rozwiązywania podobnych problemów. Kiedy już zdasz sobie sprawę z istnienia tych technik, będziesz widział odpowiednie wzorce gdziekolwiek spojrzysz. Za wzorzec można jednak uznać tylko takie rozwiązanie, które zostało zastosowane w wielu programach opracowywanych zupełnie niezależnie od siebie. Twierdzenie „Wynaleźliśmy wzorzec projektowy, który…” jest więc niezawodnie oznaką autorów, którzy nie rozumieją istoty wzorców. Istnieje oczy- wiście możliwość opracowania genialnego projektu, ale nie będzie to wzorzec projek- towy dopóty, dopóki nie zostanie zastosowany przez wielu programistów pracujących całkowicie niezależnie nie tylko od siebie, ale też od autorów projektu. (Nie można oczy- wiście wykluczyć sytuacji, w której wynaleziony „wzorzec” staje się wzorcem rzeczy- wistym z racji jego powszechnego stosowania). Wzorzec projektowy jest więc ogólną techniką wykorzystywaną do rozwiązywania pewnej klasy powiązanych ze sobą problemów. Nigdy nie jest to konkretne rozwiąza- nie jakiegoś problemu. Prawdopodobnie każdy architekt, któremu udało się stworzyć „przytulne” pomieszczenie zaprojektował jego oświetlenie w nieco inny sposób — to samo dotyczy programistów, z których każdy trochę inaczej implementuje swoje rozwiązania. Wzorzec jest ogólną strukturą rozwiązania (jeśli chcesz, możesz to na- zywać metarozwiązaniem), ale z pewnością nie jest rozwiązaniem jako takim. Dobrą analogią jest świat muzyki. Pojęcie „muzyki klasycznej” można traktować jak pewien wzorzec kompozytorski. Identyfikacja muzyki pasującej do wzorca „muzyki kla- sycznej” jest możliwa, ponieważ analizowane utwory po prostu brzmią jak muzyka klasyczna. Nie oznacza to jednak, że poszczególne dzieła tej muzyki są identyczne. Skoro natura wzorców jest tak ogólna, kopiowanie wzorców projektowych z kodu jednego programu i wklejanie ich do kodu innych programów jest niemożliwe (choć w niektó- rych przypadkach możesz ponownie wykorzystać określone rozwiązanie pod warunkiem, że bieżący kontekst jest podobny do oryginalnego kontekstu użycia tego rozwiązania). Ta fundamentalna zasada jest źródłem wielu nieporozumień wśród osób, które funkcjonują w świecie wzorców projektowych od niedawna. Komentarze, z którymi miałem okazję się zapoznać, przeglądając strony internetowe wskazują, że wielu programistów uważa, że jeśli jakaś książka nie zawiera tych samych przykładów co książka Bandy Czworga, dowodzi to braku kompetencji autora tej książki. Moim zdaniem, taka postawa dowodzi 18 Wzorce projektowe. Analiza kodu sposobem na ich poznanie raczej braku zrozumienia koncepcji wzorców projektowych po stronie autorów tego ro- dzaju opinii: mylą fragment kodu demonstrujący jakiś wzorzec z samym wzorcem. Mając to na uwadze, spróbuję zaprezentować różne przykłady dla każdego z omawianych wzor- ców, abyś mógł się przekonać, jak odmienne mogą być konkretne implementacje oparte na tym samym wzorcu — nie będę też wykorzystywał przykładów Bandy Czworga, chyba że będą dotyczyły rzeczywistych problemów związanych z programowaniem (ten warunek spełnia tylko niewielka ich część). Analizę wzorców projektowych dodatkowo komplikuje fakt, że rzeczywiste obiekty i klasy należące do poszczególnych wzorców niemal zawsze należą też do innych wzorców. Kiedy przyjrzysz się interesującemu Cię rozwiązaniu pod określonym kątem, dostrze- żesz jeden wzorzec; jednak kiedy spojrzysz na to samo rozwiązanie pod innym kątem, zobaczysz zupełnie inny wzorzec. Badania wzorców bywają jeszcze trudniejsze w sytu- acjach, gdy wiele implementacji wzorców opiera się na identycznych strukturach statycz- nych. Kiedy analizujesz przedstawione w książce Bandy Czworga diagramy struktury statycznej (zapisane w języku UML), wszystkie wyglądają bardzo podobnie — widać tam interfejs, klasę klienta i klasę implementacji. Różnic pomiędzy wzorcami należy szu- kać w dynamicznym zachowaniu systemu i w celach realizowanych przez programistę, a nigdy w klasach czy w sposobie ich łączenia. Spróbuję zilustrować wymienione problemy, posługując się przykładem ze świata tradycyjnej architektury budynków — skupię się na dwóch dziedzinach: wentylacji i oświetleniu. Jeśli chodzi o wentylację, nie chcę, by pokój robił wrażenie „dusznego”. Jeśli przeanali- zujemy rozwiązanie tego problemu w wielu naprawdę komfortowych pomieszczeniach, dostrzeżemy w nich jeden wzorzec, który będę nazywał wentylacją przecinającą. Po- mieszczenia należące do tego wzorca mają źródło i ujście powietrza ustawione naprze- ciwko siebie na dwóch przeciwległych ścianach, w dodatku na wysokości okien. Po- wietrze dostaje się do pomieszczenia poprzez źródło, po czym przechodzi przez całe to pomieszczenie i opuszcza je przez ujście. Kiedy już dysponowałem zidentyfikowanym (i nazwanym) wzorcem, stworzyłem krótki opis — nazywany przez Bandę Czworga in- tencją — który podsumowuje zarówno ogólny problem, jak i rozwiązanie wynikające z tego wzorca. W przypadku wentylacji przecinającej moim celem było „wyelimino- wanie poczucia duszności i zapewnienie wyższego komfortu przez umożliwienie bezpo- średniego przepływu powietrza w poziomie, na wysokości okien”. Mechanizmem archi- tektonicznym, który ten cel realizuje jest logiczna reifikacja (wyjaśnię to słowo za chwilę) wzorca. (Banda Czworga używa w tym kontekście słowa intencja, co jest dosyć dziwne. Sam nie będę się posługiwał tym słowem zbyt często, ponieważ w moim odczuciu znacznie lepszym słowem jest cel). Reifikacja to brzydkie, ale dość przydatne słowo w tym kontekście. Słowo to nie jest zbyt często stosowane w literaturze. Reifikacja znaczy dosłownie „materializację, nadanie czemuś określonej treści i formy”. Reifikacja koncepcji jest więc jej konkretną reali- zacją, a dla pojedynczej koncepcji mogą istnieć miliony możliwych reifikacji. Używam tego słowa (zamiast któregoś z bardziej popularnych wyrażeń), aby podkreślić czym nie jest wzorzec. Przykładowo, wzorzec nie jest „egzemplarzem” czy „instancją”. Każdy egzemplarz klasy jest identyczny (przynajmniej w sensie struktury) z wszystkimi pozostałymi egzemplarzami tej samej klasy. Z pewnością nie jest to cecha wzorca Rozdział 1. ♦ Wstęp: 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 możliwych (pra- widłowych) implementacji. Jakie więc są reifikacje wzorca wentylacji przecinającej? W pomieszczeniu mogą istnieć okna usytuowane naprzeciwko siebie, okno na wprost drzwi, dwoje drzwi naprzeciw siebie, okno na wprost wentylatora „ujemnego” (wyciągającego powietrze), dwa wen- tylatory (wejściowy i wyjściowy) zainstalowane na przeciwległych ścianach lub wielki miech (obsługiwany przez skaczącego na nim orangutana) zainstalowany przy jednej ze ścian i skierowany w stronę przeciwległej ściany z wybitym otworem. Tak naprawdę w ogóle nie są nam potrzebne ściany — pomieszczenie bez dwóch przeciwległych ścian doskonale pasuje do tego wzorca. Istnieje nieskończenie wiele reifikacji tego wzorca. Ponieważ reguły konstruowania reifikacji wzorca i tak są bardzo elastyczne, nie możesz wybierać tylko tych atrybutów, które uważasz za przydatne. Przykładowo, samo posia- danie wlotów i ujść powietrza nie jest warunkiem wystarczającym, jeśli nie są spełnione wymagania odnośnie do ich wysokości i usytuowania naprzeciwko siebie. Umieszcze- nie wlotu i wylotu powietrza np. na suficie nie będzie stanowiło prawidłowej reifikacji tego wzorca (co może potwierdzić każdy, kto pracuje w budynku z wielkimi przestrze- niami biurowymi z podwieszanymi wentylatorami). Podsumowując, celem wentylacji poprzecznej jest „wyeliminowanie uczucia duszności i zapewnienie większego komfortu przez umożliwienie poziomego przepływu powietrza w poprzek pokoju, na wysokości okien”. Uczestnicy tego wzorca (mogą to być okna, drzwi czy nawet orangutany) mają przypisane takie role jak źródło i ujście powietrza. Przejdźmy teraz do problemu oświetlenia. Po przeanalizowaniu wielu pomieszczeń stwier- dziłem, że najbardziej urokliwe są pokoje z oknami na dwóch przylegających ścianach. Dlatego też tak dużym powodzeniem cieszą się narożne gabinety — wielokierunkowe naturalne źródła światła czynią pomieszczenie znacznie przyjemniejszym. Zdecydo- wałem, że nazwę ten wzorzec projektowy biurem narożnym; cel wzorca zdefiniowałem w następujący sposób: „zapewnienie większego komfortu przez zastosowanie dwóch źródeł naturalnego światła na dwóch przylegających ścianach”. Także w tym przy- padku reifikacji wzorca jest nieskończenie wiele: okna na dwóch ścianach, okno na jednej ścianie i francuskie drzwi na drugiej, francuskie drzwi na dwóch ścianach itp. Mógłbyś teraz powiedzieć, że także rozwiązanie z oknem na jednej ścianie i lustrem na ścianie przylegającej jest reifikacją tego wzorca, ponieważ lustro odbijające naturalne światło można interpretować jak jego źródło. Cóż, gdybym był Billem Gatesem, mógłbym zali- czyć do tego wzorca także pomieszczenia z jednym oknem i zawieszonym na ścianie obok telewizorem plazmowym pokazującym obraz za tą ścianą, ale z pewnością nie byłaby to prawidłowa reifikacja tego wzorca, ponieważ telewizor plazmowy nie jest i nigdy nie będzie „naturalnym źródłem światła”. Istnieje oczywiście mnóstwo sposo- bów implementacji wzorców samych okien i francuskich drzwi. Przeanalizujmy teraz konkretny projekt — plan budynku. Na rysunku 1.1 przedstawiono reifikację wzorców projektowych wentylacji poprzecznej i biura narożnego (w ramach pojedynczego projektu). Widać tam zarówno diagram architektoniczny, jak i równoważny diagram UML. Wzorce są identyfikowane za pomocą symbolu współpracy języka UML w wersji 1.5. Nazwa wzorca jest zapisywana wewnątrz elipsy rysowanej przerywaną 20 Wzorce projektowe. Analiza kodu sposobem na ich poznanie Rysunek 1.1. Połączona reifikacja wentylacji poprzecznej i biura narożnego linią i rozszerzającej klasę mającą udział w danym wzorcu. Linie łączące wzorce z klasa- mi są oznaczane nazwami ról odgrywanych przez te klasy w odpowiednich wzorcach. Okno od strony południowo-zachodniej pełni funkcję wlotu powietrza dla wentylacji po- przecznej, natomiast drzwi naprzeciwko tego okna są ujściem powietrza. Dwa pozostałe okna nie odgrywają żadnej roli we wzorcu wentylacji poprzecznej, ponieważ są rozmiesz- czone na ścianach przylegających do ściany południowo-zachodniej. Skupmy się teraz na czymś innym — okna południowo-zachodnie i południowo-wschodnie należą do wzorca biura narożnego, ponieważ pełnią funkcję dwóch źródeł naturalnego światła. Ani drzwi, ani okno północno-zachodnie nie należą do tego wzorca projektowego, ponieważ nie stanowią wystarczających źródeł światła. Okno od strony południowo-zachodniej jest o tyle interesujące, że należy do dwóch wzorców projektowych jednocześnie — pełni funkcję „źródła powietrza” we wzorcu wentylacji poprzecznej oraz „źródła światła” we wzorcu biura narożnego. Obiekty i klasy występujące w rozmaitych wzorcach często wzajemnie się mieszają w podobny sposób. Kluczowe znaczenie ma zrozumienie, że identyfikacja wzorców nie jest możliwa wy- łącznie na podstawie analizowanej struktury. Przykładowo, wentylacja może być bloko- wana np. przez meble — wówczas żadne z okien nie będzie stanowiło źródła powietrza. Podobnie, jedno z okien może się znajdować metr od ściany sąsiedniego budynku lub łączyć gabinet z korytarzem — wówczas nie jest wystarczającym źródłem naturalnego światła (choć niewykluczone, że sprawdza się w roli źródła lub ujścia powietrza). Kiedy będziesz analizował rzeczywiste wzorce, przekonasz się, że do identyfikacji wzorca pro- jektowego w programie komputerowym niezbędna jest wiedza związana z kontekstem (włącznie ze znajomością zamierzeń architekta). Nie można identyfikować wzorców projektowych wyłącznie na podstawie diagramów UML. Musisz znać docelowe (a więc zgodne z zamierzeniami architekta) zastosowanie poszczególnych obiektów lub klas. W kolejnych rozdziałach będziesz miał okazję zapoznać się z wieloma przykładami tego niezwykłego zjawiska. Rozdział 1. ♦ Wstęp: programowanie obiektowe i wzorce projektowe 21 Wracając do problemu kopiowania i wklejania — mam nadzieję, że rozumiesz już reguły rządzące reifikacją wzorców i jesteś sobie w stanie wyobrazić ich stosowanie w roz- maitych projektach, z których każdy można by zaimplementować na mnóstwo sposo- bów. Twierdzenie, że można skopiować i wkleić wzorzec za pomocą odpowiedniego narzędzia projektowego jest pozbawione sensu. Niezależnie od tego wielu producen- tów obiektowych narzędzi CASE zachwala swoje programy jako zawierające bogate „biblioteki wzorców”, z których możesz wybierać gotowe wzorce i wstawiać je do swo- ich projektów. W praktyce biblioteki te zawierają jedynie gotowe struktury języka UML dla pojedynczych reifikacji wzorca zaprezentowanego w książce Bandy Czworga. Co prawda wklejanie niektórych spośród tych struktur czasami może być pewnym ułatwie- niem, jednak nie należy tej prostej operacji mylić z dużo bardziej wymagającym pro- cesem stosowania wzorca w projekcie. Dobry projekt niemal zawsze musi wykorzysty- wać własną reifikację, która jest właściwa tylko w określonym kontekście. Bezmyślne kopiowanie i wklejanie ma tyle wspólnego z projektowaniem, co zabawa w „malowanie liczbami” z rzeczywistym malowaniem. Czemu te wzorce mają w ogóle służyć? Skoro wzorce nie mają żadnego konkretnego kształtu, czemu tak naprawdę służą? Kiedy po raz pierwszy przeczytałem książkę Bandy Czworga, byłem rozczarowany. Po- czątkowo miałem wrażenie, że nie jest to nic innego jak pedagogiczna prezentacja mate- riału, który jest już doskonale znany wszystkim kompetentnym projektantom oprogra- mowania (bardzo często próbującym bezskutecznie poszukiwać eleganckich rozwiązań dla problemów, których dotyczą wzorce). Przyznaję, że gdybym przeczytał tę książkę kilka lat wcześniej, mój umysł być może nie byłby tak obciążony pewnymi wątpliwo- ściami i niechęcią do nowinek — zamieszanie wokół wzorców projektowych począt- kowo traktowałem jak szum wokół niczego. Myślałem w ten sposób do momentu, w którym musiałem omówić pewien projekt z in- nym projektantem. Mój rozmówca wskazał fragment projektu i powiedział: „Te interfejsy tworzą most łączący te dwa podsystemy; sam most jest implementowany za pomocą tego zbioru adapterów (zarządców) obiektów”. Byłem zażenowany tym, co się stało. Tym jednym zdaniem mój kolega prawdopodobnie oszczędził dobre pół godziny starannych wyjaśnień. Pomyślałem wtedy, że być może rzeczywiście w tych wzorcach projekto- wych kryje się jakaś korzyść. Niedługo potem udałem się na prezentację pierwszej wersji języka programowania Java, podczas której wszystkie składniki podsystemu AWT były opisywane właśnie na bazie odpowiednich wzorców. Prezentacja była krótka i jednocześnie zupełnie kla- rowna — w praktyce była dużo krótsza i prostsza od odpowiedniej prelekcji wygła- szanej przez osobę, która nie posługuje się wzorcami. Zanim przystąpiłem do pracy nad następnym projektem raz jeszcze przeczytałem książkę Bandy Czworga, po czym specjalnie próbowałem rozważać zadania mojego projektu pod kątem możliwości stosowania ewentualnych wzorców. Zacząłem sobie zadawać 22 Wzorce projektowe. Analiza kodu sposobem na ich poznanie pytanie: „Co tak naprawdę próbuję osiągnąć i czy istnieją jakieś wzorce, które mogłyby mi to zadanie ułatwić?” (do znajdowania właściwych wzorców wykorzystywałem te fragmenty dostępnych opisów, które były poświęcone ich przeznaczeniu). Kiedy odpo- wiedź brzmiała „tak”, bez zastanowienia używałem wybranego wzorca. Szybko odkry- łem, że takie podejście z jednej strony znacznie skraca czas projektowania, z drugiej strony przyczynia się do poprawy jakości projektu. Im lepiej znałem potrzebne wzorce, tym szybciej mogłem pracować nad kolejnymi projektami. Co więcej, moje początkowe wersje projektów wymagały znacznie mniejszej liczby poprawek niż projekty przygo- towywane w sposób tradycyjny. Złapałem bakcyla. Wzorce stanowią organizacyjny szkielet, który zasadniczo poprawia możliwości komu- nikacyjne, będące przecież w dłuższej perspektywie prawdziwym celem projektu. Dys- kusje, które poprzednio wymagały godzin, teraz — właśnie dzięki wzorcom — zajmują tylko kilka minut, co powoduje, że wszystkie osoby zaangażowane w prace nad projek- tem mogą realizować swoje zadania szybciej. Swego czasu uważnie czytałem wszystkie materiały, do których mogłem dotrzeć, i odkryłem, że książka Bandy Czworga doty- czyła jedynie warstwy wierzchniej tego głębokiego tematu. W internecie i w literaturze branżowej ukazywały się setki udokumentowanych wzorców projektowych, z których wiele miało zastosowanie w wykonywanej przeze mnie pracy. Z czasem doszedłem do przekonania, że właśnie solidna wiedza o wzorcach przydatnych w mojej pracy może być zasadniczym elementem decydującym o szybkości i jakości wykonywania zadań. (Przez „solidną” wiedzę o wzorcach rozumiem ich znajomość na pamięć, a więc taką, która nie będzie wymagała wertowania książek). Rola wzorców w procesie projektowania Kiedy wzorce zaczęły być wykorzystywane w procesach projektowania i jaka jest ich rola w tych procesach? Odpowiedź na to pytanie różni się w zależności od użytej meto- dyki (mam nadzieję, że używasz jakiejś metodyki), jednak zainteresowanie wzorcami projektowymi wynika przede wszystkim z codziennych problemów rozwiązywanych na poziomie implementacji, zatem źródeł popularności wzorców w procesach projekto- wych należy upatrywać w zbliżeniu warstw projektowania i implementacji. Ważniejsze jest inne pytanie: Kiedy kończy się analiza (która jako taka wiąże się z dziedziną pro- blemu) i rozpoczyna projektowanie (które z natury rzeczy dotyczy implementacji)? Najlepszą znaną mi analogią jest projektowanie i konstruowanie budynków. Plany budynków nie obejmują wszystkich szczegółów konstrukcyjnych. Najczęściej poka- zują rozmieszczenie murów, ale nie mówią o sposobie ich wznoszenia. Określają miej- sca instalacji wodno-kanalizacyjnej, ale nie definiują rozmieszczenia wszystkich rur. Dopiero w czasie konstruowania budynku projektowane są konkretne rozwiązania w zakresie wznoszenia murów i prowadzenia sieci wodno-kanalizacyjnej, jednak propo- nowane tam rozwiązania rzadko są w pełni realizowane, ponieważ proces implementacji Rozdział 1. ♦ Wstęp: programowanie obiektowe i wzorce projektowe 23 stawia przed budowniczymi konkretne problemy do rozwiązania (często nieprzewidy- walne w fazie przygotowywania projektu). Przykładowo, cieśla może zastosować określo- ny wzorzec „rozmieszczenia śrub i gwoździ”, który zagwarantuje odpowiednio mocną konstrukcję ściany. Projekt przeważnie mówi tylko o miejscu, w którym powinna sta- nąć ściana, nie wspomina jednak o sposobie jej konstruowania. Z analogiczną sytuacją mamy do czynienia w świecie oprogramowania: w przypadku większości przedsięwzięć programistycznych proces projektowania powinien się zakoń- czyć w punkcie, w którym dobry programista może bez trudu ten projekt zaimplemen- tować. Nie jestem w stanie wyobrazić sobie opisu techniki tworzenia okien w podsys- temie graficznego interfejsu użytkownika Swing zawartego w projekcie. Jest to jedno z tych zadań, które programista po prostu powinien potrafić zrealizować; a jeśli kod jest pisany z zachowaniem profesjonalnych standardów (a więc z uważnie dobranymi nazwami, właściwym 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ą szczegółowo opisywane w dokumentach two- rzonych na etapie projektowania — zamiast tego reprezentują decyzje podejmowane przez osoby odpowiedzialne za implementację. Wzorce stosowane przez te osoby nie- mal nigdy nie są dogłębnie dokumentowane, zwykle same nazwy elementów tych wzor- ców (lub inne komentarze) powinny w stopniu wystarczającym identyfikować podej- mowane działania. (Przykładowo, klasa WidgetFactory jest reifikacją wzorca klasy- wytwórni). Istnieją oczywiście wyjątki od reguły oddzielania wzorców od właściwych projektów. W świecie oprogramowania odpowiednik okien występujących we wzorcu narożnego biura może się pojawiać w dokumentach projektowych (które wskazują programistom miejsca rozmieszczenia okien). Podobnie, bardzo skomplikowane systemy, których pro- jekty wymagają stosowania znacznie bardziej szczegółowych opisów (tak jak plany ar- chitektoniczne dla drapacza chmur są dużo bardziej szczegółowe od planów dla domku jednorodzinnego), często już w wyniku fazy projektowania mają dogłębnie udokumen- towane wzorce projektowe. Napięcia pomiędzy wzorcami a prostotą Istotnym problemem są dodatkowe komplikacje wprowadzane do systemów właśnie w wyniku stosowania wzorców projektowych. Jak ślepa zgodność jest domeną ludzi nierozgarniętych, tak niepotrzebna złożoność jest domeną kiepskich programistów. „Mali politycy, filozofowie i duchowni” ponad wszystko cenią sobie zgodność, natomiast wielu „małych” programistów i architektów sądzi, że wzorce projektowe są ze wszech miar dobre, i że powinni ich używać wszędzie tam, gdzie nadarza się taka okazja. Takie bez- myślne podejście niemal zawsze prowadzi do powstawania kruchych, trudnych w kon- serwacji programów komputerowych. Każdy wzorzec ma przecież swoje wady, które przemawiają za tym, by go nie stosować. Proste systemy są łatwiejsze w budowie, łatwiejsze w konserwacji, mniejsze i szybsze od systemów skomplikowanych. Prosty system „maksymalizuje ilość wykonanej pracy” przez 24 Wzorce projektowe. Analiza kodu sposobem na ich poznanie zwiększanie (w porównaniu ze skomplikowanymi systemami) „ilości pracy, która nie jest wykonana niejako przy okazji”. Program musi przecież robić dokładnie to, czego oczekuje od niego użytkownik. Dodawanie nieproszonych funkcji dramatycznie wy- dłuża czas tworzenia systemu i jednocześnie obniża jego stabilność. Prostota zwykle nie jest celem łatwym do osiągnięcia. Programiści uwielbiają złożoność we wszelkich jej postaciach, zatem bardzo często wykazują silną skłonność do nadmier- nego komplikowania swojej pracy. Z punktu widzenia wielu programistów znacznie łatwiej jest szybko zbudować dosyć skomplikowany system niż zmusić się do później- szego upraszczania swojego dzieła. Programiści podejrzewający, że otrzymane wyma- gania będą z czasem rozwijane lub zmieniane mają tendencje do implementowania wy- magań, które mogą się pojawić w przyszłości. Komplikowanie kodu tylko dlatego, że ktoś nabrał mglistych podejrzeń o przyszłych zmianach jest jednak zupełnie niewłaściwe. (Za każdym razem, gdy próbuję przewidzieć przyszłość, popełniam zasadnicze błędy). Programiści powinni pisać kod w taki sposób, aby dodawanie nowych lub modyfiko- wanie istniejących funkcji było możliwie proste, co nie oznacza, że muszą od razu im- plementować całą funkcjonalność, jaka przychodzi im do głowy. Problemem może być także nadmierne uproszczenie rozwiązania, które z natury rzeczy powinno być skomplikowane. Programista, który naprawdę chce zrobić „dokładnie” to, co jest potrzebne (i w dążeniu do prostoty rezygnujący z wymaganej funkcjonal- ności), tworzy rozwiązanie równie złe, jak programista implementujący niepotrzebne funkcje. Przykładem nadmiernego uproszczenia jest popularna funkcja „cofnij”. Alan Cooper — twórca Visual Basica i ceniony specjalista od interfejsów użytkownika — argumentuje, że nigdy nie należy pytać użytkowników, czy naprawdę chcą coś robić. Pewnie, że chcą — przecież gdyby nie chcieli, nie żądaliby określonych funkcji! Ile razy zrezygnowałeś z usuwania pliku tylko dlatego, że na ekranie pojawiło się to głupkowate okno dialogowe? Najlepszym rozwiązaniem problemu przypadkowego usunięcia pliku (lub innych, równoważnych problemów) jest wykonanie żądania użytkownika i jedno- cześnie zapewnienie możliwości cofnięcia tej operacji, jeśli użytkownik popełni błąd. Funkcja cofania zmian jest obsługiwana np. przez większość edytorów tekstu. (Wyobra- żasz sobie, by edytor wyświetlał okno dialogowe z pytaniem: „Czy naprawdę chcesz usunąć ten znak?”). Funkcja cofania zmian jest jednak trudna do zaimplementowania, więc dosyć często można się spotkać z tłumaczeniem lenistwa koniecznością utrzy- mania prostoty oprogramowania. „Kompletny system cofania zmian wprowadza tyle dodatkowej złożoności, że znacznie lepszym rozwiązaniem wydaje się zastosowanie tradycyjnych okien dialogowych z potwierdzeniami”. Te trzy wymagania (prostota, kompletność i łatwość modyfikacji) niekiedy wzajemnie się wykluczają. Wzorce opisane w tej książce są ogromnym ułatwieniem między inny- mi wtedy, gdy konieczna jest zmiana lub dodanie jakiegoś elementu, jednak udogodnie- nia w tym względzie są okupione dodatkowym komplikowaniem kodu źródłowego. Niestety, nie istnieje jedna prosta reguła opisująca kiedy stosowanie wzorców jest dobre, a kiedy nie jest zalecane — ocena przydatności wzorców należy do stosujących je programistów. Duże znaczenie ma tutaj doświadczenie, którego wielu projektantów i programistów po prostu nie ma (a także — jak słusznie stwierdził Ken Arnold, współ- autor oryginalnej książki o programowaniu w Javie — zmysłu estetycznego, który także nie jest częstą cechą wśród programistów). Można więc pisać fatalne programy, które na każdym kroku wykorzystują wzorce projektowe. Samo stosowanie wzorców nie jest żadną gwarancją sukcesu w świecie oprogramowania. Rozdział 1. ♦ Wstęp: programowanie obiektowe i wzorce projektowe 25 Z drugiej strony, budowa w kodzie źródłowym bloków wzorców (np. przez częste wyko- rzystywanie interfejsów) zawsze jest rozwiązaniem korzystnym, nawet wtedy, gdy sto- sowanie w pełni rozwiniętych wzorców nie jest uzasadnione. Interfejsy nadmiernie nie komplikują kodu źródłowego, a ewentualny rozwój tego kodu w przyszłości będzie znacznie prostszy, jeśli system od początku będzie budowany na podstawie jasnej struk- tury interfejsów. Koszt takiego działania jest stosunkowo niski, natomiast potencjalne korzyści są bardzo duże. Klasyfikacja wzorców W niektórych sytuacjach przydaje się klasyfikacja wzorców, która ułatwia wybór wła- ściwych rozwiązań. W tabeli 1.1 (pochodzącej z książki Bandy Czworga) przedstawiono jeden ze sposobów podziału wzorców projektowych. Możesz jednak samodzielnie two- rzyć podobne tabele, w których będziesz dzielił wzorce na kategorie według dobra- nych przez siebie kryteriów. Tabela 1.1. Klasyfikacja wzorców projektowych według Bandy Czworga Cel Konstrukcyjne Strukturalne Klasa Metoda wytwórcza Adapter klas Z a k r e s O b i e k t Wytwórnia abstrakcji Budowniczy Prototyp Singleton Adapter obiektu Most Kompozyt Dekorator Fasada Waga piórkowa Pośrednik Czynnościowe Interpretator Metoda szablonowa Łańcuch odpowiedzialności Polecenie Iterator Mediator Memento Obserwator Stan Strategia Wizytator Banda Czworga podzieliła wzorce projektowe na dwa zakresy: wzorce klas wymagają reifikacji implementacji mechanizmu dziedziczenia (słowo kluczowe extends), nato- miast wzorce obiektów powinny być implementowane wyłącznie z wykorzystaniem mechanizmu dziedziczenia interfejsów (słowo kluczowe implements). To nie przypa- dek, że istnieje znacznie więcej wzorców klas niż wzorców obiektów. (Więcej infor- macji na ten temat znajdziesz w następnym rozdziale). W ramach tych dwóch zakresów wzorce są dalej dzielone na trzy kategorie. Wzorce konstrukcyjne dotyczą wyłącznie tworzenia obiektów. Przykładowo, wzorzec wytwórni abstrakcji zapewnia mechanizmy tworzenia obiektów bez znajomości klas, do których te nowe obiekty należą. (Na tym etapie trochę upraszczam rzeczywiste znaczenie tego wzor- ca, ale zagadnienia te szczegółowo wyjaśnię w dalszej części tej książki). Wszystkie 26 Wzorce projektowe. Analiza kodu sposobem na ich poznanie wzorce strukturalne należą do modelu statycznego — obejmują organizację strukturalną programu. Przykładowo, most opisuje sposób oddzielania od siebie dwóch podsyste- mów w taki sposób, aby każdy z nich mógł być modyfikowany bez konieczności zmian drugiego. Wszystkie wzorce czynnościowe należą do tzw. modelu dynamicznego, a więc obejmują techniki wzajemnego oddziaływania obiektów w czasie wykonywania progra- mu. Przykładowo, wzorzec łańcucha odpowiedzialności opisuje taki system przesyłania komunikatów pomiędzy obiektami, który umożliwia wypełnianie tych komunikatów danymi i ich przetwarzanie przez obiekty potrafiące tak przygotowane komunikaty wła- ściwie obsługiwać. Okazuje się, że jeszcze w czasie kompilacji nie musisz wiedzieć, które to obiekty; odpowiednie decyzje będą podejmowane dopiero w fazie wykonywa- nia programu. Wszystkie te wzorce omówię szczegółowo (choć w innej kolejności) w dalszej części tej książki, pamiętaj jednak, że istnieje wiele innych kategorii wzorców projektowych poza tymi, które zostały zidentyfikowane w książce Bandy Czworga. Wzorzec progra- mowania czasu rzeczywistego, wzorzec przetwarzania wielowątkowego czy wzorzec Enterprise JavaBean (EJB) Javy to tylko kilka z wielu przykładów. Kolejnym ważnym zagadnieniem są występujące pomiędzy wzorcami wzajemne zależ- ności. Przykładowo, podczas lektury dalszej części tej książki przekonasz się, że wzo- rzec polecenia w takiej czy innej formie występuje we wszystkich pozostałych wzorcach czynnościowych. W książce Bandy Czworga przedstawiono diagram pokazujący te rela- cje zależnościowe, ale, szczerze mówiąc, ich diagram jest na tyle zawiły, że nie ma zbyt dużej wartości praktycznej. Najważniejsze jest zapamiętanie, że rozmaite wzorce rzeczy- wiście są ze sobą powiązane, czasem w znacznym stopniu, czasem w sposób niejasny. Jeśli masz problem z odróżnieniem jednego wzorca od innego, z pewnością nie jesteś sam. Tego typu nieporozumienia najczęściej są wynikiem naturalnych zależności pomię- dzy wzorcami. W takich przypadkach radzę się skupiać na tych częściach opisu wzor- ców, które dotyczą ich celu (przeznaczenia) — pamiętaj, że każda reifikacja zgodna z zamierzeniami projektanta jest dopuszczalna. Sama analiza struktury (naturalna w przypadku programistów) często rodzi niepotrzebne nieporozumienia. Z pewnością odkryjesz, że np. wszystkie wzorce strukturalne mają niemal identyczne struktury sta- tyczne, choć szczegółowe efekty stosowania tych struktur są bardzo zróżnicowane. Struk- tury te dotyczą w równym stopniu komunikacji i oprogramowania, zatem nie należy się skupiać wyłącznie na analizie oprogramowania. O programowaniu, ogólnie Kolejnym ważnym zagadnieniem, które muszę przynajmniej ogólnie omówić jeszcze przed przystąpieniem do analizy samych wzorców jest projektowanie obiektowe (zorien- towane obiektowo). Po pierwsze, projektowanie obiektowe (ang. Object-Oriented Design — OOD) oraz pro- gramowanie obiektowe (ang. Object-Oriented Programming — OOP) to dwie zupełnie różne dziedziny. Proces projektowania rozpoczyna się od gromadzenia wymagań i wiąże się z systematycznym realizowaniem takich zadań jak analiza przypadków użycia — efektem tego procesu jest projekt, na podstawie którego programista może opracowywać Rozdział 1. ♦ Wstęp: programowanie obiektowe i wzorce projektowe 27 kod źródłowy programu. Proces programowania rozpoczyna się od analizy projektu lub jego części oraz stosowania takich rozwiązań jak wyprowadzanie struktur, hermetyzacja oraz wzorce projektowe, by ostatecznie stworzyć program komputerowy (będący reali- zacją otrzymanego na początku projektu). Wielu ludzi myli programowanie z projekto- waniem. To, że od sześciu lat programujesz w Javie, rozumiesz mechanizmy wydziela- nia podklas i potrafisz pisać 1000 linii testowanego na bieżąco kodu dziennie nie oznacza jeszcze, że znasz się na projektowaniu obiektowym. Istnieje nawet teoria, zgod- nie z którą wielu nawet najlepszych programistów nie rozumie podstawowych reguł projektowania obiektowego. Dobrą analogią jest branża budowlana. Budynki są projektowane przez architektów, ale budowane przez wykonawców. Systemy obiektowe są projektowane przez projektantów obiektowych i implementowane przez programistów obiektowych. Te dwie role mogą co prawda być łączone przez jedną osobę, ale takie rozwiązanie jest dosyć rzadkie. Archi- tekci muszą wiedzieć, jak konstruuje się budynki — w przeciwnym razie nie byliby w stanie opracowywać właściwych projektów. Z drugiej strony, wykonawcy w ogóle nie muszą rozumieć metod pracy architektów. (Nie twierdzę przy tym, że nie ma archi- tektów, którzy bardzo chętnie projektowaliby budynki nienadające się do budowy lub do zagospodarowania, lub że nie istnieją wykonawcy, którzy potrafią bez trudu iden- tyfikować kiepskie projekty). Najlepsi programiści są też dobrymi architektami, a najlepsi architekci są jednocześnie dobrymi programistami. To wymieszanie umiejętności jest szczególnie istotne w popularnych obecnie tzw. metodykach lekkich, zwinnych (ang. agile), które przewidują równoległe projektowanie i kodowanie. Żadna z tych metodyk nie uwzględnia nadrzędnej roli architekta, który pociąga za sznurki kierujące działania- mi programistów. Mówi się, że wielu programistów jest doświadczonymi rzemieślnikami, którzy produ- kują co prawda piękny kod, ale w ogóle nie rozumieją procesu projektowania — są bu- downiczymi, nie projektantami. Proszę, nie traktuj tego zdania jako przejawu mojej pogardy dla niesamowitych umiejętności budowniczych — chodzi tylko o to, że projekty przygotowywane ad hoc przez programistów są często dalekie od ideału. Ostatni raport zespołu Standish Group dotyczący tysięcy projektów programistycznych powstałych w ciągu wielu lat stwierdza, że około 72 procent tego typu przedsięwzięć zakończyło się niepowodzeniem. Za najważniejszą przyczynę tak wysokiego współ- czynnika nieudanych projektów uznano właśnie brak odpowiednich projektów i wszyst- kie jego następstwa (a więc np. niewłaściwa metodyka gromadzenia informacji o wy- maganiach). Oznacza to, że nawet doświadczeni architekci mogą popełniać błędy, jeśli zaniechają stosowania reguł rządzących procesami architektonicznymi. Ta książka jest poświęcona programowaniu obiektowemu i architekturom obiektowym, nie samym procesom planowania architektur. Wzorce projektowe dotyczą zwykle szcze- gółów implementacyjnych, które mają zastosowanie w pracy programistów obiekto- wych w czasie przekładania otrzymywanych projektów na kod źródłowy programów. Stworzenie dobrego projektu nie jest jednak możliwe bez odpowiednich procesów. (Z pewnością takimi procesami są rozwiązania proponowane w ramach metodyk zwinnych). Co więcej, nie jest możliwe tworzenie dobrego kodu bez właściwych pro- jektów (które dodatkowo mogą ewoluować w czasie). Proste stosowanie wzorców projektowych w kodzie (ad hoc, w nieprzemyślany sposób) z pewnością nie przyczyni 28 Wzorce projektowe. Analiza kodu sposobem na ich poznanie się do znacznego poprawienia jakości programu, a w skrajnych przypadkach może tę ja- kość dodatkowo obniżyć. Niepotrzebne komplikacje — a wiele wzorców projektowych jest skomplikowanych — niczego nie poprawią. Proszę więc, abyś nie mylił zagadnień omawianych w tej książce z procesem projekto- wania obiektowego jako całością. Wzorce są tylko niewielką częścią tej układanki — w niektórych przypadkach ich udział jest wręcz minimalny. Nie jest to książka o pro- jektowaniu obiektowym, tylko o przekształcaniu projektów obiektowych w konkretne implementacje. Aby jednak skutecznie stosować wzorce projektowe, musisz wiedzieć, jak projektować. Musisz znać proces projektowania. Na wspomnianej w przedmowie stronie internetowej wymieniłem wiele książek poświęconych projektowaniu i zalecam ich uważne przeczytanie. Programowanie języka FORTRAN w Javie Skoro niniejsza książka jest tak ściśle związana z zagadnieniami programowania obiek- towego, warto chyba poświęcić chwilę na omówienie różnic pomiędzy techniką obiek- tową a proceduralnym programowaniem systemów (przynamniej na poziomie struktu- ralnym). Proceduralne podejście do programowania można scharakteryzować jako „ukierunkowane na dane”, gdyż struktura programu proceduralnego koncentruje się wo- kół przepływu danych pomiędzy funkcjami (procedurami), które te dane przetwarzają lub sprawdzają. Centralnym elementem projektów tego typu programów zwykle jest baza danych; w praktyce bardzo wiele programów proceduralnych sprowadza się do pre- zentowania tabel bazy danych za pośrednictwem odpowiedniego interfejsu użytkownika. Systemy proceduralne są zwykle mocno zhierarchizowane, koncentrują się wokół poję- cia „globalnej kontroli”. Element globalny (mający zwykle postać funkcji lub procedury na szczycie tej hierarchii) przetwarza dane zebrane z innych źródeł — albo funkcji na niższych poziomach hierarchii, albo utworzonych wcześniej danych globalnych. Główną wadą systemów proceduralnych są utrudnienia w diagnostyce i konserwacji. Współdzie- lone dane tworzą relacje „sprzęgające” (niepożądane zależności) pomiędzy poszczegól- nymi funkcjami systemu. Kiedy zostanie zmieniona jedna funkcja, wprowadzona mody- fikacja będzie miała wpływ na działanie pozostałych. W skrajnych przypadkach efektem z pozoru banalnej zmiany w jednej z funkcji może być konieczność poświęcenia całych miesięcy na oczyszczenie i naprawienie kodu całego systemu. Z drugiej strony, systemy obiektowe są w istocie siatką współpracujących agentów, które wzajemnie się komunikują za pośrednictwem jakiegoś systemu przesyłania komunika- tów. Obiekty są równoważne — nie istnieje obiekt zwierzchni, który mógłby wydawać dyrektywy pozostałym obiektom. Co prawda właściwości dobrze zaprojektowanych obiektów będę szczegółowo omawiał w dalszej części tego rozdziału, warto jednak już teraz wprowadzić kilka najważniejszych zagadnień. Patrząc na obiekt z zewnątrz, nie powinniśmy mieć pojęcia o sposobie jego implementacji. Powinna więc istnieć możli- wość wymiany całej implementacji bez konieczności modyfikowania któregokolwiek z obiektów klienckich (obiektów wykorzystujących obiekt, który właśnie został zmie- niony). Mimo że obiekty czasami przekazują pomiędzy sobą inne obiekty, przepływ danych jest zupełnie inny niż w systemach proceduralnych. Obiekt pilnie strzeże swoich danych i wykonuje na nich operacje w odpowiedzi na otrzymywane komunikaty. Obiekty Rozdział 1. ♦ Wstęp: programowanie obiektowe i wzorce projektowe 29 nie przekazują danych do innych obiektów, chyba że jest to absolutnie konieczne (nawet wówczas przekazywane dane są hermetycznie zamykane w ramach tworzonych w tym celu obiektów). Te dwie koncepcje (ukrywanie implementacji i abstrakcja danych) w świe- cie systemów obiektowych mają kluczowe znaczenie. Dobrym sposobem odróżniania systemów obiektowych od systemów proceduralnych jest analiza efektów wprowadzanych zmian. W systemach proceduralnych wszelkie zmia- ny wpływają na pozostałe składniki programu, a duże zmiany w działaniu programu wy- magają zwykle bardzo głębokich i rozległych zmian w całym kodzie. W systemach obiek- towych niezbędne modyfikacje można wprowadzać tylko w wybranych punktach. Pojedyncza zmiana w kodzie systemu obiektowego może powodować znaczne zmiany w zachowaniu całego programu. Przykładowo, jeśli decydujesz się na zmianę formatu danych wykorzystywanego do przechowywania informacji, w przypadku systemu pro- ceduralnego musiałbyś zmienić kod w wielu miejscach, ponieważ dane są przetwarzane przez wiele procedur; w systemie obiektowym zmiany będą dotyczyły wyłącznie obiektu odpowiedzialnego za składowanie danych. Oczywiście takie reguły programowania obiektowego jak zapewnianie abstrakcji danych (ukrywanie sposobu dzi
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

Wzorce projektowe. Analiza kodu sposobem na ich poznanie
Autor:

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: