Ich möchte meinem Team die Verwendung des Adaptermusters demonstrieren . Ich habe viele Bücher und Artikel online gelesen. Jeder zitiert ein Beispiel, das nützlich ist, um das Konzept zu verstehen (Form, Speicherkarte, elektronischer Adapter usw.), aber es gibt keine wirkliche Fallstudie.
Können Sie bitte eine Fallstudie zum Adaptermuster teilen?
ps Ich habe versucht, vorhandene Fragen zu stackoverflow zu durchsuchen, aber die Antwort nicht gefunden, sodass ich sie als neue Frage poste. Wenn Sie wissen, dass es bereits eine Antwort darauf gibt, leiten Sie diese bitte weiter.
oop
design-patterns
adapter
software-design
AksharRoop
quelle
quelle
Antworten:
Viele Beispiele für Adapter sind trivial oder unrealistisch ( Rectangle vs. LegacyRectangle, Ratchet vs. Socket , SquarePeg vs. RoundPeg , Duck vs. Turkey ). Schlimmer noch, viele zeigen nicht mehrere Adapter für verschiedene Adapter an ( jemand zitierte Javas Arrays.asList als Beispiel für das Adaptermuster ). Die Anpassung einer Schnittstelle nur einer Klasse an die Arbeit mit einer anderen Klasse scheint ein schwaches Beispiel für das GoF-Adaptermuster zu sein. Dieses Muster verwendet Vererbung und Polymorphismus, so dass man erwarten würde, dass ein gutes Beispiel mehrere Implementierungen von Adaptern für verschiedene Adapter zeigt .
Das beste Beispiel, das ich gefunden habe, ist in Kapitel 26 von Anwenden von UML und Mustern: Eine Einführung in die objektorientierte Analyse und das Design und die iterative Entwicklung (3. Ausgabe) . Die folgenden Bilder stammen aus dem Lehrermaterial, das auf einer FTP-Site für das Buch bereitgestellt wird.
Die erste zeigt, wie eine Anwendung mehrere Implementierungen (Adaptees) verwenden kann, die funktional ähnlich sind (z. B. Steuerrechner, Buchhaltungsmodule, Kreditautorisierungsdienste usw.), jedoch unterschiedliche APIs haben. Wir möchten vermeiden, unseren Domain-Layer-Code fest zu codieren, um die verschiedenen Möglichkeiten zur Berechnung von Steuern, Nachverkäufen, Autorisierung von Kreditkartenanfragen usw. zu handhaben. Dies sind alles externe Module, die variieren können und für die wir den nicht ändern können Code. Mit dem Adapter können wir die Hardcodierung im Adapter durchführen, während unser Domänenschichtcode immer dieselbe Schnittstelle verwendet (die IWhateverAdapter-Schnittstelle).
In der obigen Abbildung sehen wir nicht die tatsächlichen Adaptees. Die folgende Abbildung zeigt jedoch, wie ein polymorpher Aufruf
postSale(...)
in der IAccountingAdapter-Schnittstelle erfolgt, der zu einer Buchung des Verkaufs über SOAP an ein SAP-System führt.quelle
Wie man aus einer französischen Person eine normale Person macht ...
Beispiel
quelle
Konvertieren Sie eine Schnittstelle in eine andere Schnittstelle.
Um die Stromversorgung anzuschließen, haben wir weltweit verschiedene Schnittstellen. Mit Adapter können wir einfach wie weise verbinden.
quelle
Hier ist ein Beispiel, das die Konvertierung
analog data
in simuliertdigit data
.Es bietet einen Adapter, der Float-Digit-Daten in Binärdaten konvertiert. In der realen Welt ist es wahrscheinlich nicht nützlich. Es hilft nur, das Konzept des Adaptermusters zu erklären.
Code
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); } }
Code - Testfall
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())); } }
Abhängigkeit - über 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>
Wie zu testen
Führen Sie einfach den Unit-Test durch.
quelle
Beispiele aus der Praxis könnten ein Sprachübersetzer oder ein mobiles Ladegerät sein. Mehr hier in diesem Youtube-Video:
Youtube - Adapter Design Pattern: Einführung
quelle
Sie können das Adapter-Entwurfsmuster verwenden, wenn Sie mit verschiedenen Schnittstellen mit ähnlichem Verhalten arbeiten müssen (was normalerweise Klassen mit ähnlichem Verhalten, aber mit unterschiedlichen Methoden bedeutet). Ein Beispiel hierfür wäre eine Klasse zum Anschließen an ein Samsung-Fernsehgerät und eine andere zum Anschließen an ein Sony-Fernsehgerät. Sie haben ein gemeinsames Verhalten wie das Öffnen des Menüs, das Starten der Wiedergabe, das Herstellen einer Verbindung zu einem Netzwerk usw., aber jede Bibliothek hat eine andere Implementierung (mit unterschiedlichen Methodennamen und Signaturen). Diese verschiedenen herstellerspezifischen Implementierungen werden in den UML-Diagrammen als Adaptee bezeichnet .
In Ihrem Code ( in den UML-Diagrammen Client genannt ) können Sie anstelle von Hardcode für die Methodenaufrufe jedes Anbieters (oder Adaptees ) eine generische Schnittstelle ( in UML-Diagrammen Target genannt ) erstellen, um diese ähnlichen Verhaltensweisen und Funktionen zu verpacken mit nur einem Objekttyp.
Die Adapter werden dann die Umsetzung Ziel Schnittstelle seine Methodenaufrufe an die Delegierung Adaptees , die an die übergeben werden Adapter über Konstruktor.
Damit Sie dies in Java-Code realisieren können, habe ich ein sehr einfaches Projekt geschrieben, das genau das oben erwähnte Beispiel verwendet und Adapter verwendet, um mit mehreren Smart-TV-Schnittstellen umzugehen. Der Code ist klein, gut dokumentiert und selbsterklärend. Sehen Sie sich also an, wie eine Implementierung in der realen Welt aussehen würde.
Laden Sie einfach den Code herunter und importieren Sie ihn als Maven-Projekt in Eclipse (oder Ihre Lieblings-IDE). Sie können den Code ausführen, indem Sie org.example.Main.java ausführen . Denken Sie daran, dass es hier wichtig ist zu verstehen, wie Klassen und Schnittstellen zusammengesetzt werden, um das Muster zu entwerfen. Ich habe auch einige gefälschte Adaptees im Paket com.thirdparty.libs erstellt . Ich hoffe es hilft!
https://github.com/Dannemann/java-design-patterns
quelle
Ein echtes Beispiel ist Qt-Dbus.
Der qt-dbus verfügt über ein Dienstprogramm zum Generieren des Adapter- und Schnittstellencodes aus der bereitgestellten XML-Datei. Hier sind die Schritte dazu.
Das vollständige Beispiel für Qt-Dbus finden Sie hier -
http://www.tune2wizard.com/linux-qt-signals-and-slots-qt-d-bus/
quelle
Eine PHP-Implementierung des Adaptermusters zur Abwehr von Injektionsangriffen finden Sie hier:
http://www.php5dp.com/category/design-patterns/adapter-composition/
Einer der interessanten Aspekte des Adaptermusters besteht darin, dass es zwei Varianten gibt: einen Klassenadapter, der auf Mehrfachvererbung basiert, und einen Objektadapter, der auf Komposition basiert. Das obige Beispiel beruht auf der Zusammensetzung.
quelle
Adapterdesignmuster helfen beim Konvertieren der Schnittstelle einer Klasse in die Schnittstelle der Client-Erwartungen.
Beispiel: Sie haben einen Dienst, der das Wetter (in Celsius) zurückgibt, indem Sie den Städtenamen als Eingabewert übergeben. Angenommen, Ihr Kunde möchte die Postleitzahl als Eingabe übergeben und erwartet im Gegenzug die Temperatur der Stadt. Hier benötigen Sie einen Adapter, um dies zu erreichen.
quelle
Ein reales Beispiel kann das Melden von Dokumenten in einer Anwendung sein. Einfacher Code wie hier.
Adapter sind meiner Meinung nach sehr nützlich für die Programmierstruktur.
Ergebnisse werden sein:
quelle
Verwenden Sie den Adapter, wenn Sie eine Schnittstelle haben, die Sie nicht ändern können, die Sie jedoch verwenden müssen. Sehen Sie es so, als wären Sie der neue Mann in einem Büro und Sie können die grauen Haare nicht dazu bringen, Ihren Regeln zu folgen - Sie müssen sich an ihre anpassen. Hier ist ein echtes Beispiel aus einem echten Projekt, an dem ich irgendwann gearbeitet habe, wo die Benutzeroberfläche eine Selbstverständlichkeit ist.
Sie haben eine Anwendung, die alle Zeilen in einer Datei in eine Listendatenstruktur einliest und in einem Raster anzeigt (nennen wir die zugrunde liegende Datenspeicherschnittstelle IDataStore). Der Benutzer kann durch diese Daten navigieren, indem er auf die Schaltflächen "Erste Seite", "Vorherige Seite", "Nächste Seite", "Letzte Seite" klickt. Alles funktioniert gut.
Jetzt muss die Anwendung mit Produktionsprotokollen verwendet werden, die zu groß sind, um in den Speicher eingelesen zu werden, aber der Benutzer muss immer noch darin navigieren! Eine Lösung wäre, einen Cache zu implementieren, in dem die erste Seite, die nächste, die vorherige und die letzte Seite gespeichert werden. Wenn der Benutzer auf "Nächste Seite" klickt, wird die Seite aus dem Cache zurückgegeben und der Cache aktualisiert. Wenn sie auf die letzte Seite klicken, wird die letzte Seite aus dem Cache zurückgegeben. Im Hintergrund haben wir einen Filestream, der die ganze Magie macht. Auf diese Weise haben wir im Gegensatz zur gesamten Datei nur vier Seiten im Speicher.
Sie können einen Adapter verwenden, um diese neue Cache-Funktion zu Ihrer Anwendung hinzuzufügen, ohne dass der Benutzer dies bemerkt. Wir erweitern den aktuellen IDataStore und nennen ihn CacheDataStore. Wenn die zu ladende Datei groß ist, verwenden wir CacheDataStore. Wenn wir die ersten, nächsten, vorherigen und letzten Seiten anfordern, werden die Informationen an unseren Cache weitergeleitet.
Und wer weiß, morgen möchte der Chef anfangen, die Dateien aus einer Datenbanktabelle zu lesen. Alles, was Sie tun, ist, IDataStore wie für Cache auf SQLDataStore zu erweitern und die Verbindung im Hintergrund einzurichten. Wenn sie auf Nächste Seite klicken, generieren Sie die erforderliche SQL-Abfrage, um die nächsten paar hundert Zeilen aus der Datenbank abzurufen.
Im Wesentlichen hat sich die ursprüngliche Oberfläche der Anwendung nicht geändert. Wir haben einfach moderne und coole Funktionen angepasst, um sie zu verwenden und gleichzeitig die alte Benutzeroberfläche beizubehalten.
quelle
Das Beispiel von @Justice o spricht nicht klar über das Adaptermuster. Erweiterung seiner Antwort - Wir haben eine vorhandene Schnittstelle IDataStore, die unser Consumer-Code verwendet, und können diese nicht ändern. Jetzt werden wir gebeten, eine coole neue Klasse aus der XYZ-Bibliothek zu verwenden, die das tut, was wir implementieren möchten, aber wir können diese Klasse nicht ändern, um unseren IDataStore zu erweitern. Haben Sie das Problem bereits gesehen? Erstellen einer neuen Klasse - ADAPTER, die die Schnittstelle implementiert, die unser Consumer-Code erwartet, dh IDataStore, und durch Verwendung der Klasse aus der Bibliothek, deren Funktionen wir benötigen - ADAPTEE, als Mitglied in unserem ADAPTER, können wir erreichen, was wir wollten.
quelle
Gemäß dem Buch „C # 3.0 Design Patterns“ von Judith Bishop verwendete Apple das Adaptermuster, um Mac OS an die Arbeit mit Intel-Produkten anzupassen (siehe Kapitel 4, Auszug hier 2 ).
quelle
Ein Beispiel aus dem Yii-Framework wäre: Yii verwendet den internen Cache unter Verwendung eines Schnittstellen-ICache. https://www.yiiframework.com/doc/api/1.1/ICache
deren Unterschrift ist wie: -
Angenommen, Sie möchten in einem Yii-Projekt die Symfony-Cache-Bibliothek https://packagist.org/packages/symfony/cache mit ihrer Cache-Schnittstelle verwenden, indem Sie diesen Dienst in der Konfiguration der Yii-Dienstkomponenten (Service Locator) https: / definieren /github.com/symfony/cache-contracts/blob/master/CacheInterface.php
Wir sehen, dass der Symfony-Cache eine Schnittstelle mit nur einer get-Methode hat, bei der eine set-Methode und eine andere Signatur für eine get-Methode fehlen, da Symfony die get-Methode auch als Setter verwendet, wenn der zweite aufrufbare Parameter angegeben wird.
Da der Yii-Kern diesen Yii-Cache / diese Yii-Schnittstelle intern verwendet, ist es schwierig (Erweiterung von Yii / YiiBase), wenn nicht an bestimmten Stellen unmöglich, die Aufrufe an diese Schnittstelle neu zu schreiben.
Außerdem ist der Symfony-Cache weder unsere Klasse, sodass wir die Schnittstelle nicht so umschreiben können, dass sie zur Yii-Cache-Oberfläche passt.
Hier kommt also das Adaptermuster zur Rettung. Wir werden ein Mapping = einen Zwischenadapter schreiben, der die Aufrufe der Yii-Cache-Schnittstelle der Symfony-Cache-Schnittstelle zuordnet
Würde so aussehen
quelle
Dies ist ein Beispiel für die Adapterimplementierung:
quelle