Adapter
Adapter jest strukturalnym wzorcem który pozwala na współprace obiektów o niekompatybilnych interfejsach.
Wzorzec wykorzystuje specjalny obiekt którego zadaniem jest konwersja interfejsu jednego obiektu w taki sposób by mógł z niego korzystać inny, niekompatybilny wcześniej obiekt. Obiekt Adapter ukrywa szczegóły konwersji i to w taki sposób, że obiekt który konwertuje nie wie o istnieniu adaptera. Przykładowo możemy stworzyć adapter dla obiektu zwracającego dane w formacie xml, tak aby zwracane dane miały format json. Możemy wykorzystywać wzorzec nie tylko do konwersji formatu danych, ale również do nawiązania współpracy między obiektami o różnych interfejsach. Zasada działania wzorca:
- Obiekt Adapter jest stworzony interfejsem, który jest kompatybilny z istniejącym obiektem.
- Używając tego interfejsu, istniejący obiekt może bezpiecznie odwoływać się do metod adaptera.
- Po wywołaniu metody Adaptera przekazuje on żadanie do drugiego obiektu, ale w formacie i na zasadach których ten obiekt oczekuje.
Czasami możliwe jest stworzenie adaptera, który będzie w stanie działać dwukierunkowo, przekazując odpowiednio przygotowane żądania w obie strony.
Używając Adaptera tworzymy pośrednika który działa jak translator miedzy naszym kodem, a np.: klasą kodu legacy, rozwiązania 3-rd party albo jakąkolwiek inną klasą z dziwnym interfejsem.
Sposób implementacji wzorca:
- Znajdź przynajmniej dwie klasy o niekompatybilnych interfejsach:
- Przydatny ‘service’ - klasa której nie możesz zmienić (np. 3-rd party, legacy albo z wieloma istniejącymi zależnościami)
- Jedna lub kilka klas twojej aplikacji które potrzebują wykorzystać powyższy ‘service’
- Zadeklaruj interfejs klienta i opisz jak komunikuje się z servicem.
- Stwórz klase Adapter która implementuje interfejs klienta. Pozostaw metody puste.
- Do Adaptera dodaj pole które przechowuje referencje do niekompatybilnego obiektu. Wartość dla tego pola często jest inicjalizowane w konstruktorze lub przekazywana do adaptera z wywołaniem jego metod.
- Zaimplementuj metody interfejsu klienta w adapterze. Większość prawdziwej pracy powinna zostać wykonana przez niekompatybilny wcześniej obiekt, zadaniem adaptera jest tylko przekazanie mu informacji w odpowiednim formacie i kolejności oraz ewentualne odebranie ich i zwrócenie klientowi.
- Klient powinien używać adaptera z użyciem jego interfejsu, co pozwoli w przyszłości wymieniać lub rozszerzać adaptery bez wpływu na istniejacy kod.
Zalety i wady:
- Zachowanie zasady Single Responsibility. Logika konwersji jest odseparowana od reszty programu.
-
Zasada Open/Closed - możemy wprowadzić nowe adaptery bez potrzeby edycji istniejącego kodu klienta jeśli ten wykorzystuje adaptery przez ich interface.
- Ogólna zawiłość kodu wzrasta, ponieważ musimy wprowadzić zbiór nowych interfejsów i klas. Czasami prostszym rozwiązaniem będzie zmiana niekompatybilnej klasy tak by pasowała do reszty aplikacji.
Przykładowy kod: https://github.com/rafczow/php-patterns-practice/tree/master/src/Adapter
Powiązane wzorce: Bridge Decorator