Adapter

1 minute read

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:

  1. Obiekt Adapter jest stworzony interfejsem, który jest kompatybilny z istniejącym obiektem.
  2. Używając tego interfejsu, istniejący obiekt może bezpiecznie odwoływać się do metod adaptera.
  3. 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:

  1. 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’
  2. Zadeklaruj interfejs klienta i opisz jak komunikuje się z servicem.
  3. Stwórz klase Adapter która implementuje interfejs klienta. Pozostaw metody puste.
  4. 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.
  5. 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.
  6. 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