JavaRush /Java-Blog /Random-DE /Designmuster-Adapter

Designmuster-Adapter

Veröffentlicht in der Gruppe Random-DE
Hallo! Heute werden wir ein wichtiges neues Thema ansprechen – Muster, oder mit anderen Worten: Designmuster . Was sind Muster? Ich denke, Sie kennen den Ausdruck „Erfinden Sie das Rad nicht neu.“ Beim Programmieren, wie auch in vielen anderen Bereichen, gibt es eine Vielzahl typischer Situationen. Für jeden von ihnen wurden im Prozess der Programmierentwicklung vorgefertigte Arbeitslösungen erstellt. Dies sind Designmuster. Relativ gesehen ist ein Muster ein bestimmtes Beispiel, das eine Lösung für eine Situation bietet wie: „Wenn Ihr Programm etwas tun muss, wie geht das am besten?“ Es gibt viele Muster, ihnen ist ein hervorragendes Buch „Studying Design Patterns“ gewidmet, das Sie unbedingt lesen sollten. Designmuster „Adapter“ – 2Um es möglichst kurz zu formulieren: Ein Muster besteht aus einem gemeinsamen Problem und seiner Lösung, die bereits als eine Art Standard gelten können. In der heutigen Vorlesung lernen wir eines dieser Muster namens „Adapter“ kennen. Der Name ist verräterisch, und Adapter sind Ihnen im wirklichen Leben schon mehr als einmal begegnet. Einer der gebräuchlichsten Adapter sind Kartenleser, mit denen viele Computer und Laptops ausgestattet sind. Adapter-Entwurfsmuster – 3Stellen Sie sich vor, wir hätten eine Art Speicherkarte. Was ist das Problem? Tatsache ist, dass sie nicht weiß, wie man mit einem Computer interagiert. Sie verfügen über keine gemeinsame Schnittstelle. Der Computer verfügt über einen USB-Anschluss, in den Sie jedoch keine Speicherkarte einstecken können. Die Karte kann nicht in den Computer eingesetzt werden, weshalb wir unsere Fotos, Videos und anderen Daten nicht speichern können. Der Kartenleser ist ein Adapter, der dieses Problem löst. Immerhin hat es ein USB-Kabel! Im Gegensatz zur Karte selbst kann der Kartenleser in einen Computer eingesetzt werden. Sie verfügen über eine gemeinsame Schnittstelle mit dem Computer – USB. Sehen wir uns anhand eines Beispiels an, wie es aussehen würde:
public interface USB {

   void connectWithUsbCable();
}
Dies ist unsere USB-Schnittstelle. Die einzige Möglichkeit besteht darin, das USB-Kabel einzustecken:
public class MemoryCard {

   public void insert() {
       System.out.println("Карта памяти успешно вставлена!");
   }

   public void copyData() {
       System.out.println("Данные скопированы на компьютер!");
   }
}
Dies ist unsere Klasse, die die Speicherzuordnung implementiert. Es verfügt bereits über zwei Methoden, die wir benötigen, aber hier liegt das Problem: Die USB-Schnittstelle wird nicht implementiert. Die Karte kann nicht in den USB-Steckplatz eingesteckt werden.
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 macht die KlasseCardReader und warum ist sie ein Adapter? Es ist einfach. Die anzupassende Klasse (Speicherzuordnung) wird zu einem der Felder des Adapters. Das ist logisch, denn auch im wirklichen Leben stecken wir eine Karte in den Kartenleser und sie wird auch Teil davon. Im Gegensatz zu einer Speicherkarte verfügt der Adapter über eine gemeinsame Schnittstelle mit dem Computer. Es verfügt über ein USB-Kabel und kann daher über USB mit anderen Geräten verbunden werden. Daher implementiert unsere Klasse im Programm CardReaderdie USB-Schnittstelle. Aber was passiert in dieser Methode? Und es passiert genau das, was wir brauchen! Der Adapter delegiert die Arbeit an unsere Speicherkarte. Schließlich leistet der Adapter selbst nichts, der 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 Aufgabe erfüllen und Dateien kopieren kann! Unser Adapter ermöglicht dies, indem er eine eigene Schnittstelle (Methode connectWithUsbCable()) für die „Bedürfnisse“ der Speicherkarte bereitstellt. Lassen Sie uns eine Art 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 als Ergebnis erhalten? Konsolenausgabe:
Карта памяти успешно вставлена!
Данные скопированы на компьютер!
Super, unsere Aufgabe wurde erfolgreich abgeschlossen! Hier sind einige zusätzliche Links mit Informationen zum Adaptermuster:

Abstract-Klassen Reader und Writer

Jetzt kehren wir zu unserer Lieblingsbeschäftigung zurück: Wir lernen ein paar neue Klassen für die Arbeit mit Input und Output :) Wie viele davon haben wir schon gelernt, frage ich mich? ReaderHeute werden wir über Klassen und sprechen Writer. Warum mit ihnen? Denn dies hängt mit unserem vorherigen Abschnitt – Adaptern – zusammen. Schauen wir sie uns genauer an. Beginnen wir mit Reader'a. Readerist eine abstrakte Klasse, daher können wir ihre Objekte nicht explizit erstellen. Aber tatsächlich kennen Sie ihn bereits! Schließlich BufferedReadersind die Klassen, die Sie gut kennen InputStreamReader, ihre Erben :)
public class BufferedReader extends Reader {}

public class InputStreamReader extends Reader {}
Eine KlasseInputStreamReader ist also ein klassischer Adapter . Wie Sie sich wahrscheinlich erinnern, können wir ein Objekt an seinen Konstruktor übergeben InputStream. Am häufigsten verwenden wir hierfür eine Variable System.in:
public static void main(String[] args) {

   InputStreamReader inputStreamReader = new InputStreamReader(System.in);
}
Was tut es InputStreamReader? Wie jeder Adapter konvertiert er eine Schnittstelle in eine andere. In diesem Fall wird die Schnittstelle InputStream'a zur Schnittstelle Reader'a. Zuerst hatten wir eine Klasse InputStream. Es funktioniert gut, kann aber nur einzelne Bytes lesen. Darüber hinaus haben wir eine abstrakte Klasse Reader. Es verfügt über eine hervorragende Funktionalität, die wir wirklich brauchen – es kann Zeichen lesen! Wir brauchen diese Gelegenheit natürlich wirklich. Aber hier stehen wir vor einem klassischen Problem, das Adapter normalerweise lösen – Schnittstelleninkompatibilität. Wie äußert es sich? Werfen wir einen Blick direkt in die Oracle-Dokumentation. Hier sind die Klassenmethoden InputStream. Designmuster „Adapter“ – 4Eine Reihe von Methoden ist eine Schnittstelle. Wie Sie sehen, read()verfügt diese Klasse über eine Methode (auch in mehreren Versionen), die jedoch nur Bytes lesen kann: entweder einzelne Bytes oder mehrere Bytes mithilfe eines Puffers. Diese Option passt nicht zu uns – wir wollen Zeichen lesen. Die von uns benötigte Funktionalität ist bereits in der abstrakten Klasse implementiertReader . Dies ist auch in der Dokumentation zu sehen. Adapter-Entwurfsmuster – 5Allerdings sind die Schnittstellen InputStream„a“ und „a“ nicht kompatibel! ReaderWie Sie sehen, unterscheiden sich in allen Methodenimplementierungen read()sowohl die übergebenen Parameter als auch die Rückgabewerte. Und hier brauchen wir es InputStreamReader! Er wird als Adapter zwischen unseren Klassen fungieren. Wie im oben betrachteten Beispiel mit dem Kartenleser übergeben wir das Objekt der Klasse „adapted“ „intern“, also an den Konstruktor der Klasse „Adapter“. Im vorherigen Beispiel haben wir ein Objekt MemoryCarddarin übergeben CardReader. Jetzt übergeben wir das Objekt InputStreaman den Konstruktor InputStreamReader! Als Qualität InputStreamverwenden wir die bereits bekannte Variable System.in:
public static void main(String[] args) {

   InputStreamReader inputStreamReader = new InputStreamReader(System.in);
}
Und tatsächlich: Ein Blick in die Dokumentation InputStreamReaderzeigt, dass die „Anpassung“ gelungen ist :) Jetzt stehen uns Methoden zur Verfügung, mit denen wir Zeichen lesen können. Adapter-Entwurfsmuster – 6Und obwohl unser Objekt System.in(ein an die Tastatur gebundener Thread) dies zunächst nicht zuließ, haben die Ersteller der Sprache dieses Problem durch die Erstellung des Adaptermusters gelöst. Die abstrakte Klasse Readerhat, wie die meisten I/O-Klassen, einen Zwillingsbruder – Writer. Es hat den gleichen großen Vorteil Reader: Es bietet eine praktische Schnittstelle zum Arbeiten mit Symbolen. Bei Ausgabeströmen sehen das Problem und seine Lösung genauso aus wie bei Eingabeströmen. Es gibt eine Klasse OutputStream, die nur Bytes schreiben kann; Es gibt eine abstrakte Klasse Writer, die mit Symbolen arbeiten kann, und es gibt zwei inkompatible Schnittstellen. Auch dieses Problem wird durch das Adaptermuster erfolgreich gelöst. Mithilfe einer Klasse OutputStreamWriterkönnen wir zwei Klassenschnittstellen einfach Writeraneinander „anpassen“ OutputStream. OutputStreamUnd nachdem wir im Konstruktor einen Bytestream erhalten haben , OutputStreamWriterkönnen wir mit dessen Hilfe jedoch Zeichen schreiben, keine Bytes!
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 ein Zeichen mit dem Code 32144 - 綐 in unsere Datei geschrieben, sodass wir nicht mehr mit Bytes arbeiten müssen :) Das ist alles für heute, wir sehen uns in den nächsten Vorlesungen! :) :)
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION