Co to są wzorce projektowe?

Report
Wzorce
projektowe
autor: Piotr Dziedzic
wzorce projektowe
1
Co to są
wzorce
projektowe?
Po co nam te
wzorce
projektowe?
Kto to
wymyślił?
wzorce projektowe
Podział
wzorców
projektowych
Omówienie
wzorców na
konkretnych
przykładach z
podziałem na
3 kategorie
wzorców
czyli……
2
Wzorce
kreacyjne
Wzorce
strukturalne
Wzorce
behawioralne
wzorce projektowe
Źródła
informacji na
temat
wzorców.
Podsumowanie
3
Wzorce projektowe w informatyce wywodzą się z wzorców
projektowych w architekturze, które zostały zaproponowane przez
amerykańskiego architekta Christophera Alexandra.
„ Wzorzec to sprawdzona koncepcja, która opisuje problem powtarzający się
wielokrotnie w określonym kontekście, działające na niego prawa, oraz podaje
istotę jego rozwiązania w sposób abstrakcyjny.”
Christopher Alexander
W kontekście architektonicznym (bardzo podobnie jest w informatyce),
każdy wzorzec powinien być opisany przez następujące atrybuty:
• układ sił działających na niego – czyli środowisko i jego wpływ;
• rozwiązanie – schemat konstrukcji, która uwzględnia działające siły,
równoważy je i oferuje najlepsze osiągnięcie ;
• kontekst - opis warunków, w których rozwiązanie można zastosować;
Taki wzorzec jest gotowym schematem postępowania, który można
zastosować w wielu sytuacjach, łącząc go także z innymi wzorcami.
wzorce projektowe
4
Sprawdzone w praktyce rozwiązanie często pojawiających się,
powtarzalnych problemów projektowych czyli to rozpowszechnione w
społeczności programistów danego języka (bądź ogólnie programistów)
rozwiązanie powszechnego problemu, sytuacji, z którą możemy się
spotkać w czasie projektowania aplikacji.
Wzorzec określa dany problem, prezentując jedno, lub więcej jego
rozwiązań. Pokazuje powiązania i zależności pomiędzy klasami oraz
obiektami i ułatwia tworzenie, modyfikację oraz pielęgnację kodu
źródłowego. Jest opisem rozwiązania, a nie jego implementacją.
Co warto podkreślić : Wzorce projektowe stosowane są w
projektach wykorzystujących programowanie obiektowe.
Wartość wzorców projektowych stanowi nie tylko samo
rozwiązanie problemu, ale także dokumentacja.
wzorce projektowe
5
• nazwy wzorca;
• problemu – opisuje sposoby rozpoznawania sytuacji, w których możemy
zastosować dany wzorzec oraz warunki jakie muszą zostać spełnione,
by jego zastosowanie miało sens;
• rozwiązania – opisuje elementy rozwiązania: ich relacje, powiązania oraz
obowiązki, zawiera także wskazówki implementacyjne dla różnych
technologii;
• konsekwencji – zestawienie wad i zalet stosowania wzorca,
uwzględniające informacje o jego brakach oraz kosztach rozwoju i
utrzymania systemu wykorzystującego dany wzorzec.
wzorce projektowe
6
• Nazwa wzorca oraz klasyfikacja: opisowa oraz unikalna nazwa,
umożliwiająca identyfikację oraz odwoływanie się do wzorca; klasyfikacja
według jednego ze schematów.
• Przeznaczenie: opis celu, który stoi za wzorcem oraz powody, jakimi
należy się kierować podczas jego wyboru.
• Inne nazwy: jeżeli istnieją inne, dobrze znane nazwy wzorca, należy je
podać.
• Motywacja: scenariusz zawierający problem powiązany z kontekstem, w
którym wzorzec może być stosowany.
• Stosowalność: sytuacje, w których wzorzec może być użyteczny.
wzorce projektowe
7
Struktura: graficzna reprezentacja wzorca, zwykle jako diagram klas lub
diagram interakcji.
Uczestnicy: lista klas i obiektów stosowanych w tym wzorcu oraz ich
zobowiązania.
Współpraca: opis wzajemnej interakcji klas i obiektów we wzorcu.
Konsekwencje: wykaz wyników, efektów ubocznych oraz kompromisów jakie
występują podczas użycia wzorca.
Implementacja: wskazówki dotyczące implementacji wzorca; zwrócenie
uwagi na specyficzne kwestie.
Przykładowy kod: przykład zastosowania wzorca z wykorzystaniem jednego z
języków programowania.
Przykłady zastosowania: znane przykłady zastosowania wzorca w
rzeczywistych programach.
Pokrewne wzorce: odniesienie wzorca do innych, z którymi wiąże się przez
wspólne stosowanie lub można go z nimi zamienić.
wzorce projektowe
8
Szczegółowiej:
Wzorce projektowe mogą przyspieszyć proces rozwoju
oprogramowania przez dostarczenie wypróbowanych rozwiązań dla
problemów, które mogą nie być oczywiste na początku procesu
projektowego.
Często zagadnienia te wiążą się z ewolucją oczekiwań względem
projektowanego systemu: rozszerzeniem jego funkcjonalności, zmianą
sposobu i formatu wprowadzanych danych czy dostosowaniem aplikacji
do różnych klas użytkowników. Nieuwzględnienie ich na początku procesu
rozwoju produktu programistycznego powoduje często konieczność
gruntownego przebudowywania zaawansowanego lub gotowego już
oprogramowania.
wzorce projektowe
9
Dokładniej - termin wzorca projektowego został wprowadzony do inżynierii
oprogramowania przez Kenta Becka oraz Warda Cunninghama w 1987 roku.
Natomiast w 1995 roku autorami pierwszej szeroko znanej publikacji
poświęconej wzorcom w inżynierii oprogramowania byli E. Gamma, R. Helm,
R.Johnson i J. Vlissides, znani jako Banda Czterech (Gang of Four). W swojej
książce opisali 24 wzorce projektowe dotyczące konstrukcji, struktury i
zachowania obiektów w systemach informatycznych. Ich zdaniem, poziom
abstrakcji wzorca projektowego powinien znajdować się powyżej poziomu
pojedynczej klasy.
wzorce projektowe
10
Od tego czasu wzorce projektowe stały się jednym z podstawowych
narzędzi projektowania systemów. Powstały nowe, specjalizowane
wzorce poświęcone rozwiązaniom dla konkretnych technologii czy
platform (np. wzorce dla J2EE).
Panowie z „gangu czterech” w jednej z swoich książek napisali
bardzo krótką, a zarazem ujmująca cała istotę sprawy definicję wzorca
projektowego:
„Wzorzec projektowy identyfikuje i opisuje pewną
abstrakcję, której poziom znajduje się powyżej
poziomu abstrakcji pojedynczej klasy, instancji
lub komponentu.”
wzorce projektowe
11
Wzorce
projektowe
Wzorce kreacyjne
(creational design
patterns)
Wzorce strukturalne
(structural design
patterns)
wzorce projektowe
Wzorce behawioralne/
czynnościowe (behavioral
design patterns)
12
• Metoda Wytwórcza (Factory Method),
Budowniczy (Builder), Fabryka Abstrakcyjna
(Abstract Factory), Prototyp (Prototype),
Singleton, Leniwa inicjalizacja (ang. lazy
initialization)
• Adapter, Dekorator (Decorator), Fasada
(Facade), Kompozyt (Composite), Most (Bridge),
Pełnomocnik (Proxy), Pyłek (Flyweight),
Kompozyt (Composite)
• Interpreter, Metoda Szablonowa (Template
Method), Iterator, Łańcuch Zobowiązań (Chain
of Responsibility), Mediator, Obserwator
(Observer), Odwiedziający (Visitor), Pamiątka
(Memento), Polecenie (Command), Stan (State),
Strategia (Strategy)
wzorce projektowe
13
SINGLETON
definicja
Według Bandy Czworga: „Zapewnienie, że klasa posiada tylko jedna instancje oraz dostarczenie
globalnego punktu dostępu do tej instancji”
Szczegółowiej: Singleton jest to jeden z kreacyjnych, obiektowych wzorców projektowych,
którego celem jest ograniczenie możliwości tworzenia obiektów danej klasy do jednej
instancji oraz zapewnienie globalnego dostępu do stworzonego obiektu. Niekiedy wzorzec
uogólnia się do przypadku wprowadzenia pewnej maksymalnej liczby obiektów, jakie mogą
istnieć w systemie. Niektórzy programiści uznają go za antywzorzec, ponieważ łamie zasady
projektowania obiektowego, często bywa nadużywany lub sprowadza się do stworzenia
obiektowego zamiennika dla zmiennej globalnej.
przeznaczenie
● do ograniczania możliwości tworzenia obiektów danej klasy do jednej instancji oraz
zapewnienie globalnego punktu dostępu do niej.
● używany w sytuacji, gdy istnieje potrzeba stworzenia klasy, która posiadałaby wyłącznie
jedną instancję
● użycie zamiast obiektu globalnego - nie zaśmiecany wtedy globalnej przestrzeni nazw
różnymi nazwami obiektów.
wzorce projektowe
14
SINGLETON - szczegóły
Struktura wzorca
Singleton implementuje się przez stworzenie klasy, która posiada statyczną metodę, która
najpierw sprawdza, czy istnieje już instancja tej klasy, w razie potrzeby tworząc ją. Następnie
instancja zwracana jest przez referencję. Instancję przechowuje się w prywatnym lub
chronionym, statycznym polu, do którego dostęp ma tylko opisana wyżej metoda, która jest
jedyną drogą pozyskania instancji obiektu singletonu – aby uniemożliwić tworzenie
dodatkowych instancji, konstruktor klasy deklaruje się jako prywatny lub chroniony.
Przykład konkretnego zastosowania
Rozważmy aplikację prowadzącą dla celów diagnostycznych dziennik zdarzeń.
Poszczególne komponenty dodają wpis do dziennika, przekazując mu jego treść, natomiast
dziennik określa, gdzie faktycznie zostanie on zapisany. Każdy komponent może uzyskać w
dowolnym momencie dostęp do dziennika, zatem musi on być dostępny globalnie. To
dziennik decyduje o tym, gdzie wpis zostanie faktycznie zapisany. Komponent musi jedynie
przekazać jego treść. Oznacza to istnienie pojedynczej instancji dziennika. Z dziennika
mogą również korzystać komponenty wielokrotnego użytku, zatem nie powinny one być
zależne od mechanizmów udostępniania zasobów specyficznych dla danej aplikacji.
Możemy to zapewnić, implementując w dzienniku wzorzec singleton tak, aby mógł on we
własnym zakresie zarządzać dostępem do siebie samego.
wzorce projektowe
15
SINGLETON – zalety i wady
zalety
● singleton nie musi ograniczać się do obsługi pojedynczej instancji klasy – przy niewielkiej
zmianie podejścia można za jego pomocą zarządzać także większą liczbą obiektów,
● klasa zaimplementowana z użyciem wzorca singleton może samodzielnie kontrolować
liczbę swoich instancji istniejących w systemie,
● proces pobierania instancji klasy jest niewidoczny dla użytkownika. Nie musi on wiedzieć,
czy w chwili wywołania metody instancja istnieje czy dopiero jest tworzona,
● tworzenie nowej instancji ma charakter leniwy, tj. zachodzi dopiero przy pierwszej próbie
użycia. Jeśli żaden komponent nie zdecyduje się korzystać z klasy, jej instancji nie będą
niepotrzebnie przydzielone zasoby.
wady
● brak elastyczności bo już na poziomie kodu jest na sztywno określona liczba instancji jakie
mogą istnieć w systemie;
● poważnie utrudnia testowanie aplikacji przez wprowadzenie do niej globalnego stanu łamie
zasadę jednej odpowiedzialności;
● łamie zasadę otwarte-zamknięte nie można go rozszerzyć;
wzorce projektowe
16
FABRYKA ABSTRAKCYJNA (ang. Abstract Factory)
definicja
Według Bandy Czworga: „Dostarczenie interfejsu służącego do tworzenia rodzin powiązanych ze sobą
obiektów bez konieczności wyspecyfikowania ich klas konkretnych”
Szczegółowiej: jest to jeden z kreacyjnych wzorców projektowych (obiektowy), którego celem
jest dostarczenie interfejsu do tworzenia różnych obiektów jednego typu (tej samej rodziny)
bez specyfikowania ich konkretnych klas. Umożliwia jednemu obiektowi tworzenie różnych,
powiązanych ze sobą, reprezentacji podobiektów określając ich typy podczas działania
programu. Fabryka abstrakcyjna różni się od Budowniczego, tym, że kładzie nacisk na
tworzenie produktów z konkretnej rodziny, a Budowniczy kładzie nacisk na sposób tworzenia
obiektów.
przeznaczenie
● Wzorzec fabryki abstrakcyjnej stosujemy, kiedy zachodzi potrzeba skoordynowania
procesów tworzenia rodzin obiektów.
● Wzorzec ten pozwala na wyodrębnienie reguł dotyczących tworzenia obiektów z obiektów
użytkownika, które będą wykorzystywać tworzone obiekty.
● Ułatwia tez przystosowywanie kodu źródłowego do pracy w zróżnicowanych środowiskach.
System tworzy nową unikatową konkretną fabrykę (która z kolei tworzy nowy unikatowy
produkt) dla każdego środowiska, ale ponieważ korzystasz w swoim kodzie wyłącznie z
interfejsów, zagwarantowanie właściwej współpracy konkretnymi środowiskami nie jest
twoim zmartwieniem.
wzorce projektowe
17
FABRYKA ABSTRAKCYJNA - szczegóły
Struktura wzorca
Jak widać na załączonym diagramie
klas wzorzec zbudowany jest z kilku
podstawowych klas. Klasa Fabryka
abstrakcyjna deklaruje abstrakcyjny
interfejs umożliwiający tworzenie
produktów. Interfejs ten jest
implementowany w Fabrykach
konkretnych, które odpowiedzialne są
za tworzenie konkretnych produktów.
Każda fabryka konkretnego produktu
posiada także metodę wytwórczą tego
produktu.
Przykłady konkretnych zastosowań (implementacji)
1) Przykładem może być fabryka elementów wizualnych GUI – okien, kursorów, ikon.
Produkty różnią się implementowanymi interfejsami (np. IWindow, ICursor, IImage) ale
nadal leżą w tym samym obszarze – GUI.
2) Rozpatrzmy aplikację kliencką, która łączy się ze zdalnym serwerem. Celem projektanta
takiej aplikacji jest to, aby była ona przenośna. Jednym z rozwiązań takiego problemu jest
stworzenie fabryki, która będzie tworzyła odpowiednie obiekty w zależności od tego na
jakiej platformie się znajduje.
wzorce projektowe
18
FABRYKA ABSTRAKCYJNA – zalety i wady
plusy
● Jednym z plusów wykorzystania wzorca jest możliwość ukrycia szczegółów
implementacyjnych klas reprezentujących konkretny produkt - klient widzi tylko interfejs.
Ukryciu ulegają także nazwy tych klas, co nie wymusza ich zapamiętywania i odizolowuje
klienta od problemu określenia do której klasy należy obiekt.
Do zysków należy także możliwość całkowitego ukrycia implementacji obiektów przed
klientem. Klient widzi tylko interfejs i nie ma możliwości zajrzenia do kodu oraz to, że
wymuszana jest spójność produktów.
minusy
● Do minusów należy zaliczyć trudność rozszerzania rodziny obiektów o nowe podobiekty.
Wymusza to modyfikację klasy fabryki abstrakcyjnej oraz wszystkich obiektów, które są
tworzone przez nią.
● Jeśli dany produkt nie realizuje powierzonych mu zadań zgodnie z oczekiwaniami, z reguły
jest się zmuszonym do zmiany interfejsu abstrakcyjnego produktu , co zwykle jest dosyć
trudne, ponieważ wymaga zmodyfikowania definicji wszystkich konkretnych produktów.
wzorce projektowe
19
BUDOWNICZY(ang. Builder)
definicja
Według Bandy Czworga: „Wzór budowniczy służy do utworzenia obiektów złożonych z części składowych,
które muszą zostać utworzone w tym samym celu i przy użyciu określonego algorytmu. Klasa kontroluje
zewnętrzny algorytm budowy”. Szczegółowiej: Budowniczy jest to jeden z kreacyjnych wzorców
projektowych (obiektowy), którego celem jest rozdzielenie sposobu tworzenia obiektów od ich
reprezentacji. Dzięki takiemu rozwiązaniu w tym samym procesie konstrukcyjnym możemy
tworzyć różne reprezentacje obiektów. Budowniczy różni się od wzorca fabryki abstrakcyjnej
oraz pozostałych wzorców kreacyjnych tym, że skupia się na sposobie tworzenia obiektów
reprezentujących produkty. Budowniczy tworzy drobną częśc skomplikowanego produkt za
każdym swoim wywołaniem jednoczesnie kontrolując stan wykonanej pracy. Klient dostaje
produkt po zakończeniu pracy Budowniczego a nie - tak jak w przypadku Fabryki abstrakcyjnej "od razu".
przeznaczenie
● Wzorzec budowniczego stosowany jest do oddzielenia sposobu tworzenia obiektów od tego jak
te obiekty mają wyglądać.
● Gdy chcemy sobie zagwarantować możliwość łatwego dodawania obsługi nowego formatu
danych wyjściowych, zapobiegając jednocześnie jakimkolwiek modyfikacjom klasy zarządzającej
obiektami budowniczymi.
● chcemy przekonwertować te same dane do kilku różnych postaci
wzorce projektowe
20
BUDOWNICZY- szczegóły
Struktura wzorca
Jak widać na przedstawionym diagramie klas
wzorzec składa się z dwóch podstawowych
obiektów. Pierwsza z nich oznaczony jest jako
Budowniczy - jego celem jest dostarczenie
interfejsu do tworzenia produktów. Drugim
obiektem jest obiekt oznaczony jako Konkretny
Budowniczy a jego celem jest tworzenie
konkretnych produktów korzystając z
interfejsu obiektu Budowniczy. Strukturę
wzorca uzupełnia obiekt Kierownika, wydaje on
polecenia konstrukcji produktów
wykorzystując do tego obiekt Budowniczego
Przykłady konkretnych zastosowań (implementacji)
1) Różne warianty omawianego wzorca wykorzystywane są w bibliotece MFC, implementując
architekturę dokument/widok. Obiekt klasy CDocument oraz jego podobiekty tworzone są
poprzez wywołanie metody tworzącej z trzema parametrami typu CRuntimeClass. Klasa ta
zawiera metodę CreateObject, umożliwiającą jej tworzenie obiektów różnych klas . Dzięki
takiemu zachowaniu CRuntimeClass może być nazwana klasą Budowniczego.
2) Przykładem może być też oprogramowanie konwertujące tekst z jednego formatu na drugi.
Algorytm odczytujący i interpretujący dane wejściowe jest oddzielony od algorytmu tworzącego
dane wyjściowe. Dzięki takiemu rozwiązaniu możemy stosować jeden obiekt odczytujący dane
wejściowe oraz wiele obiektów zapisujących je w różnych formatach (ASCII, HTML, RTF, itp.)
wzorce projektowe
21
BUDOWNICZY– zalety i wady
Plusy dodatnie
● Do plusów stosowania wzorca należą: duża możliwość zróżnicowania wewnętrznych
struktur klas oraz możliwość kontrolowania tworzenia obiektów po stronie klienta.
Stosowanie wzorca zapobiega także tworzeniu zduplikowanego kodu odpowiedzialnego za
tworzenie obiektów, gdyż proces tworzenia konkretnych elementów obiektów zamknięty jest
w poszczególnych procedurach.
duża skalowalność (dodawanie nowych reprezentacji obiektów jest uproszczone)
Plusy ujemne
● Minusem jest duża liczba obiektów reprezentujących konkretne produkty.
● Ewentualna zmiana interfejsu budowniczego wiąże się z koniecznością zmodyfikowania
wszystkich implementujących klas.
wzorce projektowe
22
PROTOTYP (ang. Prototype)
definicja
Wzorzec projektowych, którego celem jest umożliwienie tworzenia obiektów danej klasy bądź
klas z wykorzystaniem już istniejącego obiektu, zwanego prototypem. Głównym celem tego
wzorca jest uniezależnienie systemu od sposobu w jaki tworzone są w nim produkty.
przeznaczenie
Wzorzec Prototyp powinien być używany, gdy:
● system powinien być niezależny od tego, jak jego produkty są tworzone, składane i
reprezentowane;
● klasy, których egzemplarze należy tworzyć są specyfikowane w czasie wykonywania
programu, np. przez dynamiczne ładowanie.
● istnieje potrzeba uniknięcia budowania hierarchii klas fabryk, która jest porównywalna z
hierarchią klas produktów
● stan obiektów klasy może przyjmować tylko jedną z kilku różnych wartości; może być
wówczas wygodniej zainstalować odpowiednią liczbę prototypów i klonować je niż ręcznie
tworzyć egzemplarze klasy za każdym razem z odpowiednim stanem.
wzorce projektowe
23
PROTOTYP - szczegóły
Struktura wzorca
Wzorzec prototypu określa rodzaj obiektów do
tworzenia za pomocą prototypowej instancji.
Prototypy nowych produktów są często budowane
przed pełną produkcją, ale w tym przykładzie,
prototyp jest bierny i nie bierze udziału w
kopiowaniu siebie samego.
Prototyp - deklaruje interfejs klonowania się.
PrototypKonkretny - implementuje operację
klonowania się. Klient - tworzy nowy obiekt, prosząc
prototyp o sklonowanie się. Współpraca - Klient
prosi prototyp o sklonowanie się.
Przykłady konkretnych zastosowań (implementacji)
Projekty, które intensywnie używają wzorca Kompozytu i Dekoratora, mogą równie dobrze
odnieść korzyść ze stosowania Prototypu.
Praktyczną wskazówką na to, kiedy może być potrzebnym używanie metody clone(), jest
konieczność tworzenia prawdziwej kopii (ang. true copy) innej instancji w czasie wykonywania
programu. Prawdziwa kopia to taka, w której skopiowany obiekt ma wszystkie swe pola
identyczne z pierwowzorem. Gdy używa się operatora new, to wtedy te pola mają wartości
początkowe. Przykładowo, jeśli projektuje się system do przeprowadzania transakcji
bankowych, to wtedy potrzebna jest taka kopia obiektu, która przechowa dane konta, wykona
transakcje na tej kopii i zamieni tę kopię z oryginałem. W takim przypadku potrzebna jest raczej
metoda clone() niż operator new.
wzorce projektowe
24
PROTOTYP– zalety i wady
Plusy dodatnie
● Prototyp może skrócić czas tworzenia obiektów.
Można zainstalować nowy konkretny produkt w ramach wytwórni przez proste przekazanie
tej wytwórni prototypu ( już w czasie wykonania programu). Usuwanie produktów jest
równie łatwe.
Plusy ujemne
● Trzeba wprost interpretować metodę clone(), co w niektórych przypadkach może być dość
trudne. Należy też rozważyć problem głębokiej lub płytkiej kopii. (Czy Twoja kopia powinna
się sprowadzać do referencji, czy raczej należy klonować cały wskazany obiekt?) I wreszcie
metodę klonująca powinna niekiedy działać jak konstruktor –inicjalizować pewne pola
wartościami domyślnymi. Przykładowo, klon pola listy zwykle nie może się znaleźć na liście.
wzorce projektowe
25
Metoda wytwórcza (ang. factory method)
Definicja:
Jeden z kreacyjnych wzorców projektowych (klasowy), którego celem jest dostarczenie
interfejsu do tworzenia obiektów nieokreślonych jako powiązanych typów. Tworzeniem
egzemplarzy zajmują się podklas.
Ten wzorzec projektowy stosuje się gdy:
● klasa potomna ma decydować jaki obiekt ma zostać stworzony przez klasę bazową;
● nie da się określić jakie obiekty powinny być tworzone przez klasę bazową;
● chcemy ukryć implementację
Leniwa inicjalizacja (ang. lazy initialization)
Definicja:
Wzorzec projektowy, który opóźnia tworzenie obiektu do chwili w której następuje pierwsze
jego użycie. Niniejszy wzorzec jest ściśle powiązany ze wzorcem projektowym metoda
wytwórcza.
Zastosowanie:
● gdy chcemy zapewnić istnienie obiektu w chwili jego użycia;
● nie chcemy pamiętać o konieczności tworzenia obiektu.
wzorce projektowe
26
MOST (ang. Bridge)
definicja
Wzorzec projektowy, pozwalający na modyfikowanie implementacji oraz abstrakcji w czasie
działania programu. Interfejs zostaje całkowicie odizolowany od swojej implementacji. Dzięki
temu zyskujemy możliwość oddzielnego modyfikowania abstrakcji oraz oddzielnej modyfikacji
implementacji. Implementacja w opisie mostu oznacza obiekty, których używa klasa
abstrakcyjna i klasy jej pochodne (nie same klasy pochodne).
przeznaczenie
Według Bandy Czworga intencja mostu jest:
„Usunięcie powiązań pomiędzy abstrakcją i implementacją, aby obie mogły zmieniać się niezależnie” ,
czyli konsekwencją stosowania mostu jest umożliwienie odrębnej ewolucji Abstrakcji (np. poprzez dziedziczenie)
i jej implementacji – możemy np. łatwo dodać nową implementację do całej hierarchii klas dziedziczących po
Abstrakcji
Zaleca się stosowanie tego wzorca aby:
● odseparować implementację od interfejsu,
● poprawić możliwości rozbudowy klas, zarówno implementacji, jak i interfejsu (m.in. przez dziedziczenie),
● ukryć implementację od klienta, co umożliwia zmianę implementacji bez zmian interfejsu,
● do zapewnienia niezależności aplikacji od platformy.
wzorce projektowe
27
MOST - szczegóły
Struktura wzorca
Elementy tworzące strukturę tego wzorca to:
Abstrakcja: niezależny od podsystemu portal do
kodu właściwego dla danego systemu,
RozwiniętaAbstrakcja(Udoskonalona abstrakcja):
często pomijana wersja abstrakcji, którą
dostosowano do potrzeb konkretnej aplikacji,
Implementacja(Implementator): interfejs
wykorzystywany przez abstrakcję do
komunikowania się z implementacją właściwą dla
odpowiedniego podsystemu. Interfejs ten zwykle
pełni tez funkcję abstrakcyjnego produktu w
ramach wzorca wytwórni abstrakcji ,
ImplementacjeKonkretne( Konkretny
implementator): implementacja implementatora
właściwa dla danego podsystemu.
Przykład konkretnych zastosowań (implementacji)
Wyobraźmy sobie abstrakcję jaką jest figura. Można ją wyszczególnić na np. kwadraty, czy trójkąty,
jednak są pewne metody dla każdej figury jak np. rysowanie. Jednak rysowanie może być różne dla
różnych bibliotek graficznych czy systemów operacyjnych. Wzorzec mostu pozwala na stworzenie
nowych klas, które dostarczają konkretnych implementacji do rysowania. Klasa abstrakcyjna figury
dostarcza informacji o figurze (np. wielkość), podczas gdy implementacja dostarcza interfejs do
rysowania. Przydatny może być w sytuacji, gdy graficzny interfejs użytkownika (GUI) musi
wyglądać inaczej w zależności od posiadanego systemu operacyjnego. Zmiany w kodzie mają
charakter dynamiczny (wszystkie modyfikacje dokonywane są w trakcie działania programu).
wzorce projektowe
28
MOST– plusy i minusy
Plusy
● W czystym modelu dziedziczenia trzeba by korzystać z nadklasy implementującej pewne
zachowania oraz zbioru podklas, których zachowanie byłoby dostosowane do potrzeb
poszczególnych platform. We wzorcu projektowym mostu taka nadklasa jest w praktyce
zastępowana interfejsem, zatem w rozwiązaniach opartych na tej technice problemy
związanie z dziedziczeniem implementacji są zminimalizowane, a łączna liczba klas
ograniczona.
minusy
● Implementowanie interfejsów w taki sposób, aby każda implementacja zachowywała się tak
samo jest stosunkowo trudne. Most podsystemu AWT języka Java implementuje komponenty
okien dla różnych środowisk operacyjnych, ale np. implementacja dla systemu Motif
zachowuje się trochę inaczej niż implementacja dla systemu Windows.
wzorce projektowe
29
DEKORATOR (ang. Decorator)
definicja
Według Bandy Czworga intencją dekoratora jest :
„Dodanie nowych rodzajów odpowiedzialności do obiektu w dynamiczny sposób. Dekorator oferuje
rozwiązania alternatywne do tworzenia klas pochodnych w celu rozszerzenia funkcjonalności obiektu”.
Szczegółowiej: Jest to wzorzec, który pozwala na dodanie nowej funkcjonalności do
istniejących klas dynamicznie podczas działania programu. Wzorzec dekoratora polega na
opakowaniu oryginalnej klasy w nową klasę "dekorującą". Zwykle przekazuje się oryginalny
obiekt jako parametr konstruktora dekoratora, metody dekoratora wywołują metody
oryginalnego obiektu i dodatkowo implementują nową funkcjonalność.
przeznaczenie
● Dekoratory są alternatywą dla dziedziczenia. Dziedziczenie rozszerza zachowanie klasy w
trakcie kompilacji, w przeciwieństwie do dekoratorów, które rozszerzają klasy w czasie
działania programu.
● Do ulepszania i rozbudowywania obiektów. Załóżmy, że chcemy dodać do jakiejś klasy
metodę (niech jeszcze będzie to abstrakcja lub metoda wirtualna, a klasa ta będzie nadklasą
dla innych klas). Dzięki dekoratorowi nie musimy tutaj nic modyfikować, możemy nadać
obiektowi nowe zachowanie podczas działania programu.
● Dodatkowo wzorzec ten zapobiega przed eksplozją klas, spowodowaną dużą liczbą
rozszerzeń danej klasy (np. kombinacji rożnych składników na pizzy może być mnóstwo), czyli
dekorator ułatwia rozbijanie wielkich , złożonych operacji na operacje mniejsze, prostsze.
wzorce projektowe
30
DEKORATOR - szczegóły
Struktura wzorca
Elementy tworzące strukturę tego wzorca to:
Komponent: interfejs dla obiektów przystosowanych do przejmowania nowych zakresów
odpowiedzialności (lub do modyfikowania swojego zachowania) w czasie wykonywania
programu,
Konkretny komponent: obiekt, do którego są dołączone nowe zakresy odpowiedzialności lub
nowe zachowania,
Dekorator: opakowuje komponent i definiuje interfejs z jednej strony dostosowany do
interfejsu tego komponentu, ale jednocześnie zachowujący się w nieco inny sposób,
Konkretny dekorator: rozszerza dekorator celem zdefiniowania dodatkowych zachowań.
wzorce projektowe
31
Przykład konkretnego zastosowania (implementacji):
Rozważmy okno w graficznym interfejsie użytkownika (GUI):
By pozwolić na przewijanie jego zawartości, należy dodać do niego poziome lub pionowe
paski przewijania. Okna są reprezentowane przez instancje klasy Okno i klasa ta nie
powinna posiadać żadnych metod dodających paski przewijania. Można utworzyć podklasę
PrzewijaneOkno, która udostępniałaby te metody lub stworzyć OknoDekoratorPrzewijane,
który jedynie dodawałby tę funkcjonalność do istniejących obiektów Okno. W tym miejscu
działałyby oba rozwiązania.
Teraz załóżmy, że potrzeba dodać ramki dookoła okien. I w tym przypadku klasa Okno
nie ma takiej funkcjonalności. Pojawia się problem z podklasą OknoPrzewijane, bo aby
dodać ramki do wszystkich okien potrzeba stworzyć podklasy OknoZRamką i
OknoPrzewijaneZRamką. Problem staje się jeszcze większy z każdą kolejną opcją. W
przypadku dekoratorów wystarczy stworzyć jedną klasę OknoDekoratorRamka - podczas
działania programu można dynamicznie dekorować istniejące okna z
OknoDekoratorPrzewijane lub OknoDekoratorRamka lub oboma.
wzorce projektowe
32
DEKORATOR– zalety i wady
zalety
● Rozmiar i zależność hierarchii klas jest znacznie ograniczona
● Wysoka elastyczność (w odróżnieniu od dziedziczenia)
● Unikanie przeładowanych właściwościami klas na szczycie hierarchii
wady
● Funkcjonalność wprowadzana w ramach dekoratora (np. mechanizm zwracania znaków do
strumienia danych wejściowych) stwarza pewne trudności (lub nawet zagrożenia) w zakresie
uzyskiwania dostępu, jeśli dekorator sam jest dekorowany. W tego typu systemach
zasadnicze znaczenie ma kolejność stosowania dekoratorów. Przykładowo, klasa
PushbackInputStream języka Java zdaje egzamin w skrajnie zewnętrznej warstwie, ale już w
wersji opakowanej w ramach klasy BufferedInputStream po prostu nie działa (nie zwraca
znaków do bufora).
wzorce projektowe
33
FASADA-(ang. Facade)
definicja
Według Bandy Czworga :
„Dostarczenie jednolitego interfejsu do zbioru interfejsów pewnego podsystemu. Fasada definiuje bardziej
ogólny interfejs, ułatwiając w ten sposób wykorzystanie podsystemu”.
Fasada to wzorzec, który służy do ujednolicenia dostępu do złożonego systemu poprzez
wystawienie uproszczonego, uporządkowanego interfejsu programistycznego, który ułatwia
jego użycie. Fasada jest obiektem pośredniczącym między klasami klienta wywołującego
operacje a klasami, które te operacje wykonują.
przeznaczenie
Wzorzec fasady wykorzystuje się :
● aby ukryć złożoność tworzonego systemu przez dostarczenie udokumentowanego, publicznego API.
Definiujemy dozwolony dostęp do API przez co redukujemy ilość możliwych przypadków błędnego
użycia. Programiści muszą przyswoić sobie API fasady a nie całego systemu. Bardzo przydaje się to
wtedy, gdy biblioteka jest długo rozwijana i jest duża ilość przedawnionych metod. Fasada odświeża
API skupiając się jedynie na operacjach aktualnych.
● aby uprościć używanie cudzej biblioteki. Definiuje się wygodne, dostosowane do konkretnego
zastosowania metody pośredniczące między systemami. Zwiększa się czytelność kodu.
● ukryć implementację przed klientem, co umożliwia zmianę implementacji bez zmian interfejsu,
● aby poprawić użyteczność biblioteki ze źle skonstruowanym API przez stworzenie nakładki, która
dostarcza nowe, uporządkowane API.
wzorce projektowe
34
FASADA- szczegóły
Struktura wzorca
Wzorzec wyróżnia następujące elementy:
złożony system – reprezentowany na diagramie
przez klasy Element1 do Element5. Chcemy
uprościć dostęp do niego,
fasada – klasa posiadająca referencje do
elementów systemu z metodami do wykonywania
najczęściej potrzebnych zadań,
klient – dowolny kod zainteresowany
wykorzystaniem złożonego systemu.
Klient komunikuje się z systemem poprzez fasadę,
która w jego imieniu wykonuje niezbędne operacje
na złożonym systemie. To, czy klient posiada także
bezpośredni dostęp do systemu, leży w gestii
programisty implementującego wzorzec.
Przykład konkretnego zastosowania (implementacji)
Przykładem użycia wzorca fasady może być aplikacja bankomatowa, która musi wchodzić w
interakcję z systemem bankowym. Skoro aplikacja bankomatowa wykorzystuje tylko niewielką
część możliwości systemu bankowego (autoryzacja karty, sprawdzenie stanu konta, wypłata i ew.
wpłata), to można zastosować obiekt fasady, który zasłoni przed zewnętrznymi aplikacjami
skomplikowaną strukturę wewnętrzną systemu bankowego. Upraszcza to pisanie aplikacji na
bankomaty, a jednocześnie zapewnia lepsze bezpieczeństwo systemu bankowego.
wzorce projektowe
35
FASADA– plusy dodatnie i plusy ujemne
Plusy dodatnie
● Zmniejszenie liczby zależności między klientem, a złożonym systemem — jeśli klient nie
korzysta bezpośrednio z żadnych elementów ukrytego za fasadą systemu, całość jest
łatwiejsza w konserwacji i utrzymaniu.
● Wprowadzenie podziału aplikacji na warstwy, który ułatwia niezależny rozwój elementów
klienta i złożonego systemu.
● Możliwość zablokowania klientowi drogi do bezpośredniego korzystania ze złożonego
systemu, jeśli jest to konieczne.
● Kod klienta wykorzystującego fasadę jest czytelniejszy i łatwiejszy w zrozumieniu
Plusy ujemne
● Praktycznie nie ma wad. Za „wadę” można uznać to, że mimo zastosowania fasady
programista i tak może korzystać z klas podsystemu bezpośrednio.
wzorce projektowe
36
Adapter (ang. adapter )
Intencja Adaptera wg Bandy Czworga:
„Dostosowanie interfejsu klasy do interfejsu, którego oczekuje użytkownik. Adapter umożliwia współprace
klas, która bez jego zastosowania nie byłaby możliwa ze względu na ich niezgodne interfejsy.”
Szczegółowiej: Wzorzec Adapter konwertuje interfejs jednej klasy na interfejs innej klasy.
Używamy tego wzorca, jeśli chcemy, żeby dwie niezwiązane ze sobą klasy współpracowały ze
sobą w jednym programie. Koncepcja wzorca Adaptera jest bardzo prosta: piszemy klasę, która
posiada wymagany interfejs, a następnie zapewniamy jej komunikację z klasą, która ma inny
interfejs. Istnieją dwa sposoby realizacji: poprzez dziedziczenie i poprzez kompozycję.
Ten wzorzec projektowy stosuje się gdy:
● klasa nie może być użyta ponieważ ma niekompatybilny interfejs
● gdy nie mamy kodu źródłowego klas nie możemy zmienić ich interfejsu a nawet jeśli mamy kod
źródłowy klasy nie powinniśmy raczej zmieniać interfejsu klasy!
Kompozyt (ang Composite)
Definicja: Wzorzec projektowy, którego celem jest składanie obiektów w taki sposób, aby klient
widział wiele z nich jako pojedynczy obiekt. Pozwala traktować jednakowo obiekty jak i grupy
obiektów. Aplikacji dostarczają możliwość grupowania obiektów, jednak wykonanie operacji na
pojedynczych obiektach i ich kontenerach są różne. Kompozyt definiuje interfejs, który jest
wspólny zarówno dla pojedynczych jednostek jak i ich grup. W interfejsie tym są zdefiniowane
wszystkie metody charakterystyczne dla obu tych obiektów przez co są traktowane tak samo przez
klienta, ale implementacja tych metod jest różna w pojedynczych obiektach niż w grupach.
Zastosowanie: Wzorzec kompozyt wykorzystuje się najczęściej w sytuacji gdy chcemy
reprezentować całą hierarchię obiektów lub tylko część hierarchii), lub gdy chcemy aby klient nie
widział różnicy między pojedynczym obiektem a ich kolekcją. Klient będzie traktował wszystkie
obiekty jednakowo.
wzorce projektowe
37
Pełnomocnik (ang. Proxy)
Definicja:
Wzorzec projektowy, którego celem jest utworzenie obiektu zastępującego inny obiekt.
Stosowany jest w celu kontrolowanego tworzenia na żądanie kosztownych obiektów oraz
kontroli dostępu do nich.
Ten wzorzec projektowy stosuje się gdy:
● Pełnomocnik ma zastosowanie zawsze wtedy, kiedy potrzeba bardziej uniwersalnego lub
bardziej wyrafinowanego odwołania do obiektu niż zwykły wskaźnik.
● Istnieją cztery rodzaje tego wzorca, które jednocześnie definiują sytuacje, w których może
zostać użyty: wirtualny - przechowuje obiekty, których utworzenie jest kosztowne; tworzy je
na żądanie; ochraniający - kontroluje dostęp do obiektu sprawdzając, czy obiekt wywołujący
ma odpowiednie prawa do obiektu wywoływanego; zdalny - czasami nazywany ambasadorem;
reprezentuje obiekty znajdujące się w innej przestrzeni adresowej; sprytne odwołanie czasami nazywany sprytnym wskaźnikiem; pozwala na wykonanie dodatkowych akcji podczas
dostępu do obiektu, takich jak: zliczanie referencji do obiektu czy ładowanie obiektu do
pamięci.
wzorce projektowe
38
Pyłek (Waga piórkowa, ang. Flyweight)
Definicja:
Jest wzorcem strukturalnym, który rozszerza niejako wzorzec puli obiektów. Pula obiektów
przechowuje nierozróżnialne obiekty gotowe do użycia – niemożliwe jest przechowywanie w
nich informacji specyficznej. Pyłek stosowany jest do tworzenia bardzo dużej liczby
obiektów (niewiele się różniących lub identycznych), którymi można zarządzać w sposób
jednolity. Wzorzec tworzy jedyną instancję danego obiektu, którą następnie można
modyfikować – zmieniać wartości określonych jego atrybutów (tylko to, co uległo zmianie).
W ten sposób klient odczytuje dokładnie taki obiekt, jakiego oczekuje, nie powodując
zwiększenia zużycia zasobów. Efektem tego jest zmniejszenie zapotrzebowania aplikacji na
pamięć. Istotą tego wzorca jest podział danych przechowywanych w obiektach na dane
wewnętrzne (współdzielone) i zewnętrzne (unikatowe dla każdego obiektu). Dane
wewnętrzne nie są modyfikowane przy inicjacji obiektu, natomiast dane zewnętrzne są
dostarczane dla każdego obiektu z zewnątrz (np. poprzez odczyt z bazy danych) przed
przekazaniem do klienta.
Zastosowanie:
Wzorzec Pyłek stosuje się tam, gdzie klasa ma wiele egzemplarzy, a każdy z tych
egzemplarzy może być sterowany w ten sam sposób. Przykład zastosowania można znaleźć
w aplikacji wspomagającej modelowanie przestrzennego terenu. W takim muszą się znaleźć
obiekty reprezentujące drzewa. Nawet jeśli te obiekty będą bardzo proste (np. będą
zawierać tylko wysokość i współrzędne położenia drzewa), to przy modelowaniu wielkich
kompleksów zieleni, gdzie takich obiektów potrzebne będą ogromne ilości, aplikacja zacznie
działać bardzo wolno. Dlatego w tego typu aplikacjach stosuje się wzorzec Pyłek i zamiast
tworzyć wiele egzemplarzy drzew, tworzy się jeden obiekt, który przechowuje stany
(współrzędne, wysokość) wszystkich drzew w tablicy.
wzorce projektowe
39
METODA SZABLONOWA (ang. Template Method)
definicja
Według Bandy Czworga :„Zdefiniowanie ogólnego szkieletu używanego algorytmu i pozostawienie
implementacji niektórych jego kroków dla klas pochodnych. Dzięki temu uzyskuje się możliwość
modyfikacji niektórych kroków algorytmu bez konieczności zmieniania jego ogólnej struktury”.
Szczegółowiej: zadaniem tego wzorca jest zdefiniowanie metody będącej szkieletem algorytmu
– algorytm ten może być następnie dokładnie definiowany w klasach pochodnych. Niezmienna
część algorytmu zostaje opisana w metodzie szablonowej, której klient nie może nadpisać. W
metodzie szablonowej wywoływane są inne metody, reprezentujące zmienne kroki algorytmu.
Mogą one być abstrakcyjne lub definiować domyślne zachowania. Klient, który chce skorzystać
z algorytmu, może wykorzystać domyślną implementację bądź może utworzyć klasę pochodną i
nadpisać metody opisujące zmienne fragmenty algorytmu.
przeznaczenie
Wzorzec metody szablonowej wykorzystuje się :
● zwykle w szkieletach aplikacji opartych na mechanizmie dziedziczenia. Taki szkielet zawiera
zbiór nadklas odpowiadających za realizację około 90 procent wszystkich zadań, a jego celem
jest przeniesienie operacji właściwych dla konkretnej aplikacji na poziom metod abstrakcyjnych.
Oznacza to, że metody wspomnianych nadklas musza wywoływać abstrakcyjne metody
szablonowe. Z tak skonstruowanego szkieletu można korzystać przez zdefiniowanie klas
potomnych , które wprowadzają własną implementację zachowań określonej aplikacji przez
przykrycie odpowiednich metod szablonowych.
wzorce projektowe
40
METODA SZABLONOWA - szczegóły
Struktura wzorca
Wzorzec składa się z co najmniej dwóch klas.
Klasa podstawowa AbstractClass, definiuje szkielet
algorytmu i jest bazą z której korzysta klient. Składa
się z metody szablonowej templateMethod, która
jest publiczna i której klient nie ma możliwości
rozszerzyć oraz z metod prywatnych (lub
chronionych) method1 oraz method2, które są
wykorzystywane w metodzie templateMethod i
zawierają domyślną implementację algorytmu.
Klient chcący zmienić któryś z kroków algorytmu
musi zdefiniować klasę pochodną, ConcreteClass w
której przedefiniuje jedną bądź wszystkie prywatne
metody implementujące kolejne kroki algorytmu.
Przykład konkretnego zastosowania (implementacji)
Przykładem zastosowania tego wzorca są biblioteki wspomagające automatyzację testów
jednostkowych. Biblioteka jUnit definiuje ogólny algorytm wykonywania testów. Składa się on z
trzech podstawowych kroków: przygotowania środowiska do wykonania testów, wykonania
testów a następnie posprzątania po wykonanych testach. Kroki te reprezentowane są przez
metody setUp, runTest oraz tearDown. Wymienione metody wykonywane są w niezmiennej
kolejności przez metodę run, której klient nie może zmienić. Dzięki temu, użytkownik nie może
zmieniać kolejności podstawowych bloków algorytmu, może jednak nadpisać metody setUp,
runTest oraz tearDown, co pozwala mu dostosować testy do swoich potrzeb.
wzorce projektowe
41
METODA SZABLONOWA– zalety i wady
Plusy
● Jednym z najbardziej neutralnych zastosowań wzorca projektowego metody szablonowej
jest udostępnianie pustych „punktów zaczepienia” na poziomie nadklasy tylko po to, by
programista mógł w niej wstawiać swoją funkcjonalność właśnie za pośrednictwem
odpowiednich relacji dziedziczenia.
Minusy
● Stosowanie tego wzorca jest w większości przypadków nieuzasadnione, lepszym
rozwiązaniem jest użycie wzorca strategii. W systemach obiektowych stosowanie
mechanizmu dziedziczenia do modyfikowania zachowań nadklasy jest nie zalecane.
wzorce projektowe
42
ŁAŃCUCH ZOBOWIAZAŃ/ŁAŃCUCH
ODPOWIEDZIALNOŚCI (ang. Chain of Responsibility)
definicja
Wzorzec projektowy, który automatycznie przekazuje odpowiedzialność za wykonanie zadania
do następnika, jeżeli obecny obiekt nie potrafi obsłużyć otrzymanego zadania. W przypadku gdy
następnik nie istnieje - zadanie zostaje odrzucone. Łańcuch zobowiązań można wyobrazić sobie
jako listę obiektów obsługujących, którą przechodzimy dopóki nie znajdziemy obiektu, który
będzie w stanie obsłużyć żądane zadanie. Łańcuch zobowiązań prowadzi do utworzenia
łańcucha obiektów, które analizując żądanie zapewniają obsługę lub przekazują je dalej.
Odpowiedzialność za przetworzenie żądania spada na obiekt, który jest do tego zadania
najlepiej przygotowany.
przeznaczenie
Wzorzec znajduje zastosowanie wszędzie tam, gdzie mamy do czynienia z różnymi
mechanizmami podobnych żądań, które można zaklasyfikować do różnych kategorii. Dodatkową
motywacją do jego użycia są często zmieniające się wymagania., czyli wzorzec łańcucha
zobowiązań stosujemy, gdy:
● więcej niż jeden obiekt może obsłużyć żądanie ;
● przesyłamy żądania do jednego z wielu odbiorców bez bezpośredniego specyfikowania
odbiorcy;
● zbiór obiektów mogących obsłużyć żądanie jest definiowany automatycznie;
wzorce projektowe
43
ŁAŃCUCH ZOBOWIAZAŃ/ŁAŃCUCH
ODPOWIEDZIALNOŚCI - szczegóły
Struktura wzorca
Struktura tego wzorca jest bardzo prosta: obiekty typu Handler są powiązane ze sobą w postaci
jednokierunkowej kolejki (albo łańcucha). Nadchodzące od klienta żądanie jest przekazywane
wzdłuż tego łańcucha, gdzie każdy obiekt typu Handler ma szansę na ich obsłużenie. Co ważne,
obiekty typu Handler są od siebie niezależne, tzn. nie wiedzą o sobie nic (poza abstrakcyjnym
wskazaniem na obiekt następnika).
UCZESTNICY:
Handler - definiuje interfejs do obsługi żądań
Concrete Handler - obsługuje jeden rodzaj żądania, pozostałe przekazuje do następnika w
łańcuchu, posiada referencję typu Handler do następnika
Client - inicjuje przetwarzanie, przekazując żądanie do pierwszego obiektu Handler w łańcuchu
wzorce projektowe
44
Przykład konkretnego zastosowania (implementacji)
Obiekt Inbox wywołuje pierwszy obiekt Filtr w łańcuchu. Kolejne filtry przekazują sobie
sterowanie Prostym przykładem tego wzorca jest np. mechanizm filtrów obecnych w większości
klientów poczty elektronicznej. Wiadomość przychodząca do foldera Inbox jest przesyłana przez
łańcuch zdefiniowanych przez użytkownika filtrów: każdy z nich może dokonać pewnej akcji na
wiadomości, polegającej na przeniesieniu jej do innego foldera, zmianie jej priorytetu czy
usunięciu jej. Każdy filtr podejmuje decyzję (poprzez wywołanie metody isEligible()), czy
konkretna wiadomość powinna być przez niego obsłużona, i przekazuje sterowanie dalej.
wzorce projektowe
45
ŁAŃCUCH ZOBOWIAZAŃ/ŁAŃCUCH ODPOWIEDZIALNOŚCI
– plusy i minusy
Plusy
● elementy łańcucha mogą być dynamicznie dodawane i usuwane w trakcie działania
programu.
● zmniejszenie liczby zależności między nadawcą, a odbiorcami,
● implementacja pojedynczej procedury nie musi znać struktury łańcucha oraz innych
procedur.
● Daje elastyczność w rozdzielaniu odpowiedzialności pomiędzy obiekty.
Minusy
● wzorzec nie gwarantuje, że każde żądanie zostanie obsłużone.
● śledzenie i debugowanie pracy działania łańcucha może być trudne.
wzorce projektowe
46
STRATEGIA (ang.Strategy)
definicja
Według Bandy Czworga intencją wzorca strategii jest :„Zdefiniowanie rodziny algorytmópw,
zastosowanie hermetyzacji dla każdego z nich i stosowanie ich wymiennie. Strategia pozwala zmieniać
algorytmy niezależnie od wykorzystujących je obiektów.”
Szczegółowiej: to jeden z czynnościowych wzorców projektowych, który definiuje rodzinę
wymiennych algorytmów i kapsułkuje je w postaci klas. Umożliwia wymienne stosowanie
każdego z nich w trakcie działania aplikacji niezależnie od korzystających z nich klientów.
Hermetyzacja pojedynczego algorytmu przetwarzania w obiekcie strategii zamiast definiowania
wszystkich możliwych sposobów przetwarzania w jednym miejscu i wybieraniu odpowiedniego
za pomocą długich instrukcji warunkowych. Wzorzec Strategy służy zatem do modelowania
algorytmu realizacji pewnej czynności, który może zostać określony i zmieniony w trakcie
wykonywania programu.
przeznaczenie
• Często okazuje się, że dziedziczenie jest niewystarczającym sposobem tworzenia
oprogramowania. Wtedy z pomocy przychodzi inne techniki projektowania aplikacji np. wzorzec
strategii. Strategia definiuje wspólny interfejs dla każdego obsługiwanego algorytmu. Wymusza
to na nas stworzenie implementacji obsługiwanych funkcji w klasach dostarczających konkretne
algorytmy. Zawsze przed zastosowaniem dziedziczenia warto pomyśleć, czy wzorzec strategii
nie będzie lepszym rozwiązaniem. Można jej używać gdy: •Potrzebuje się kilku wariantów
pewnego algorytmu. • Algorytm powinien kapsułkować i hermetyzować dane o których klient
niekoniecznie powinien wiedzieć • Klasa posiada wiele różnych zachowań
zaimplementowanych za pomocą drabinki konstrukcji warunkowych. • Powiązane ze sobą klasy
różnią się tylko zachowaniem.
wzorce projektowe
47
STRATEGIA- szczegóły
Struktura wzorca
Elementy wzorca:
Strategia — interfejs definiujący operacje, które muszą
obsługiwać wszystkie dostępne algorytmy. Zakładamy,
że wszyscy klienci zainteresowani wykorzystaniem
algorytmów będą używać właśnie tego interfejsu.
Konkretna strategia —implementuje określony algorytm
zgodnie ze zdefiniowanym interfejsem. Klient —
użytkownik rodziny algorytmów posiadający referencję
do obiektu Strategia. Istotne jest, że obiekty Klient oraz
Strategia współpracują ze sobą w celu wykonania
określonego zadania. Klient wykonuje wszystkie ogólne
zadania i nadzoruje przepływ sterowania, zaś strategie
implementują te części zadania, które można
wymieniać.
Przykłady konkretnych zastosowań (implementacji)
1) Oficjalna wirtualna maszyna Javy HotSpot wykorzystuje wzorzec Strategia w wewnętrznej
implementacji mechanizmu odśmiecania pamięci, oferując do wyboru kilka algorytmów
różniących się właściwościami. Programista wybiera strategię odśmiecania najlepiej
dopasowaną do profilu jego aplikacji.
2) Innym miejscem zastosowania wzorca jest sytuacja, w której poszczególne strategie
rozwiązują inny problem. Za ilustrację może posłużyć tutaj sklep internetowy posiadający
swoje oddziały w kilku krajach różniących się obowiązującymi w nich przepisami
podatkowymi. Klient implementuje podstawową, wspólną dla wszystkich funkcjonalność, zaś
operację naliczenia podatku deleguje do strategii zaimplementowanej dla konkretnego kraju.
wzorce projektowe
48
STRATEGIA– zalety i wady
Zalety
● wzorzec pozwala na ścisłe, formalne zdefiniowanie rozszerzalnych rodzin algorytmów
dzięki wprowadzeniu interfejsu .
● bazuje na koncepcji kompozycji, a nie na dziedziczeniu — nie ma sztywnego powiązania
między algorytmem, a miejscem jego wykorzystania. Może on być wymieniany w trakcie
działania programu,
● eliminacja instrukcji warunkowych,
● umożliwia wybór implementacji — algorytmy mogą rozwiązywać ten sam problem, lecz
różnić się uzyskiwanymi korzyściami (zużycie pamięci, złożoność obliczeniowa,
optymalizacja pod kątem pewnych szczególnych przypadków).
● możliwość niezależnego testowania klientów i strategii.
wady
● dodatkowy koszt komunikacji między klientem, a strategią (wywołania metod,
przekazywanie danych),
● zwiększenie liczby obiektów.
wzorce projektowe
49
ODWIEDZAJĄCY/WIZYTATOR
(ang.Visitor)
definicja
Jest to wzorzec projektowy, którego zadaniem jest odseparowanie algorytmu od struktury
obiektowej na której operuje. Praktycznym rezultatem tego odseparowania jest możliwość
dodawania nowych operacji do aktualnych struktur obiektów bez konieczności ich modyfikacji.
We wzorcu projektowym wprowadzony zostaje nowy typ obiektu Wizytator, którego zadaniem
jest "odwiedzenie" każdego elementu w danej strukturze obiektów i wykonanie na nim
konkretnych działań. Różne implementacje wizytatorów, mogą wykonywać różne zadania,
rozszerzając funkcjonalność struktury elementów, bez ich wewnętrznej modyfikacji.
przeznaczenie
Wizytator można stosować, gdy:
• Struktura, którą posiadasz składa się z dużej ilości klas obiektów, z różnymi interfejsami, a Ty
potrzebujesz wykonać operacje zależne od tych konkretnych klas.
• Wiele różnych operacji niepowiązanych ze sobą musi zostać wykonanych na obiektach
złożonej struktury obiektowej.
• Klasy definiujące strukturę obiektową rzadko się zmieniają, a Ty często potrzebujesz dopisać
nowe operacje na tej strukturze.
• W wszelkiego rodzaju statystykach i raportach budowanych na podstawie dużych struktur
danych (stabilnych!) .
• W obsłudze zamówień seryjnych .
wzorce projektowe
50
ODWIEDZAJĄCY/WIZYTATOR- szczegóły
Struktura wzorca
Elementy wzorca:
Wizytator – deklaruje interfejs dla każdej klasy elementówKonkretnych.
WizytatorKonkretny – implementuje interfejsy zadeklarowane przez wizytatora.
Element – deklaruje interfejs przyjmij, otrzymującą jako argument wizytatora.
ElementKonkretny – implementuje operację przyjmij, a także inne swoje działania.
Struktura – posiada swoje elementy, powinna zapewniać odwiedzającemu interfejs
umożliwiający odwiedzanie elementów .
wzorce projektowe
51
Przykłady konkretnych zastosowań (implementacji)
Zadanie: Odwiedź wszystkie pola szachownicy ruchem skoczka szachowego, odwiedzając
każde pole tylko raz, zaczynając z a) rogu, b) jednego z pól środkowych, c) jednego z pól
na brzegu, ale nie rogu.
Rozwiązanie: Osoba (czyli Klient) sterująca skoczkiem, zaczynającym z odpowiedniego
miejsca szachownicy (czyli Odwiedzający), wykonuje ruchy skoczkiem po polach
szachownicy (czyli Struktura Obiektów). W zależności od pola na których się znajdzie
(czyli Elemencie Struktury) może wykonać zaakceptowane kroki – wycofać ruch (brak
możliwości ruchu dalej, a są pola jeszcze bez odwiedzin), przejść do kolejnego wolnego
pola, zakończyć przejście (wszystkie pola zostały odwiedzone).
wzorce projektowe
52
ODWIEDZAJĄCY/WIZYTATOR– zalety i wady
Zalety
● łatwe dodawanie nowych operacji,
● dodawanie nowych operacji bez modyfikacji samej struktury danych,
● kod realizujący operację zostaje skupiony w jednym miejscu,
● niezależne odwiedzanie całej hierarchii klas,
● kumulowanie stanu
Wady
● załamanie hermetyzacji,
● utrudnione zmiany w strukturze np. dodawanie nowych klas
wzorce projektowe
53
Pamiątka (ang. memento)
Definicja:
Wzorzec, którego zadaniem jest zapamiętanie i udostępnienie na zewnątrz wewnętrznego
stanu obiektu bez naruszania hermetyzacji. Umożliwia to przywracanie zapamiętanego stanu
obiektu.
Potrzeba zaimplementowania takiej funkcjonalności pojawia się dość często. Memento nie
ma na celu zarządzania stanem ale tylko umożliwienie bezpiecznego dostępu do niego i
równie bezpiecznego odtworzenia.
Wzorzec pamiątki stosujemy gdy:
Chcemy przechowywać stany obiektów i mieć możliwość ich przywrócenia w dowolnym
momencie; Chcemy zachować hermetyzację obiektu, którego stan ma być zapamiętywany.
Pamiątka może zostać wykorzystana w procesorze tekstu do zaimplementowania operacji
"Cofnij" oraz "Ponów". Za każdym razem kiedy użytkownik wykonuje jakąś akcję –
wprowadza tekst, zmienia wielkość czcionki czy jej kolor – tworzony jest obiekt pamiątki
zapamiętujący bieżący stan dokumentu. Gdy użytkownik zleci wycofanie ostatniej operacji,
stan dokumentu zostanie odtworzony za pomocą wcześniej zapisanej pamiątki.
Inny przykład zastosowania tego wzorca projektowego to ziarno generatora liczb
pseudolosowych czy pojedynczy stan automatu skończonego.
wzorce projektowe
54
Mediator (ang. mediator
)
Definicja: to wzorzec, umożliwiający zmniejszenie liczby powiązań między różnymi klasami, poprzez
utworzenie mediatora będącego jedyną klasą, która dokładnie zna metody wszystkich innych klas, którymi
zarządza. Nie muszą one nic o sobie wiedzieć, jedynie przekazują polecenia mediatorowi, a ten rozsyła je do
odpowiednich obiektów.
Zastosowanie: Jest to wzorzec wykorzystywany do skupiania złożonych procedur komunikacji i sterowania
w środowisku powiązanych obiektów. Obiekty w systemie zamiast komunikować się między sobą
bezpośrednio robią to poprzez klasę Mediatora – nie muszą wtedy wiedzieć o swoim własnym istnieniu
bezpośrednio. Wysyłają informację do mediatora, a on przekaże go do obiektu, który ma być celem żądania.
Pozwala to na łatwą przyszłą modyfikację aplikacji, ponieważ wszystkie wspólne relacje są w jednym
miejscu.
Obserwator (ang. observer
)
Definicja: to wzorzec z rodziny wzorców operacyjnych. Składa się z obiektu, który nazywamy "obiektem
obserwowanym" oraz pewnej liczy obiektów obserwujących. Występuje tutaj relacja typu jeden do wielu.
Obiekt obserwowany jest zarządcą danych, informuje on wszystkich swoich obserwatorów o zmianach w
danych, które zawiera. Jest on jedynym prawowitym właścicielem owych danych. Gdy obserwatorzy
dostają informacje, że dane uległy zmianie, pobierają je od obiektu obserwowanego i dokonują aktualizacji
w posiadanych przez siebie danych, pod kątem stanu danych obiektu obserwowanego. Obiekt obserwujący
sam może zdecydować, czy dalej chce obserwować, ale również obiekt obserwowany może go usunąć z
listy obserwatorów. Obserwator nie zna innych obserwujących, są oni niezależni od siebie (jakakolwiek
modyfikacja jednego obserwatora nie wpływają na innych). Wzorzec charakteryzuje się łatwością
dodawania nowych obserwujących (tworzymy nową klasę, która będzie implementować interfejs
obserwator).
Wzorzec obserwatora stosujemy, gdy: • Nie chcemy tworzyć skomplikowanych klas, o wielu zadaniach,
które zmniejszałyby naszą elastyczność w ich używaniu, • zmiana w jednym obiekcie pociąga za sobą
konieczność zmian w pozostałych (nie wiemy ilu) • obiekt, który zmienia stan bez wnikania w to, czym
one są.
wzorce projektowe
55
● Wzorce projektowe (design patterns) są to sprawdzone w praktyce rozwiązanie często
pojawiających się, powtarzalnych problemów projektowych czyli to rozpowszechnione w
społeczności programistów rozwiązanie powszechnego problemu, sytuacji, z którą możemy się
spotkać w czasie projektowania aplikacji.
● Czyli: „Wzorzec projektowy pozwala uczyć się na sukcesach innych zamiast nauki na własnych błędach”
Mark Johnson
● Wzorce projektowe stosowane są w projektach wykorzystujących programowanie obiektowe
czyli np. C++ czy tez Pytonie.
● Wartość wzorców projektowych stanowi nie tylko samo rozwiązanie problemu, ale także
dokumentacja, która wyjaśnia cel, działanie, zalety danego rozwiązania, co pomaga w
łatwiejszym stosowaniu i adaptacji wzorców w danym zastosowaniu.
● Wzorce projektowe w informatyce wywodzą się z wzorców projektowych w architekturze.
● Termin wzorca projektowego został wprowadzony do inżynierii oprogramowania przez Kenta
Becka oraz Warda Cunninghama w 1987 roku. Natomiast w 1995 roku autorami pierwszej
szeroko znanej publikacji poświęconej wzorcom w inżynierii oprogramowania byli E. Gamma, R.
Helm, R.Johnson i J. Vlissides, znani jako Banda Czterech (Gang of Four).
● Podzielili oni wzorce na trzy grupy: Wzorce kreacyjne, wzorce strukturalne, wzorce
behawioralne (czynnościowe).
wzorce projektowe
56
Liczne pozycje książkowe takie jak:
● Allan Shalloway James R. Trott: „Projektowanie zorientowane obiektowo. Wzorce projektowe”
● Allen Holub: „Wzorce projektowe. Analiza kodu sposobem na ich poznanie”
Z których korzystałem przy przygotowywaniu prezentacji, jak i wiele innych taki jak np.:
● Erich Gamma, Richard Helm, Ralph Johnson, John M. Vlissides: „Wzorce projektowe. Elementy
oprogramowania obiektowego wielokrotnego użytku”
● Elisabeth Freeman, Eric Freeman, Bert Bates, Kathy Sierra: „Wzorce projektowe. Rusz głową!”
● Bernd Bruegge, Allen H. Dutoit: „
Inżynieria oprogramowania w ujęciu obiektowym. UML, wzorce projektowe i Java”
● John M. Vlissides: „Pattern Hatching: Design Patterns Applied”
● Joshua Kerievsky: „Refactoring to Patterns”
Szeroko dostępne medium o dużych możliwościach (czytaj Internet) jest też źródłem bardzo
dużej ilości wiedzy o wzorcach np.:
http://brasil.cel.agh.edu.pl/~09sbfraczek/wzorce-projektowe-wstep,1,57.html
wzorce projektowe
57
Cała prezentacja i notatka jest dostępna na mojej stronie internetowej:
● pośrednio: http://student.agh.edu.pl/~piex/ w zakładce do pobierania --> JPO2 --> laborki
● lub pod bezpośrednim linkiem: http://student.agh.edu.pl/~piex/semestr5/JPO2/laborki/
wzorce projektowe
58

similar documents