CodeGym /Java-Blog /Random-DE /Adapter-Designmuster
Autor
Pavlo Plynko
Java Developer at CodeGym

Adapter-Designmuster

Veröffentlicht in der Gruppe Random-DE
Hallo! Heute werden wir ein wichtiges neues Thema ansprechen: Designmuster . Was sind diese Muster? Ich denke, Sie kennen sicher den Ausdruck „ Das Rad nicht neu erfinden “. Beim Programmieren, wie auch in vielen anderen Bereichen, gibt es eine Vielzahl gemeinsamer Situationen. Im Zuge der Weiterentwicklung der Softwareentwicklung wurden für jeden von ihnen vorgefertigte Lösungen erstellt, die funktionieren. Diese Lösungen werden Entwurfsmuster genannt. Konventionell handelt es sich bei einem Muster um eine Lösung, die wie folgt formuliert ist: „Wenn Sie X in Ihrem Programm ausführen müssen, ist dies der beste Weg, dies zu tun.“ Es gibt viele Muster. Ihnen ist das hervorragende Buch „Head First Design Patterns“ gewidmet, mit dem Sie sich unbedingt vertraut machen sollten. Adapter-Entwurfsmuster – 2Vereinfacht ausgedrückt besteht ein Muster aus einem gemeinsamen Problem und einer entsprechenden Lösung, die als eine Art Standard betrachtet werden kann. In der heutigen Lektion werden wir eines dieser Muster kennenlernen: Adapter. Der Name ist Programm und Adapter sind Ihnen im wirklichen Leben schon oft begegnet. Zu den gebräuchlichsten Adaptern gehören die Kartenleser, über die viele Computer und Laptops verfügen. Adapter-Entwurfsmuster – 3Angenommen, wir haben eine Art Speicherkarte. Also, was ist das Problem? Es weiß nicht, wie es mit dem Computer interagieren soll. Sie haben keine gemeinsame Schnittstelle. Der Computer verfügt über einen USB-Anschluss, aber wir können die Speicherkarte nicht hineinstecken. Die Karte lässt sich nicht an den Computer anschließen, sodass wir unsere Fotos, Videos und andere Daten nicht speichern können. Ein Kartenleser ist ein Adapter, der dieses Problem löst. Immerhin hat es ein USB-Kabel! Im Gegensatz zur Karte selbst kann der Kartenleser an den Computer angeschlossen werden. Sie teilen sich eine gemeinsame Schnittstelle mit dem Computer: USB. Mal sehen, wie das in der Praxis aussieht:

public interface USB { 

   void connectWithUsbCable(); 
}
Dies ist unsere USB-Schnittstelle mit nur einer Methode zum Anschluss über USB.

public class MemoryCard { 

   public void insert() { 
       System.out.println("Memory card successfully inserted!"); 
   } 

   public void copyData() { 
       System.out.println("The data has been copied to the computer!"); 
   } 
}
Dies ist unsere Klasse, die die Speicherkarte darstellt. Es verfügt bereits über die beiden Methoden, die wir benötigen, aber hier liegt das Problem: Die USB-Schnittstelle wird nicht implementiert. Die Karte lässt sich nicht in den USB-Anschluss einstecken.

public class CardReader implements USB { 

   private MemoryCard memoryCard; 

   public CardReader(MemoryCard memoryCard) { 
       this.memoryCard = memoryCard; 
   } 

   @Override 
   public void connectWithUsbCable() { 
       this.memoryCard.insert(); 
       this.memoryCard.copyData(); 
   } 
}
Und hier ist unser Adapter! Was bedeutet dasCardReaderKlasse machen und was genau macht einen Adapter aus? Es ist alles einfach. Die anzupassende Klasse (MemoryCard) wird zu einem der Felder des Adapters. Das macht Sinn. Wenn wir im wirklichen Leben eine Speicherkarte in einen Kartenleser stecken, wird sie auch zu einem Teil davon. Im Gegensatz zur Speicherkarte teilt sich der Adapter eine Schnittstelle mit dem Computer. Es verfügt über ein USB-Kabel, d. h. es kann über USB mit anderen Geräten verbunden werden. Deshalb implementiert unsere CardReader-Klasse die USB-Schnittstelle. Aber was genau passiert in dieser Methode? Genau das, was wir brauchen! Der Adapter delegiert die Arbeit an unsere Speicherkarte. Tatsächlich macht der Adapter selbst nichts. Ein Kartenleser verfügt über keine eigenständige Funktionalität. Seine Aufgabe besteht lediglich darin, den Computer und die Speicherkarte zu verbinden, damit die Karte ihre Arbeit erledigen kann – Dateien kopieren!connectWithUsbCable()Methode), um den „Bedürfnissen“ der Speicherkarte gerecht zu werden. Lassen Sie uns ein Client-Programm erstellen, das eine Person simuliert, die Daten von einer Speicherkarte kopieren möchte:

public class Main { 

   public static void main(String[] args) { 

       USB cardReader = new CardReader(new MemoryCard()); 
       cardReader.connectWithUsbCable(); 
   } 
}
Was haben wir also bekommen? Konsolenausgabe:

Memory card successfully inserted! 
The data has been copied to the computer!
Exzellent. Wir haben unser Ziel erreicht! Hier ist ein Link zu einem Video mit Informationen zum Adaptermuster:

Abstrakte Klassen für Leser und Schriftsteller

Jetzt kehren wir zu unserer Lieblingsbeschäftigung zurück: dem Kennenlernen einiger neuer Klassen für die Arbeit mit Eingabe und Ausgabe :) Ich frage mich, wie viele wir bereits kennengelernt haben. Heute werden wir über die Reader und- WriterKlassen sprechen. Warum gerade diese Kurse? Weil sie mit unserem vorherigen Abschnitt über Adapter zusammenhängen. Lassen Sie uns sie genauer untersuchen. Wir beginnen mit  Reader. Readerist eine abstrakte Klasse, daher können wir keine Objekte explizit erstellen.   Aber Sie kennen es eigentlich schon! Schließlich kennen Sie die Klassen BufferedReaderund InputStreamReader, die ihre Nachkommen sind, gut :)

public class BufferedReader extends Reader { 
… 
} 

public class InputStreamReader extends Reader { 
… 
}
Die InputStreamReaderKlasse ist ein klassischer Adapter. Wie Sie sich wahrscheinlich erinnern, können wir ein InputStreamObjekt an seinen Konstruktor übergeben. Dazu verwenden wir normalerweise die System.inVariable:

public static void main(String[] args) { 

   InputStreamReader inputStreamReader = new InputStreamReader(System.in); 
}
Aber was InputStreamReadermacht? Wie jeder Adapter konvertiert er eine Schnittstelle in eine andere.  In diesem Fall die InputStreamSchnittstelle zur ReaderSchnittstelle. Zunächst haben wir die InputStreamKlasse. Es funktioniert gut, aber man kann damit nur einzelne Bytes lesen. Darüber hinaus haben wir eine Readerabstrakte Klasse. Es verfügt über einige sehr nützliche Funktionen – es kann Zeichen lesen! Wir brauchen diese Fähigkeit auf jeden Fall. Aber hier stehen wir vor dem klassischen Problem, das normalerweise durch Adapter gelöst wird – inkompatible Schnittstellen. Was bedeutet das? Werfen wir einen Blick auf die Oracle-Dokumentation. Hier sind die Methoden der InputStreamKlasse. Adapter-Entwurfsmuster – 4Eine Reihe von Methoden ist genau das, was eine Schnittstelle ist. Wie Sie sehen können, hat diese Klasse eineread()Methode (tatsächlich gibt es einige Varianten), aber sie kann nur Bytes lesen: entweder einzelne Bytes oder mehrere Bytes unter Verwendung eines Puffers. Aber diese Option passt nicht zu uns – wir wollen Zeichen lesen. Wir benötigen die Funktionalität, die bereits in der Readerabstrakten Klasse implementiert ist . Wir können dies auch in der Dokumentation sehen. Adapter-Entwurfsmuster – 5Allerdings sind die InputStreamund-  ReaderSchnittstellen nicht kompatibel! Wie Sie sehen, read()verfügt jede Implementierung der Methode über unterschiedliche Parameter und Rückgabewerte. Und genau hier brauchen wir InputStreamReader! Es wird als Adapter zwischen unseren Klassen fungieren .Wie im Beispiel mit dem Kartenleser, das wir oben betrachtet haben, platzieren wir eine Instanz der anzupassenden Klasse „innerhalb“ der Adapterklasse, dh wir übergeben eine an ihren Konstruktor. Im vorherigen Beispiel haben wir ein MemoryCardObjekt hineingelegt CardReader. Jetzt übergeben wir ein InputStream Objekt an den InputStreamReaderKonstruktor! Wir verwenden unsere bekannte System.inVariable als InputStream:

public static void main(String[] args) { 

   InputStreamReader inputStreamReader = new InputStreamReader(System.in); 
}
Und tatsächlich, wenn wir uns die Dokumentation für ansehen InputStreamReader, können wir sehen, dass die Adaption gelungen ist :) Jetzt stehen uns Methoden zum Lesen von Zeichen zur Verfügung. Adapter-Entwurfsmuster – 6Und obwohl unser System.inObjekt (der an die Tastatur gebundene Stream) dies zunächst nicht zuließ, haben die Entwickler der Sprache dieses Problem durch die Implementierung des Adaptermusters gelöst. Die Readerabstrakte Klasse hat, wie die meisten I/O-Klassen, einen Zwillingsbruder –  Writer. Es hat den gleichen großen Vorteil wie  Reader – es bietet eine praktische Schnittstelle für die Arbeit mit Charakteren. Bei Ausgabeströmen sehen das Problem und seine Lösung genauso aus wie bei Eingabeströmen. Es gibt eine OutputStreamKlasse, die nur Bytes schreiben kann, es gibt eineWriterabstrakte Klasse, die weiß, wie man mit Zeichen arbeitet, und es gibt zwei inkompatible Schnittstellen. Auch dieses Problem wird durch das Adaptermuster gelöst. Wir nutzen die OutputStreamWriterKlasse, um die beiden Schnittstellen der Writer und-  OutputStream Klassen einfach aneinander anzupassen. Nachdem wir einen OutputStreamByte-Stream an den Konstruktor übergeben haben, können wir einen verwenden, OutputStreamWriterum Zeichen statt Bytes zu schreiben!

import java.io.*; 

public class Main { 

   public static void main(String[] args) throws IOException { 

       OutputStreamWriter streamWriter = new OutputStreamWriter(new FileOutputStream("C:\\Users\\Username\\Desktop\\test.txt")); 
       streamWriter.write(32144); 
       streamWriter.close();
   } 
}
Wir haben das Zeichen mit dem Code 32144 (綐) in unsere Datei geschrieben, sodass wir nicht mehr mit Bytes arbeiten müssen :) Das war's für heute. Wir sehen uns in den nächsten Lektionen! :) :)
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION