WZORCE PROJEKTOWE (I) (DESIGN PATTERNS) Maciej Patan
Motywacje W wielu dziedzinach nowoczesnej inżynierii napotykamy na następujące zagadnienia: Czy typowe zadania i problemy można rozwiązywać w powtarzalny sposób? Czy te zadania można przedstawić w postaci abstrakcyjnej, ułatwiającej tworzenie rozwiązań dla konkretnych sytuacji praktycznych? 2
Geneza wzorców Christopher Alexander, A pattern language, 1977 Wzorzec opisuje problem, który powtarza się wielokrotnie w danym środowisku, oraz podaje istotę jego rozwiązania w taki sposób, aby można było je zastosować miliony razy bez potrzeby powtarzania tej samej pracy. 3
Wzorce w inżynierii lądowej Czy zbudowaæ most, opieraj¹c przês³o na kolejnych filarach po³¹czonych ³ukiem usztywniaj¹cym przês³o, stanowi¹c jego podparcie na ca³ej d³ugoœci przês³a, czy te wieszaj¹c przês³o na linach stalowych o dopasowanych d³ugoœciach przymocowanych do lin noœnych opieraj¹cych siê na wysokich pylonach umieszczonych po obu stronach przês³a? 4
Wzorce w inżynierii lądowej Czy zbudowaæ most ³ukowy, czy te podwieszany? 5
Wzorce w inżynierii oprogramowania wzorce analityczne poziom opisu rzeczywistości, wzorce architektoniczne poziom integracji komponentów, wzorce projektowe poziom interakcji między klasami, wzorce implementacyjne poziom języka programowania. 6
Definicja Wzorzec projektowy jest dowiedzionym rozwiązaniem dla często spotykanego problemu programistycznego. Składa się z procesów komunikowania się klas i obiektów, które są dostosowywane do rozwiązania problemu w danym kontekście. 7
Definicja Wzorzec projektowy jest dowiedzionym rozwiązaniem dla często spotykanego problemu programistycznego. Składa się z procesów komunikowania się klas i obiektów, które są dostosowywane do rozwiązania problemu w danym kontekście. Wzorzec projektowy może być: szablonem lub strukturą realizującą pewien szczególny cel, techniką doprowadzenia kodu do bardziej uniwersalnej postaci spełniającej założone kryteria, idiomem programowania wysokiego poziomu, połączeniem pomiędzy komponentami programu, kształtem modelu obiektowego (diagramu reprezentującego model). 7
Klasyczne przykłady Enkapsulacja (ukrywanie danych) Problem: Udostępnione zmienne mogą podlegać bezpośredniej manipulacji z zewnątrz, co może prowadzić do naruszeń niezmienności reprezentacji zmiennej lub nieoczekiwanych zależności uniemożliwiających zmiany w implementacji. 8
Klasyczne przykłady Enkapsulacja (ukrywanie danych) Problem: Udostępnione zmienne mogą podlegać bezpośredniej manipulacji z zewnątrz, co może prowadzić do naruszeń niezmienności reprezentacji zmiennej lub nieoczekiwanych zależności uniemożliwiających zmiany w implementacji. Rozwiązanie: Ukrycie pewnych składników klasy i umożliwienie tylko specyficznego dostępu do obiektu. Wady: Interfejs może niedostatecznie (lub pośrednio) realizować pożądane operacje, co może obniżyć efektywność działania. 8
Iteracja Problem: Klient, który życzy sobie dostępu do wszystkich elementów pewnego zestawu musi wykonać przegląd specjalizowany do każdej struktury danych. 9
Iteracja Problem: Klient, który życzy sobie dostępu do wszystkich elementów pewnego zestawu musi wykonać przegląd specjalizowany do każdej struktury danych. Rozwiązanie: Implementacje wykorzystujące wiedzę o reprezentacji danych mogą dokonywać automatycznego przeglądu i zarządzania. Rezultaty są przekazywane do klienta przez standardowy interfejs. Wady: Porządek iterowania jest określony przez implementację i nie jest pod kontrolą klienta. 9
Dziedziczenie (Generalizacja) Problem: Podobne abstrakcje (klasy, obiekty itp.) posiadają podobne właściwości (pola i operacje). Ich powtarzanie jest na ogół pracochłonne, prowadzi do błędów i komplikacji w zarządzaniu. 10
Dziedziczenie (Generalizacja) Problem: Podobne abstrakcje (klasy, obiekty itp.) posiadają podobne właściwości (pola i operacje). Ich powtarzanie jest na ogół pracochłonne, prowadzi do błędów i komplikacji w zarządzaniu. Rozwiązanie: Dziedziczenie domyślnych właściwości z klasy nadrzędnej; wybór prawidłowej implementacji poprzez rozpoznawanie w czasie wykonania programu. Wady: Kod dla danej klasy ulega rozproszeniu, redukując jego zrozumiałość i przejrzystość. Rozpoznawanie w czasie wykonania wprowadza dodatkowy koszt. 10
Klasyfikacja wzorców projektowych Wzorce kreacyjne abstrakcyjne metody tworzenia obiektów, uniezależnienie systemu od sposobu tworzenia obiektów, Wzorce strukturalne sposób wiązania obiektów w struktury, właściwe wykorzystanie dziedziczenia i kompozycji, Wzorce behawioralne algorytmy i przydział odpowiedzialności, opis przepływu kontroli i interakcji, 11
Katalog wzorców projektowych A. Wesley, E. Gamma, R. Helm, R. Johnson, J. Vlissides (1995), Design Patterns, Elements of Reusable Object-Oriented Software: Wzorce kreacyjne Abstract Factory, Builder, Factory Method, Prototype, Singleton, Wzorce strukturalne Adapter, Bridge, Composite, Decorator, Composite, Facade, Proxy, Flyweight, Wzorce behawioralne Chain of Responsibility, Command, Interpreter, Mediator, Iterator, Memento, Observer, State, Strategy, Template Method, Visitor, lista wzorców jest sukcesywne uzupełniana przez innych autorów. 12
Szablon wzorca projektowego nazwa opisująca istotę wzorca, motywacje - opis problemu i rozwiązania, cel działania wzorca, aliasy, zastosowania - sytuacje docelowe dla wzorca, struktura - graficzna reprezentacja klas składowych, uczestnicy - klasy składowe wzorca i ich role, współdziałania - opis współpracy między uczestnikami, konsekwencje - efekty zastosowania wzorca, implementacja - opis wzorca w danym języku programowania, przykład kodu stosującego wzorzec, pokrewne wzorce - wzorce używane w podobnym kontekście. 13
Przykład wzorca kreacyjnego Singleton Cel zapewnienie, że klasa posiada jedną instancję wewnątrz całej aplikacji, stworzenie punktu dostępowego do tej instancji. Motywacje W pewnych okolicznościach możliwe jest powołanie do życia tylko jednego obiektu danej klasy, np. powinien być tylko jeden system plików, menedżer aplikacji czy też okno drukowania. Najlepszym rozwiązaniem jest uczynienie danej klasy odpowiedzialną za śledzenie stanu swojej jedynej instancji i zapobieganie utworzenia następnych egzemplarzy obiektów. Dodatkowo klasa może dostarczyć samodzielnie sposobu dostępu. 14
Singleton (2) Struktura i uczestnicy Singleton -uniqueinstance -singletondata + getinstance() +getsingletondata() +singletonoperation() -Singleton() return uniqueinstance definiuje statyczną metodę instance() udostępniającą instancję klasy (sterowanie liczbą instancji), ogranicza dostęp do konstruktora do własnej klasy i podklas, jest odpowiedzialna za tworzenie instancji własnej klasy. 15
Singleton (3) Konsekwencje Singleton przejmuje odpowiedzialność za tworzenie instancji własnej klasy Klient nie zarządza instancją klasy; otrzymuje ją na żądanie Singleton może zarządzać także swoimi podklasami Singleton jest zwykle obiektem bezstanowym Singleton zachowuje się podobnie do zmiennej globalnej Singleton może powodować zwiększenie liczby powiązań w systemie 16
Przykład wzorca strukturalnego Adapter (Wrapper) Cel Umożliwienie współpracy obiektów o niezgodnych typach Tłumaczenie protokołów obiektowych Motywacje Potrzeba dopasowania niekompatybilnego interfejsu klas np. z biblioteki numerycznej do interfejsu oczekiwanego przez system. W przypadku gdy nie posiadamy kodu źródłowego biblioteki nie możemy dokonać zmiany jej interfejsu wg wymagań. 17
Adapter (2) Struktura Client Target request() Adaptee specificrequest() Adapter request() adaptee adaptee->specificrequest() 18
Adapter (3) Uczestnicy Target: definiuje interfejs specyficzny dla klienta Client współpracuje z obiektami typu Target Adaptee posiada interfejs wymagający adaptacji Adapter adaptuje interfejs Adaptee do interfejsu Target 19
Adapter (4) Konsekwencje Duża elastyczność: pojedynczy Adapter może wspołpracować z wieloma obiektami Adaptee naraz, Adapter może dodawać funkcjonalność do Adaptee, Utrudnione pokrywanie metod Adaptera konieczne utworzenie podklas obiektu Adaptee i bezpośrednie odwołania do nich, Kompozycja i dziedziczenie jako mechanizmy adaptacji 20
Przykład wzorca behawioralnego Observer Cel Utworzenie zależności typu jeden-wiele pomiędzy obiektami, tak żeby informacja o zmianie stanu wyróżnionego obiektu była przekazywana wszystkim pozostałym obiektom Motywacje Potrzeba zarządzania spójnością pomiędzy współpracującymi klasami bez konieczności ich silnego sprzęgania, co zredukuje możliwość ich ponownego wykorzystania. 21
Observer (2) Motywacje (cd) Obiekt generujący zdarzenie może mieć wiele obiektów obserwujących jego stan i synchronizujących z nim swój status. W ten sposób można reagować na zdarzenie w wielu miejscach jednocześnie. Observers x y z a b c 60 30 10 50 30 20 80 10 10 a b c a c b a=50% b=30% c=20% powiadomienie o zmianie zapytanie o status Subject 22
Struktura Observer (3) Subject attach(observer) detach(observer) notify() observers for all obs in observers{ obs->update() } Observer update() ConcreteSubject subjectstate GetState() SetState() return subjectstate subject ConcreteObserver observerstate update() observerstate= subject->getstate() 23
Observer (4) Uczestnicy Subject: utrzymuje rejestr obiektów Observer, dostarcza interfejsu do dołączania i odłączania obiektów Observer, Observer udostępnia interfejs do powiadamiania o zmianach Concrete Subject przechowuje stan istotny dla obiektów Concrete Observer powiadamia obiekty Concrete Observer Concrete Observer aktualizuje swój stan na podstawie powiadomienia 24
Observer (5) Konsekwencje Luźniejsze powiązania pomiędzy obiektami: obiekt Subject komunikuje się z innymi obiektami przez interfejs Observer, obiekty Subject i Observers mogą prezentować różne warstwy abstrakcji, Programowe rozgłaszanie komunikatów Spójność stanu pomiędzy obiektami Subject i Observers Skalowalność aktualizacji: push: Observers otrzymują kompletny stan obiektu Subject, pull: Observers otrzymują powiadomienie i referencję do obiektu Subject 25
Plusy i minusy wzorce ułatwiają i przyśpieszają powtórne wykorzystanie sprawdzonych projektów i struktur, projektant posiadający pewne doświadczenie może natychmiast zastosować odpowiedni wzorzec dla danego problemu bez powtórnego odkrywania jego rozwiązania, Wyrażanie sprawdzonych technik w postaci wzorców ułatwia ich dostępność dla innych programistów, Mogą ulepszyć procesy zarządzania istniejącymi systemami i tworzenia dokumentacji dostarczając standardowej specyfikacji klas oraz ich interakcji. 26
Plusy i minusy wzorce ułatwiają i przyśpieszają powtórne wykorzystanie sprawdzonych projektów i struktur, projektant posiadający pewne doświadczenie może natychmiast zastosować odpowiedni wzorzec dla danego problemu bez powtórnego odkrywania jego rozwiązania, Wyrażanie sprawdzonych technik w postaci wzorców ułatwia ich dostępność dla innych programistów, Mogą ulepszyć procesy zarządzania istniejącymi systemami i tworzenia dokumentacji dostarczając standardowej specyfikacji klas oraz ich interakcji. każdy wzorzec wymaga znalezienia kompromisu pomiędzy uniwersalnością a efektywnością, mogą zarówno zwiększyć jak też i zmniejszyć zrozumiałość kodu programu. 26