wzorce projektowe. elementy oprogramowania obiektowego wielokrotnego użytku pełna wersja.pdf

(12309 KB) Pobierz
887613711.001.png
8
SPIS TREŚCI
Rozdział 4.
Wzorce strukturalne .......................................................................................................... 139
ADAPTER (ADAPTER) ............................................................................................ 141
DEKORATOR (DECORATOR) ................................................................................ 152
FASADA (FACADE) ................................................................................................. 161
KOMPOZYT (COMPOSITE) .................................................................................... 170
MOST (BRIDGE) ....................................................................................................... 181
PEŁNOMOCNIK (PROXY) ....................................................................................... 191
PYŁEK (FLYWEIGHT) ............................................................................................. 201
Omówienie wzorców strukturalnych ............................................................................. 213
Rozdział 5.
Wzorce operacyjne ............................................................................................................ 215
INTERPRETER (INTERPRETER) ............................................................................ 217
ITERATOR (ITERATOR) .......................................................................................... 230
ŁAŃCUCH ZOBOWIĄZAŃ (CHAIN OF RESPONSIBILITY) ................................... 244
MEDIATOR (MEDIATOR) ....................................................................................... 254
METODA SZABLONOWA (TEMPLATE METHOD) ............................................ 264
OBSERWATOR (OBSERVER) ................................................................................. 269
ODWIEDZAJĄCY (VISITOR) .................................................................................. 280
PAMIĄTKA (MEMENTO) ........................................................................................ 294
POLECENIE (COMMAND) ...................................................................................... 302
STAN (STATE) .......................................................................................................... 312
STRATEGIA (STRATEGY) ...................................................................................... 321
Omówienie wzorców operacyjnych ............................................................................... 330
Rozdział 6.
Podsumowanie ................................................................................................................... 335
6.1. Czego można oczekiwać od wzorców projektowych? ......................................... 335
6.2. Krótka historia ............................................................................................................. 339
6.3. Społeczność związana ze wzorcami ......................................................................... 340
6.4. Zaproszenie .................................................................................................................. 342
6.5. Słowo na zakończenie ................................................................................................ 342
Dodatek A
Słowniczek .......................................................................................................................... 343
Dodatek B
Przewodnik po notacji ...................................................................................................... 347
B.1. Diagram klas ................................................................................................................ 347
B.2. Diagram obiektów ...................................................................................................... 349
B.3. Diagram interakcji ...................................................................................................... 350
Dodatek C
Klasy podstawowe ............................................................................................................. 351
C.1. List ................................................................................................................................. 351
C.2. Iterator .......................................................................................................................... 354
C.3. ListIterator ................................................................................................................... 354
C.4. Point .............................................................................................................................. 355
C.5. Rect ............................................................................................................................... 355
Bibliografia ......................................................................................................................... 357
Skorowidz ........................................................................................................................... 363
R OZDZIAŁ 3.
Wzorce konstrukcyjne
Konstrukcyjne wzorce projektowe pozwalają ująć w abstrakcyjnej formie proces tworzenia
egzemplarzy klas. Pomagają zachować niezależność systemu od sposobu tworzenia, składania
i reprezentowania obiektów. Klasowe wzorce konstrukcyjne są oparte na dziedziczeniu i służą
do modyfikowania klas, których egzemplarze są tworzone. W obiektowych wzorcach kon-
strukcyjnych tworzenie egzemplarzy jest delegowane do innego obiektu.
Wzorce konstrukcyjne zyskują na znaczeniu wraz z coraz częstszym zastępowaniem w syste-
mach dziedziczenia klas składaniem obiektów. Powoduje to, że programiści kładą mniejszy
nacisk na trwałe zapisywanie w kodzie określonego zestawu zachowań, a większy — na defi-
niowanie mniejszego zbioru podstawowych działań, które można połączyć w dowolną liczbę
bardziej złożonych zachowań. Dlatego tworzenie obiektów o określonych zachowaniach wy-
maga czegoś więcej niż prostego utworzenia egzemplarza klasy.
We wzorcach z tego rozdziału powtarzają się dwa motywy. Po pierwsze, wszystkie te wzorce
kapsułkują informacje o tym, z których klas konkretnych korzysta system. Po drugie, ukry-
wają proces tworzenia i składania egzemplarzy tych klas. System zna tylko interfejsy obiektów
zdefiniowane w klasach abstrakcyjnych. Oznacza to, że wzorce konstrukcyjne dają dużą ela-
styczność w zakresie tego, co jest tworzone, kto to robi, jak przebiega ten proces i kiedy ma miejsce.
Umożliwiają skonfigurowanie systemu z obiektami-produktami o bardzo zróżnicowanych
strukturach i funkcjach. Konfigurowanie może przebiegać statycznie (w czasie kompilacji) lub
dynamicznie (w czasie wykonywania programu).
Niektóre wzorce konstrukcyjne są dla siebie konkurencją. Na przykład w niektórych warunkach
można z pożytkiem zastosować zarówno wzorzec Prototyp (s. 120), jak i Fabryka abstrakcyjna
(s. 101). W innych przypadkach wzorce się uzupełniają. We wzorcu Budowniczy (s. 92) można
wykorzystać jeden z pozostałych wzorców do określenia, które komponenty zostaną zbudowane,
a do zaimplementowania wzorca Prototyp (s. 120) można użyć wzorca Singleton (s. 130).
Ponieważ wzorce konstrukcyjne są mocno powiązane ze sobą, przeanalizujemy całą ich piątkę
razem, aby podkreślić podobieństwa i różnice między nimi. Wykorzystamy też jeden przykład
do zilustrowania implementacji tych wzorców — tworzenie labiryntu na potrzeby gry kom-
puterowej. Labirynt i gra będą nieco odmienne w poszczególnych wzorcach. Czasem celem
gry będzie po prostu znalezienie wyjścia z labiryntu. W tej wersji gracz prawdopodobnie będzie
 
88
Rozdział 3. • WZORCE KONSTRUKCYJNE
widział tylko lokalny fragment labiryntu. Czasem w labiryntach trzeba będzie rozwiązać pro-
blemy i poradzić sobie z zagrożeniami. W tych odmianach można udostępnić mapę zbadanego
już fragmentu labiryntu.
Pominiemy wiele szczegółów dotyczących tego, co może znajdować się w labiryncie i czy gra
jest jedno-, czy wieloosobowa. Zamiast tego skoncentrujemy się na tworzeniu labiryntów.
Labirynt definiujemy jako zbiór pomieszczeń. Każde z nich ma informacje o sąsiadach. Mogą
to być następne pokoje, ściana lub drzwi do innego pomieszczenia.
Klasy Room , Door i Wall reprezentują komponenty labiryntu używane we wszystkich przykła-
dach. Definiujemy tylko fragmenty tych klas potrzebne do utworzenia labiryntu. Ignorujemy
graczy, operacje wyświetlania labiryntu i poruszania się po nim oraz inne ważne funkcje nie-
istotne przy generowaniu labiryntów.
Poniższy diagram ilustruje relacje między wspomnianymi klasami:
Każde pomieszczenie ma cztery strony. W implementacji w języku C++ do określania stron
północnej, południowej, wschodniej i zachodniej służy typ wyliczeniowy Direction :
enum Direction {North, South, East, West};
W implementacji w języku Smalltalk kierunki te są reprezentowane za pomocą odpowiednich
symboli.
MapSite to klasa abstrakcyjna wspólna dla wszystkich komponentów labiryntu. Aby uprościć
przykład, zdefiniowaliśmy w niej tylko jedną operację — Enter . Jej działanie zależy od tego,
gdzie gracz wchodzi. Jeśli jest to pomieszczenie, zmienia się lokalizacja gracza. Jeżeli są to drzwi,
mogą zajść dwa zdarzenia — jeśli są otwarte, gracz przejdzie do następnego pokoju, a o za-
mknięte drzwi użytkownik rozbije sobie nos.
class MapSite {
public:
virtual void Enter() = 0;
};
Enter to prosty podstawowy element bardziej złożonych operacji gry. Na przykład jeśli gracz
znajduje się w pomieszczeniu i zechce pójść na wschód, gra może ustalić, który obiekt MapSite
znajduje się w tym kierunku, i wywołać operację Enter tego obiektu. Operacja Enter specyficzna
887613711.002.png
WZORCE KONSTRUKCYJNE
89
dla podklasy określi, czy gracz zmienił lokalizację czy rozbił sobie nos. W prawdziwej grze
operacja Enter mogłaby przyjmować jako argument obiekt reprezentujący poruszającego się
gracza.
Room to podklasa konkretna klasy MapSite określająca kluczowe relacje między komponenta-
mi labiryntu. Przechowuje referencje do innych obiektów MapSite i numer pomieszczenia
(numery te służą do identyfikowania pokojów w labiryncie).
class Room : public MapSite {
public:
Room(int roomNo);
MapSite* GetSide(Direction) const;
void SetSide(Direction, MapSite*);
virtual void Enter();
private:
MapSite* _sides[4];
int _roomNumber;
};
Poniższe klasy reprezentują ścianę i drzwi umieszczone po dowolnej stronie pomieszczenia.
class Wall : public MapSite {
public:
Wall();
virtual void Enter();
};
class Door : public Mapsite {
public:
Door(Room* = 0, Room* = 0);
virtual void Enter();
Room* OtherSideFrom(Room*);
private:
Room* _room1;
Room* _room2;
bool _isOpen;
};
Potrzebne są informacje nie tylko o częściach labiryntu. Zdefiniujemy też klasę Maze repre-
zentującą kolekcję pomieszczeń. Klasa ta udostępnia operację RoomNo , która znajduje określony
pokój po otrzymaniu jego numeru.
class Mase {
public:
Maze();
void AddRoom(Room*);
Zgłoś jeśli naruszono regulamin