Adapter: opis Wzorce Strukturalne Tomasz Borzyszkowski Alternatywna nazwa: Wrapper (opakowanie) Rola obiektu Adapter: pełni wobec Klienta rolę otoczki, która umożliwia przetłumaczenie jego żądań na protokół zrozumiały dla faktycznego wykonawcy poleceń. Pozwala dopasować interfejs istniejących obiektów do interjefsu wymaganego przez bieżącą implementację, bez zmiany kodu klas istniejących obiektów. Często porównywany do różnego rodzaju przejściówek. Adapter: diagram Adapter: struktura
Adapter: struktura Adapter: komentarz Składa się z trzech podstawowych klas: Target: interfejs, którego oczekuje klient Adaptee: obiekt dostarczający żądanej przez klienta funkcjonalności, ale niezgodny pod względem typu / interfejsu Adapter: implementuje typ Target, tłumaczy wywołania metody należącej do typu Target na wykonanie innej metody/metod w klasie Adaptee. Dzięki temu klient współpracuje z obiektem Adapter o akceptowanym przez siebie interfejsie Target, jednocześnie wykorzystując funkcjonalność dostarczoną przez Adaptee. Istnieje także wersja wykorzystującą dziedziczenie w relacji Adapter-Adaptee. Jednak wersja ta ma pewne niedogodności: powiązania między obiektami są ustalane w momencie kompilacji i nie mogą ulec zmianie ponadto, język programowania musi umożliwiać stosowanie wielokrotnego dziedziczenia lub dziedziczenia i implementacji interfejsu (jak w przypadku języków Java i C#). Adapter: implementacje Wtorzymy klasę, która implementuje wymagany interfejs, a następnie zapewniamy jej komunikację z klasą, która ma inny interfejs. Istnieją dwa sposoby realizacji, przez: dziedziczenie: z klasy, która ma niezgodny interfejs wywodzimy klasę pochodną i dopisujemy nowe metody tak, by uzyskać wymagany interfejs kompozycję: zawieramy pierwotną klasę wewnątrz nowej i tworzymy metody wymaganego interfejsu realizujące wywołania metod klasy wewnętrznej. Adapter: implementacje kompozycja public class Adoptowana { public Adoptowana() { ; public int starezad() {return 1;; public class AdapterComposition implements WymaganyIF { private Adoptowana adoptowana; public Adapter() { adoptowana = new Adoptowana(); public int nowezad {return adoptowana.starezad(); public interface WymaganyIF { public int nowezad();
Adapter: implementacje dziedziczenie public class Adoptowana { public Adoptowana() { public int starezad() {return 1; public class AdapterInherit extends Adoptowana implements WymaganyIF { public Adapter() { public int nowezad { return starezad(); public interface WymaganyIF { public int nowezad(); Dekorator: opis Rozszerza funkcjonalność obiektu poprzez dynamiczne dołączenie dodatkowych zachowań. Wzorzec ten pozwala na dekorowanie zachowania klasy, czyli zmianę jej funkcjonalności bez potrzeby dziedziczenia. Działanie podobne do dziedziczenia z tą rożnicą, że dziedziczenie rozszerza zachowanie klasy w trakcie kompilacji, a dekoratory rozszerzają klasy w czasie działania programu. Zmienia operacje dowolnego Komponentu przez użycie dodatkowego kodu w stosunku do wywoływanej przy tym operacji obiektu dekorowanego. Dekorator: zalety Dekorator: wady Zalety: dynamiczny odpowiednik dziedziczenia: większa elastyczność programowanych rozwiązań niż w przypadku dziedziczenia; obiekt można dowolnie udekorować podczas wykonania kodu rozbicie funkcjonalności: uproszczenia kodowania poprzez możliwość rozwoju szeregu klas o określonych funkcjach zamiast umieszczania wszystkich zachowań w jednym obiekcie Wady: Rozdrobnienie: projekty, w których jest używany, charakteryzują się mnogością małych podobnych do siebie obiektów, które różnią się jedynie tym jak są ze sobą połączone; zrozumienie ich funkcjonowania w celu modyfikacji może być przez to mocno utrudnione Brak identyczności obiektów: nie można korzystać z identyczności obiektow; dekorator działa jak przezroczysta otoczka, ale z punktu widzenia identyczności obiekt udekorowany nie jest taki sam jak wejściowy
Dekorator: stosowalność Dekorator: diagram w sposób jawny i dynamiczny chcemy dodać nowe zachowania do obiektu bez wpływu na pozostałe obiekty chcemy dodać nowe zachowania do obiektu, dla których istnieje możliwość, że będą zmieniane w przyszłości kiedy rozszerzanie funkcjonalności poprzez podklasy przestaje być pratyczne, np. jeżeli do hierarchii klas musimy dodać nowe podklasy zmieniające zachowania każdego liścia z danej hierarchii; w tej sytuacji lepiej stworzyć nową podklasę klasy głównej, która będzie modyfikować odpowiednie zachowania Dekorator: implementacja public interface Komponent { public void action(); public class KonkretnyKomponent implements Komponent { public void action() { /* ciało metody */ public class Dekorator implements Komponent { Komponent kompjeden = new KonkretnyKomponent(); public void action() { kompjeden.action(); Dekorator: implementacja cd public KonkretnyDekorator extends Dekorator { public void action() { super.action(); dodanametoda(); private void dodanametoda(){ /* ciało metody */
Dekorator: Starbuzz dziedziczenie Dekorator: Starbuzz dziedziczenie??? Dekorator: Starbuzz OK Fasada: opis Dostarczenie ujednoliconego sposobu dostępu do złożonego systemu poprzez wystawienie uproszczonego, uporządkowanego interfejsu programistycznego. Klienci komunikują się z systemem, wysyłając żądania do Fasady, która przekazuje je do odpowiednich obiektów systemu. Fasada może być zmuszona do tłumaczenia jej interfejsu na interfejsy komponentów podsystemu, chciaż to obiekty podsystemu wykonują właściwą pracę, Klienci wykorzystujący fasadę nie muszą mieć bezpośredniego dostępu do obiektów jej podsystemu.
Fasada: stosowalność Fasada: diagram kiedy potrzebujemy dostarczyć prostszy interfejs do złożonego podsystemu kiedy istnieje szereg zależności pomiędzy klientem a klasami implementacji abstrakcji kiedy wprowadzenie do systemu warstwy upraszczającej dostęp jest potrzebne, albo pożądane Fasada: uczestnicy Fasada: zalety Fasada: wie jakie klasy podsystemu są odpowiedzialne za spełnienie żądania; przekazuje żądania klienta do odpowiednich obiektów podsystemu Klasy podsystemu: implementują funkcje podsystemu wykonują pracę przydzieloną przez obiekt klasy Fasada nic nie wiedzą o fasadzie, to znaczy nie przechowują żadnych odwołań do niej dostarcza prostszy interfejs do rozbudowanego podsystemu bez ograniczania jego funkcjonalności osłania klienta od złożoności komponentów podsystemu dostarcza łącznik pomiędzy podsystem a jego klientów ogranicza bezpośrednie połączenia pomiędzy podsystemami, tj. każdy podsystem używa własnej Fasady i inne części systemu używają wzorca Fasady do komunikowania się z subsystemami
Fasada: przykład Most: opis Intencją wprowadzenia tego wzorca jest oddzielenie interfejsu od jego implementacji tak by oba elementy mogły istnieć niezależnie i być rozwijane niezależnie. Rozdzielenie takie pozwala niezależnie rozwijać implementowany interfejs oraz klasy implementujące ten interfejs. Most: diagram Most: uczestnicy Abstraction (BusinessObject): definiuje interfejs użytkownika abstrakcji utrzymuje odniesienie do obiektu typu Implementor RefinedAbstraction (CustomersBusinessObject): rozszerzenie interfejsu zdefiniowanego przez Abstraction Implementor (DataObject): interfejs klasy implementującej; nie musi być zgodny z Abstraction; zwykle dostarcza tylko podstawowe operacje ConcreteImplementor (CustomersDataObject): implementacja interfejsu Implementor
Most: zalety Zalety: umożliwia odseparowanie implementacji od interfejsu. poprawia możliwość rozbudowy klas ukrywanie szczegółów implementacyjnych przed klientem Stosowalność: gdy wymagane jest oddzielenie interfejsu od implementacji zarówno interfejs jak i implementacja musi zostać rozbudowana poprzez podklasy zmiany implementacyjne nie mogą mieć wpływu na klienta