Chcę zademonstrować mojemu zespołowi użycie wzorca adaptera . Przeczytałem wiele książek i artykułów online. Wszyscy cytują przykład, który jest przydatny do zrozumienia koncepcji (kształt, karta pamięci, adapter elektroniczny itp.), Ale nie ma prawdziwego studium przypadku.
Czy możesz udostępnić studium przypadku dotyczące wzoru adaptera?
ps Próbowałem przeszukać istniejące pytania na stackoverflow, ale nie znalazłem odpowiedzi, więc publikowałem je jako nowe pytanie. Jeśli wiesz, że jest już na to odpowiedź, przekieruj.
oop
design-patterns
adapter
software-design
AksharRoop
źródło
źródło
Odpowiedzi:
Wiele przykładów adapterów jest banalnych lub nierealnych ( Rectangle vs. LegacyRectangle, Ratchet vs. Socket , SquarePeg vs. RoundPeg , Duck vs. Turkey ). Co gorsza, wiele z nich nie pokazuje wielu adapterów dla różnych adapterów ( ktoś zacytował Arrays.asList języka Java jako przykład wzorca adaptera ). Dostosowanie interfejsu tylko jednej klasy do pracy z inną wydaje się słabym przykładem wzorca GoF Adapter. Ten wzorzec wykorzystuje dziedziczenie i polimorfizm, więc można by się spodziewać dobrego przykładu pokazującego wiele implementacji adapterów dla różnych adapterów .
Najlepszym przykładem znalazłem jest w rozdziale 26 Zastosowanie UML i wzorce: Wprowadzenie do Object-Oriented Analysis and Design i Iteracyjne Rozwoju (3rd Edition) . Poniższe obrazy pochodzą z materiału instruktorskiego udostępnionego na serwerze FTP do książki.
Pierwsza z nich pokazuje, w jaki sposób aplikacja może korzystać z wielu implementacji (adapterów), które są podobne funkcjonalnie (np. Kalkulatory podatkowe, moduły księgowe, usługi autoryzacji kredytu itp.), Ale mają różne API. Chcemy uniknąć zakodowania na stałe naszego kodu warstwy domeny w celu obsługi różnych możliwych sposobów obliczania podatku, obsługi posprzedażnej, autoryzacji żądań kart kredytowych itp. Są to wszystkie zewnętrzne moduły, które mogą się różnić i dla których nie możemy modyfikować kod. Adapter pozwala nam na stałe kodowanie w adapterze, podczas gdy nasz kod warstwy domeny zawsze używa tego samego interfejsu (interfejs IWh AnyAdapter).
Na powyższym rysunku nie widzimy rzeczywistych adapterów. Jednak na poniższym rysunku pokazano, w jaki sposób wykonywane jest polimorficzne wywołanie
postSale(...)
w interfejsie IAccountingAdapter, które skutkuje zaksięgowaniem sprzedaży za pośrednictwem protokołu SOAP do systemu SAP.źródło
Jak zmienić Francuza w normalną osobę ...
Przykład
źródło
Konwertuj interfejs na inny interfejs.
Aby podłączyć zasilanie, mamy różne interfejsy na całym świecie. Za pomocą adaptera możemy łatwo połączyć się, jak mądrze.
źródło
Oto przykład, który symuluje konwersję
analog data
do formatudigit data
.Dostarcza adapter, który konwertuje zmiennoprzecinkowe dane cyfrowe na dane binarne, prawdopodobnie nie jest przydatny w świecie rzeczywistym, po prostu pomaga wyjaśnić koncepcję wzorca adaptera.
Kod
AnalogSignal.java
package eric.designpattern.adapter; public interface AnalogSignal { float[] getAnalog(); void setAnalog(float[] analogData); void printAnalog(); }
DigitSignal.java
package eric.designpattern.adapter; public interface DigitSignal { byte[] getDigit(); void setDigit(byte[] digitData); void printDigit(); }
FloatAnalogSignal.java
package eric.designpattern.adapter; import java.util.Arrays; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FloatAnalogSignal implements AnalogSignal { private Logger logger = LoggerFactory.getLogger(this.getClass()); private float[] data; public FloatAnalogSignal(float[] data) { this.data = data; } @Override public float[] getAnalog() { return data; } @Override public void setAnalog(float[] analogData) { this.data = analogData; } @Override public void printAnalog() { logger.info("{}", Arrays.toString(getAnalog())); } }
BinDigitSignal.java
package eric.designpattern.adapter; import java.util.Arrays; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class BinDigitSignal implements DigitSignal { private Logger logger = LoggerFactory.getLogger(this.getClass()); private byte[] data; public BinDigitSignal(byte[] data) { this.data = data; } @Override public byte[] getDigit() { return data; } @Override public void setDigit(byte[] digitData) { this.data = digitData; } @Override public void printDigit() { logger.info("{}", Arrays.toString(getDigit())); } }
AnalogToDigitAdapter.java
package eric.designpattern.adapter; import java.util.Arrays; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * <p> * Adapter - convert analog data to digit data. * </p> * * @author eric * @date Mar 8, 2016 1:07:00 PM */ public class AnalogToDigitAdapter implements DigitSignal { public static final float DEFAULT_THRESHOLD_FLOAT_TO_BIN = 1.0f; // default threshold, private Logger logger = LoggerFactory.getLogger(this.getClass()); private AnalogSignal analogSignal; private byte[] digitData; private float threshold; private boolean cached; public AnalogToDigitAdapter(AnalogSignal analogSignal) { this(analogSignal, DEFAULT_THRESHOLD_FLOAT_TO_BIN); } public AnalogToDigitAdapter(AnalogSignal analogSignal, float threshold) { this.analogSignal = analogSignal; this.threshold = threshold; this.cached = false; } @Override public synchronized byte[] getDigit() { if (!cached) { float[] analogData = analogSignal.getAnalog(); int len = analogData.length; digitData = new byte[len]; for (int i = 0; i < len; i++) { digitData[i] = floatToByte(analogData[i]); } } return digitData; } // not supported, should set the inner analog data instead, @Override public void setDigit(byte[] digitData) { throw new UnsupportedOperationException(); } public synchronized void setAnalogData(float[] analogData) { invalidCache(); this.analogSignal.setAnalog(analogData); } public synchronized void invalidCache() { cached = false; digitData = null; } @Override public void printDigit() { logger.info("{}", Arrays.toString(getDigit())); } // float -> byte convert, private byte floatToByte(float f) { return (byte) (f >= threshold ? 1 : 0); } }
Kod - przypadek testowy
AdapterTest.java
package eric.designpattern.adapter.test; import java.util.Arrays; import junit.framework.TestCase; import org.junit.Test; import eric.designpattern.adapter.AnalogSignal; import eric.designpattern.adapter.AnalogToDigitAdapter; import eric.designpattern.adapter.BinDigitSignal; import eric.designpattern.adapter.DigitSignal; import eric.designpattern.adapter.FloatAnalogSignal; public class AdapterTest extends TestCase { private float[] analogData = { 0.2f, 1.4f, 3.12f, 0.9f }; private byte[] binData = { 0, 1, 1, 0 }; private float[] analogData2 = { 1.2f, 1.4f, 0.12f, 0.9f }; @Test public void testAdapter() { AnalogSignal analogSignal = new FloatAnalogSignal(analogData); analogSignal.printAnalog(); DigitSignal digitSignal = new BinDigitSignal(binData); digitSignal.printDigit(); // adapter AnalogToDigitAdapter adAdapter = new AnalogToDigitAdapter(analogSignal); adAdapter.printDigit(); assertTrue(Arrays.equals(digitSignal.getDigit(), adAdapter.getDigit())); adAdapter.setAnalogData(analogData2); adAdapter.printDigit(); assertFalse(Arrays.equals(digitSignal.getDigit(), adAdapter.getDigit())); } }
Zależność - via maven
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.8.2</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.13</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.13</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.16</version> </dependency>
Jak przetestować
Po prostu uruchom test jednostkowy.
źródło
Przykładami ze świata rzeczywistego może być tłumacz języka lub mobilna ładowarka. Więcej tutaj w tym wideo z YouTube:
Youtube - Wzorzec projektowy adaptera: Wprowadzenie
źródło
Wzorca projektowego Adapter można użyć, gdy masz do czynienia z różnymi interfejsami o podobnym zachowaniu (co zwykle oznacza klasy o podobnym zachowaniu, ale z różnymi metodami). Przykładem może być klasa do podłączenia do telewizora Samsung i inna do podłączenia do telewizora Sony. Będą mieć wspólne zachowanie, takie jak otwieranie menu, rozpoczynanie odtwarzania, łączenie się z siecią itp., Ale każda biblioteka będzie miała inną implementację (z różnymi nazwami metod i podpisami). Te różne implementacje specyficzne dla dostawców są nazywane Adaptee w diagramach UML.
Tak więc w swoim kodzie (nazywanym klientem na diagramach UML), zamiast sztywnego kodowania wywołań metod każdego dostawcy (lub Adaptee ), można następnie utworzyć ogólny interfejs (nazywany docelowym w diagramach UML), aby opakować te podobne zachowania i działać tylko z jednym rodzajem obiektu.
Te adaptery będą następnie wdrożyć docelowy interfejs delegowanie swoich połączeń metodę do Adaptees , które są przekazywane do Adaptery przez konstruktora.
Abyś mógł to zrealizować w kodzie Java, napisałem bardzo prosty projekt, używając dokładnie tego samego przykładu wymienionego powyżej, używając adapterów do obsługi wielu interfejsów Smart TV. Kod jest mały, dobrze udokumentowany i nie wymaga objaśnień, więc zapoznaj się z nim, aby zobaczyć, jak wyglądałaby implementacja w świecie rzeczywistym.
Po prostu pobierz kod i zaimportuj go do Eclipse (lub swojego ulubionego IDE) jako projekt Maven. Możesz wykonać kod, uruchamiając org.example.Main.java . Pamiętaj, że ważne jest, aby zrozumieć, w jaki sposób klasy i interfejsy są łączone razem w celu zaprojektowania wzorca. Stworzyłem także kilka fałszywych Adaptees w pakiecie com.thirdparty.libs . Mam nadzieję, że to pomoże!
https://github.com/Dannemann/java-design-patterns
źródło
Jednym z prawdziwych przykładów jest Qt-Dbus.
Qt-dbus zawiera narzędzie do generowania adaptera i kodu interfejsu z dostarczonego pliku xml. Oto kroki, aby to zrobić.
Możesz zobaczyć pełny przykład Qt-Dbus tutaj -
http://www.tune2wizard.com/linux-qt-signals-and-slots-qt-d-bus/
źródło
Implementację PHP wzorca Adapter używanego do obrony przed atakami typu injection można znaleźć tutaj:
http://www.php5dp.com/category/design-patterns/adapter-composition/
Jednym z interesujących aspektów wzorca adaptera jest to, że występuje on w dwóch odmianach: adapter klasy korzystający z wielokrotnego dziedziczenia oraz adapter obiektów zależny od kompozycji. Powyższy przykład opiera się na kompozycji.
źródło
Wzorce projektowe adaptera pomagają w konwersji interfejsu jednej klasy na interfejs oczekiwany przez klienta.
Przykład: Masz usługę, która zwraca pogodę (w stopniach Celsjusza), przekazując nazwę miasta jako wartość wejściową. Teraz załóżmy, że klient chce podać kod pocztowy jako dane wejściowe i oczekuje w zamian temperatury miasta. Aby to osiągnąć, potrzebujesz adaptera.
źródło
Prawdziwym przykładem może być raportowanie dokumentów w aplikacji. Prosty kod, jak tutaj.
Myślę, że adaptery są bardzo przydatne w strukturze programowania.
Wyniki będą:
źródło
Użyj adaptera, jeśli masz interfejs, którego nie możesz zmienić, ale którego musisz użyć. Zobacz, jak jesteś nowym facetem w biurze i nie możesz zmusić siwych włosów do przestrzegania twoich zasad - musisz się do nich dostosować. Oto prawdziwy przykład z prawdziwego projektu, nad którym kiedyś pracowałem, w którym interfejs użytkownika jest podany.
Masz aplikację, która wczytuje wszystkie wiersze w pliku do struktury danych List i wyświetla je w siatce (nazwijmy bazowy interfejs magazynu danych IDataStore). Użytkownik może poruszać się po tych danych, klikając przyciski „Pierwsza strona”, „Poprzednia strona”, „Następna strona”, „Ostatnia strona”. Wszystko dziala.
Teraz aplikacja musi być używana z dziennikami produkcyjnymi, które są zbyt duże, aby wczytać je do pamięci, ale użytkownik nadal musi się po nich poruszać! Jednym z rozwiązań byłoby zaimplementowanie pamięci podręcznej przechowującej pierwszą stronę, następną, poprzednią i ostatnią stronę. Chcemy, aby kiedy użytkownik kliknął „Następna strona”, zwracamy stronę z pamięci podręcznej i aktualizujemy pamięć podręczną; kiedy klikną ostatnią stronę, zwracamy ostatnią stronę z pamięci podręcznej. W tle mamy strumień plików, który robi całą magię. W ten sposób mamy w pamięci tylko cztery strony, a nie cały plik.
Możesz użyć adaptera, aby dodać tę nową funkcję pamięci podręcznej do aplikacji bez zauważenia tego przez użytkownika. Rozszerzamy obecny IDataStore i nazywamy go CacheDataStore. Jeśli plik do załadowania jest duży, używamy CacheDataStore. Kiedy wysyłamy żądanie dotyczące pierwszej, następnej, poprzedniej i ostatniej strony, informacje są kierowane do naszej pamięci podręcznej.
A kto wie, jutro szef chce zacząć czytać pliki z tabeli bazy danych. Wszystko, co musisz zrobić, to nadal rozszerzać IDataStore do SQLDataStore, tak jak w przypadku Cache, konfigurując połączenie w tle. Kiedy klikną Next page, wygenerujesz niezbędne zapytanie sql, aby pobrać następne kilkaset wierszy z bazy danych.
Zasadniczo oryginalny interfejs aplikacji nie uległ zmianie. Po prostu dostosowaliśmy nowoczesne i fajne funkcje, aby działały, zachowując stary interfejs.
źródło
Przykład @Justice o nie mówi wyraźnie o wzorze adaptera. Rozszerzając jego odpowiedź - Mamy już interfejs IDataStore, z którego korzysta nasz kod konsumencki i nie możemy go zmienić. Teraz jesteśmy proszeni o użycie nowej, fajnej klasy z biblioteki XYZ, która robi to, co chcemy zaimplementować, ale ale nie możemy zmienić tej klasy w celu rozszerzenia naszego IDataStore, widzisz już problem? Tworząc nową klasę - ADAPTER, która implementuje interfejs, którego oczekuje nasz konsumencki kod, czyli IDataStore i wykorzystując klasę z biblioteki, której cechy musimy mieć - ADAPTEE, jako członek naszego ADAPTERA, możemy osiągnąć to, co chcieliśmy.
źródło
Zgodnie z książką „Wzorce projektowe C # 3.0” autorstwa Judith Bishop, firma Apple użyła wzorca adaptera w celu dostosowania systemu Mac OS do współpracy z produktami Intel (wyjaśnione w rozdziale 4, fragment 2 )
źródło
Przykład z frameworka Yii to: Yii używa wewnętrznej pamięci podręcznej wykorzystując interfejs ICache. https://www.yiiframework.com/doc/api/1.1/ICache
którego podpis jest taki: -
Powiedzmy, że chciałbyś użyć wewnątrz projektu Yii biblioteki pamięci podręcznej symfony https://packagist.org/packages/symfony/cache z jej interfejsem pamięci podręcznej, definiując tę usługę w konfiguracji komponentów usług Yii (lokalizator usług) https: / /github.com/symfony/cache-contracts/blob/master/CacheInterface.php
Widzimy, że pamięć podręczna symfony ma interfejs tylko z metodą get, brakuje metody set i innej sygnatury dla metody get, ponieważ Symfony używa metody get również jako setera, gdy dostarcza drugi parametr wywoływalny.
Ponieważ rdzeń Yii wewnętrznie korzysta z tej pamięci podręcznej / interfejsu Yii, przepisanie wywołań do tego interfejsu jest trudne (rozszerzenie Yii / YiiBase), jeśli nie niemożliwe.
Poza tym pamięć podręczna Symfony nie jest naszą klasą, więc nie możemy przepisać jej interfejsu, aby pasował do interfejsu pamięci podręcznej Yii.
Oto więc wzór adaptera na ratunek. Napiszemy mapping = pośredni adapter, który zmapuje wywołania interfejsu pamięci podręcznej Yii do interfejsu pamięci podręcznej Symfony
Wyglądałby tak
źródło
Oto przykład implementacji adaptera:
źródło