JavaRush /Java-Blog /Random-DE /Entwurfsmuster in Java
Viacheslav
Level 3

Entwurfsmuster in Java

Veröffentlicht in der Gruppe Random-DE
Muster oder Entwurfsmuster sind ein oft übersehener Teil der Arbeit eines Entwicklers und erschweren die Wartung und Anpassung des Codes an neue Anforderungen. Ich schlage vor, dass Sie sich ansehen, was es ist und wie es im JDK verwendet wird. Natürlich gibt es alle Grundmuster in der einen oder anderen Form schon seit langem. Sehen wir sie uns in dieser Rezension an.
Designmuster in Java - 1
Inhalt:

Vorlagen

Eine der häufigsten Anforderungen bei offenen Stellen ist „Musterkenntnis“. Zunächst lohnt es sich, eine einfache Frage zu beantworten: „Was ist ein Designmuster?“ Muster wird aus dem Englischen als „Vorlage“ übersetzt. Das heißt, das ist ein bestimmtes Muster, nach dem wir etwas tun. Das Gleiche gilt auch für die Programmierung. Es gibt einige etablierte Best Practices und Ansätze zur Lösung häufiger Probleme. Jeder Programmierer ist ein Architekt. Selbst wenn Sie nur wenige Klassen oder sogar nur eine erstellen, hängt es von Ihnen ab, wie lange der Code unter sich ändernden Anforderungen überleben kann und wie bequem er von anderen verwendet werden kann. Und hier helfen Kenntnisse über Vorlagen, denn... Auf diese Weise können Sie schnell verstehen, wie Sie Code am besten schreiben, ohne ihn neu zu schreiben. Wie Sie wissen, sind Programmierer faule Leute und es ist einfacher, etwas sofort gut zu schreiben, als es mehrmals zu wiederholen. Muster können auch Algorithmen ähneln. Aber sie haben einen Unterschied. Der Algorithmus besteht aus spezifischen Schritten, die die notwendigen Aktionen beschreiben. Muster beschreiben lediglich die Vorgehensweise, nicht jedoch die Umsetzungsschritte. Die Muster sind unterschiedlich, weil... verschiedene Probleme lösen. Typischerweise werden folgende Kategorien unterschieden:
  • Generativ

    Diese Muster lösen das Problem, die Objekterstellung flexibel zu gestalten

  • Strukturell

    Diese Muster lösen das Problem des effektiven Aufbaus von Verbindungen zwischen Objekten

  • Verhalten

    Diese Muster lösen das Problem einer effektiven Interaktion zwischen Objekten

Um Beispiele zu betrachten, schlage ich die Verwendung des Online-Code- Compilers repl.it vor.
Entwurfsmuster in Java - 2

Schöpfungsmuster

Beginnen wir am Anfang des Lebenszyklus von Objekten – mit der Erstellung von Objekten. Generative Vorlagen helfen dabei, Objekte bequemer zu erstellen und bieten Flexibilität in diesem Prozess. Einer der bekanntesten ist „ Builder “. Mit diesem Muster können Sie komplexe Objekte Schritt für Schritt erstellen. In Java ist das bekannteste Beispiel StringBuilder:
class Main {
  public static void main(String[] args) {
    StringBuilder builder = new StringBuilder();
    builder.append("Hello");
    builder.append(',');
    builder.append("World!");
    System.out.println(builder.toString());
  }
}
Ein weiterer bekannter Ansatz zum Erstellen eines Objekts besteht darin, die Erstellung in eine separate Methode zu verschieben. Diese Methode wird sozusagen zu einer Objektfabrik. Deshalb heißt das Muster „ Factory- Methode“. In Java beispielsweise ist seine Wirkung in der Klasse zu sehen java.util.Calendar. Die Klasse selbst Calendarist abstrakt und um sie zu erstellen, wird die Methode verwendet getInstance:
import java.util.*;
class Main {
  public static void main(String[] args) {
    Calendar calendar = Calendar.getInstance();
    System.out.println(calendar.getTime());
    System.out.println(calendar.getClass().getCanonicalName());
  }
}
Dies liegt häufig daran, dass die Logik hinter der Objekterstellung komplex sein kann. Im obigen Fall greifen wir beispielsweise auf die Basisklasse zu Calendarund es wird eine Klasse erstellt GregorianCalendar. Wenn wir uns den Konstruktor ansehen, können wir sehen, dass je nach Bedingungen unterschiedliche Implementierungen erstellt werden Calendar. Aber manchmal reicht eine Fabrikmethode nicht aus. Manchmal müssen Sie verschiedene Objekte erstellen, damit sie zusammenpassen. Dabei hilft uns eine weitere Vorlage – „ Abstract Factory “. Und dann müssen wir verschiedene Fabriken an einem Ort schaffen. Gleichzeitig liegt der Vorteil darin, dass uns die Details der Umsetzung nicht wichtig sind, d.h. Es spielt keine Rolle, welche konkrete Fabrik wir bekommen. Die Hauptsache ist, dass es die richtigen Umsetzungen schafft. Superbeispiel:
Entwurfsmuster in Java - 3
Das heißt, abhängig von der Umgebung (Betriebssystem) erhalten wir eine bestimmte Fabrik, die kompatible Elemente erstellt. Als Alternative zum Ansatz, durch jemand anderen zu erschaffen, können wir das „ Prototyp “-Muster verwenden. Sein Wesen ist einfach: Neue Objekte werden nach dem Vorbild und Abbild bereits vorhandener Objekte erstellt, d. h. nach ihrem Prototyp. In Java ist jedem dieses Muster begegnet – das ist die Verwendung einer Schnittstelle java.lang.Cloneable:
class Main {
  public static void main(String[] args) {
    class CloneObject implements Cloneable {
      @Override
      protected Object clone() throws CloneNotSupportedException {
        return new CloneObject();
      }
    }
    CloneObject obj = new CloneObject();
    try {
      CloneObject pattern = (CloneObject) obj.clone();
    } catch (CloneNotSupportedException e) {
      //Do something
    }
  }
}
Wie Sie sehen, weiß der Anrufer nicht, wie die clone. Das heißt, die Erstellung eines Objekts auf der Grundlage eines Prototyps liegt in der Verantwortung des Objekts selbst. Dies ist nützlich, da der Benutzer dadurch nicht an die Implementierung des Vorlagenobjekts gebunden ist. Nun, das allerletzte auf dieser Liste ist das „Singleton“-Muster. Sein Zweck ist einfach: eine einzelne Instanz des Objekts für die gesamte Anwendung bereitzustellen. Dieses Muster ist interessant, da es häufig Multithreading-Probleme aufweist. Weitere Informationen finden Sie in den folgenden Artikeln:
Designmuster in Java - 4

Strukturelle Muster

Mit der Erstellung von Objekten wurde es klarer. Jetzt ist es an der Zeit, strukturelle Muster zu betrachten. Ihr Ziel ist es, einfach zu unterstützende Klassenhierarchien und deren Beziehungen aufzubauen. Eines der ersten und bekanntesten Muster ist „ Deputy “ (Proxy). Der Proxy verfügt über dieselbe Schnittstelle wie das reale Objekt, sodass es für den Client keinen Unterschied macht, ob er über den Proxy oder direkt arbeitet. Das einfachste Beispiel ist java.lang.reflect.Proxy :
import java.util.*;
import java.lang.reflect.*;
class Main {
  public static void main(String[] arguments) {
    final Map<String, String> original = new HashMap<>();
    InvocationHandler proxy = (obj, method, args) -> {
      System.out.println("Invoked: " + method.getName());
      return method.invoke(original, args);
    };
    Map<String, String> proxyInstance = (Map) Proxy.newProxyInstance(
        original.getClass().getClassLoader(),
        original.getClass().getInterfaces(),
        proxy);
    proxyInstance.put("key", "value");
    System.out.println(proxyInstance.get("key"));
  }
}
Wie Sie sehen können, ist das Original in unserem Beispiel dasjenige, HashMapdas die Schnittstelle implementiert Map. Als nächstes erstellen wir einen Proxy, der den ursprünglichen Proxy HashMapfür den Client-Teil ersetzt, der die Methoden putund aufruft getund während des Aufrufs unsere eigene Logik hinzufügt. Wie wir sehen können, erfolgt die Interaktion im Muster über Schnittstellen. Aber manchmal reicht ein Ersatz nicht aus. Und dann kann das „ Decorator “-Muster verwendet werden. Ein Dekorator wird auch Wrapper oder Wrapper genannt. Proxy und Decorator sind sich sehr ähnlich, aber wenn Sie sich das Beispiel ansehen, werden Sie den Unterschied erkennen:
import java.util.*;
class Main {
  public static void main(String[] arguments) {
    List<String> list = new ArrayList<>();
    List<String> decorated = Collections.checkedList(list, String.class);
    decorated.add("2");
    list.add("3");
    System.out.println(decorated);
  }
}
Im Gegensatz zu einem Proxy umschließt ein Dekorator etwas, das als Eingabe übergeben wird. Ein Proxy kann sowohl akzeptieren, was weitergeleitet werden muss, als auch die Lebensdauer des Proxy-Objekts verwalten (z. B. ein Proxy-Objekt erstellen). Es gibt noch ein weiteres interessantes Muster – „ Adapter “. Es ähnelt einem Dekorator – der Dekorator nimmt ein Objekt als Eingabe und gibt einen Wrapper über dieses Objekt zurück. Der Unterschied besteht darin, dass das Ziel nicht darin besteht, die Funktionalität zu ändern, sondern eine Schnittstelle an eine andere anzupassen. Java hat dafür ein sehr klares Beispiel:
import java.util.*;
class Main {
  public static void main(String[] arguments) {
    String[] array = {"One", "Two", "Three"};
    List<String> strings = Arrays.asList(array);
    strings.set(0, "1");
    System.out.println(Arrays.toString(array));
  }
}
Am Eingang haben wir ein Array. Als nächstes erstellen wir einen Adapter, der das Array zur Schnittstelle bringt List. Wenn wir damit arbeiten, arbeiten wir tatsächlich mit einem Array. Daher funktioniert das Hinzufügen von Elementen nicht, weil... Das ursprüngliche Array kann nicht geändert werden. Und in diesem Fall erhalten wir UnsupportedOperationException. Der nächste interessante Ansatz zur Entwicklung einer Klassenstruktur ist das Composite- Muster . Das Interessante daran ist, dass ein bestimmter Satz von Elementen, die eine Schnittstelle verwenden, in einer bestimmten baumartigen Hierarchie angeordnet ist. Durch den Aufruf einer Methode für ein übergeordnetes Element erhalten wir einen Aufruf dieser Methode für alle erforderlichen untergeordneten Elemente. Ein Paradebeispiel für dieses Muster ist die Benutzeroberfläche (sei es java.awt oder JSF):
import java.awt.*;
class Main {
  public static void main(String[] arguments) {
    Container container = new Container();
    Component component = new java.awt.Component(){};
    System.out.println(component.getComponentOrientation().isLeftToRight());
    container.add(component);
    container.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
    System.out.println(component.getComponentOrientation().isLeftToRight());
  }
}
Wie wir sehen können, haben wir dem Container eine Komponente hinzugefügt. Und dann haben wir den Container gebeten, die neue Ausrichtung der Komponenten anzuwenden. Und da der Container wusste, aus welchen Komponenten er besteht, delegierte er die Ausführung dieses Befehls an alle untergeordneten Komponenten. Ein weiteres interessantes Muster ist das „ Brücken “-Muster. Es wird so genannt, weil es eine Verbindung oder Brücke zwischen zwei verschiedenen Klassenhierarchien beschreibt. Eine dieser Hierarchien wird als Abstraktion und die andere als Implementierung betrachtet. Dies wird dadurch hervorgehoben, dass die Abstraktion selbst keine Aktionen ausführt, sondern diese Ausführung an die Implementierung delegiert. Dieses Muster wird häufig verwendet, wenn es „Kontroll“-Klassen und mehrere Arten von „Plattform“-Klassen gibt (z. B. Windows, Linux usw.). Bei diesem Ansatz erhält eine dieser Hierarchien (Abstraktion) einen Verweis auf Objekte einer anderen Hierarchie (Implementierung) und delegiert die Hauptarbeit an diese. Da alle Implementierungen einer gemeinsamen Schnittstelle folgen, können sie innerhalb der Abstraktion ausgetauscht werden. Ein klares Beispiel hierfür ist in Java java.awt:
Entwurfsmuster in Java - 5
Weitere Informationen finden Sie im Artikel „ Muster in Java AWT “. Unter den Strukturmustern möchte ich auch das Muster „ Fassade “ erwähnen. Sein Kern besteht darin, die Komplexität der Verwendung der Bibliotheken/Frameworks hinter dieser API hinter einer praktischen und übersichtlichen Schnittstelle zu verbergen. Als Beispiel können Sie beispielsweise JSF oder EntityManager von JPA verwenden. Es gibt auch ein anderes Muster namens „ Fliegengewicht “. Sein Wesen besteht darin, dass, wenn verschiedene Objekte denselben Zustand haben, dieser verallgemeinert und nicht in jedem Objekt, sondern an einem Ort gespeichert werden kann. Und dann kann jedes Objekt auf einen gemeinsamen Teil verweisen, was die Speicherkosten für die Speicherung senkt. Dieses Muster beinhaltet häufig das Vorab-Zwischenspeichern oder Verwalten eines Objektpools. Interessanterweise kennen wir dieses Muster auch von Anfang an:
Designmuster in Java - 6
In gleicher Weise kann hier auch ein Pool von Zeichenfolgen einbezogen werden. Sie können den Artikel zu diesem Thema lesen: „ Fliegengewichts-Designmuster “.
Entwurfsmuster in Java - 7

Verhaltensmuster

Wir haben also herausgefunden, wie Objekte erstellt und Verbindungen zwischen Klassen organisiert werden können. Das Interessanteste, was noch übrig ist, besteht darin, Flexibilität bei der Änderung des Verhaltens von Objekten bereitzustellen. Und Verhaltensmuster werden uns dabei helfen. Eines der am häufigsten genannten Muster ist das „ Strategie “-Muster. Hier beginnt das Studium der Muster im Buch „ Head First. Design Patterns “. Mithilfe des „Strategie“-Musters können wir in einem Objekt speichern, wie wir die Aktion ausführen, d. h. Das darin enthaltene Objekt speichert eine Strategie, die während der Codeausführung geändert werden kann. Dies ist ein Muster, das wir häufig verwenden, wenn wir einen Komparator verwenden:
import java.util.*;
class Main {
  public static void main(String[] args) {
    List<String> data = Arrays.asList("Moscow", "Paris", "NYC");
    Comparator<String> comparator = Comparator.comparingInt(String::length);
    Set dataSet = new TreeSet(comparator);
    dataSet.addAll(data);
    System.out.println("Dataset : " + dataSet);
  }
}
Vor uns - TreeSet. Es hat das Verhalten, die TreeSetReihenfolge der Elemente beizubehalten, d. h. sortiert sie (da es sich um ein SortedSet handelt). Dieses Verhalten hat eine Standardstrategie, die wir im JavaDoc sehen: Sortieren in „natürlicher Reihenfolge“ (bei Strings ist dies die lexikografische Reihenfolge). Dies passiert, wenn Sie einen parameterlosen Konstruktor verwenden. Aber wenn wir die Strategie ändern wollen, können wir passen Comparator. In diesem Beispiel können wir unseren Satz als erstellen new TreeSet(comparator)und dann ändert sich die Reihenfolge der Speicherelemente (Speicherstrategie) in die im Komparator angegebene. Interessanterweise gibt es fast das gleiche Muster namens „ State “. Das Muster „Zustand“ besagt, dass wir, wenn wir im Hauptobjekt ein Verhalten haben, das vom Zustand dieses Objekts abhängt, den Zustand selbst als Objekt beschreiben und das Zustandsobjekt ändern können. Und delegieren Sie Anrufe vom Hauptobjekt an den Staat. Ein weiteres Muster, das uns aus dem Studium der Grundlagen der Java-Sprache bekannt ist, ist das „ Befehl “-Muster. Dieses Entwurfsmuster legt nahe, dass unterschiedliche Befehle als unterschiedliche Klassen dargestellt werden können. Dieses Muster ist dem Strategiemuster sehr ähnlich. Aber im Strategiemuster haben wir neu definiert, wie eine bestimmte Aktion ausgeführt werden würde (z. B. Sortieren in TreeSet). Im Muster „Befehl“ definieren wir neu, welche Aktion ausgeführt wird. Der Musterbefehl begleitet uns jeden Tag, wenn wir Threads verwenden:
import java.util.*;
class Main {
  public static void main(String[] args) {
    Runnable command = () -> {
      System.out.println("Command action");
    };
    Thread th = new Thread(command);
    th.start();
  }
}
Wie Sie sehen, definiert der Befehl eine Aktion oder einen Befehl, der in einem neuen Thread ausgeführt wird. Es lohnt sich auch, das Muster der „ Verantwortungskette “ in Betracht zu ziehen. Auch dieses Muster ist sehr einfach. Dieses Muster besagt, dass Sie Handler in einer Kette sammeln können, wenn etwas verarbeitet werden muss. Dieses Muster wird beispielsweise häufig in Webservern verwendet. Am Eingang hat der Server eine Anfrage vom Benutzer. Diese Anfrage durchläuft dann die Verarbeitungskette. Diese Handlerkette umfasst Filter (z. B. keine Anfragen von einer Blacklist mit IP-Adressen akzeptieren), Authentifizierungshandler (nur autorisierte Benutzer zulassen), einen Anforderungsheader-Handler, einen Caching-Handler usw. Aber es gibt ein einfacheres und verständlicheres Beispiel in Java java.util.logging:
import java.util.logging.*;
class Main {
  public static void main(String[] args) {
    Logger logger = Logger.getLogger(Main.class.getName());
    ConsoleHandler consoleHandler = new ConsoleHandler(){
		@Override
            public void publish(LogRecord record) {
                System.out.println("LogRecord обработан");
            }
        };
    logger.addHandler(consoleHandler);
    logger.info("test");
  }
}
Wie Sie sehen können, werden Handler zur Liste der Logger-Handler hinzugefügt. Wenn ein Logger eine Nachricht zur Verarbeitung empfängt, durchläuft jede dieser Nachrichten eine Handlerkette (von logger.getHandlers) für diesen Logger. Ein weiteres Muster, das wir jeden Tag sehen, ist „ Iterator “. Sein Kern besteht darin, eine Sammlung von Objekten (z. B. eine Klasse, die eine Datenstruktur darstellt List) zu trennen und diese Sammlung zu durchlaufen.
import java.util.*;
class Main {
  public static void main(String[] args) {
    List<String> data = Arrays.asList("Moscow", "Paris", "NYC");
    Iterator<String> iterator = data.iterator();
    while (iterator.hasNext()) {
      System.out.println(iterator.next());
    }
  }
}
Wie Sie sehen, ist der Iterator nicht Teil der Sammlung, sondern wird durch eine separate Klasse dargestellt, die die Sammlung durchläuft. Der Benutzer des Iterators weiß möglicherweise nicht einmal, über welche Sammlung er iteriert, d. h. Welche Sammlung besucht er? Es lohnt sich, über das Muster „ Besucher “ nachzudenken. Das Besuchermuster ist dem Iteratormuster sehr ähnlich. Dieses Muster hilft Ihnen, die Struktur von Objekten zu umgehen und Aktionen für diese Objekte auszuführen. Sie unterscheiden sich eher im Konzept. Der Iterator durchläuft die Sammlung, sodass es dem Client, der den Iterator verwendet, egal ist, was die Sammlung enthält, sondern nur die Elemente in der Sequenz sind wichtig. Der Besucher bedeutet, dass es eine bestimmte Hierarchie oder Struktur der Objekte gibt, die wir besuchen. Beispielsweise können wir eine getrennte Verzeichnisverarbeitung und eine getrennte Dateiverarbeitung verwenden. Java verfügt über eine sofort einsatzbereite Implementierung dieses Musters in der Form java.nio.file.FileVisitor:
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.io.*;
class Main {
  public static void main(String[] args) {
    SimpleFileVisitor visitor = new SimpleFileVisitor() {
      @Override
      public FileVisitResult visitFile(Object file, BasicFileAttributes attrs) throws IOException {
        System.out.println("File:" + file.toString());
        return FileVisitResult.CONTINUE;
      }
    };
    Path pathSource = Paths.get(System.getProperty("java.io.tmpdir"));
    try {
      Files.walkFileTree(pathSource, visitor);
    } catch (AccessDeniedException e) {
      // skip
    } catch (IOException e) {
      // Do something
    }
  }
}
Manchmal müssen einige Objekte auf Änderungen in anderen Objekten reagieren, und dann hilft uns das „Observer“-Muster . Die bequemste Möglichkeit besteht darin, einen Abonnementmechanismus bereitzustellen, der es einigen Objekten ermöglicht, Ereignisse in anderen Objekten zu überwachen und darauf zu reagieren. Dieses Muster wird häufig in verschiedenen Listenern und Observern verwendet, die auf unterschiedliche Ereignisse reagieren. Als einfaches Beispiel können wir uns an die Implementierung dieses Musters aus der ersten Version von JDK erinnern:
import java.util.*;
class Main {
  public static void main(String[] args) {
    Observer observer = (obj, arg) -> {
      System.out.println("Arg: " + arg);
    };
    Observable target = new Observable(){
      @Override
      public void notifyObservers(Object arg) {
        setChanged();
        super.notifyObservers(arg);
      }
    };
    target.addObserver(observer);
    target.notifyObservers("Hello, World!");
  }
}
Es gibt ein weiteres nützliches Verhaltensmuster – „ Mediator “. Dies ist nützlich, da es in komplexen Systemen hilft, die Verbindung zwischen verschiedenen Objekten zu entfernen und alle Interaktionen zwischen Objekten an ein Objekt zu delegieren, das als Vermittler fungiert. Eine der auffälligsten Anwendungen dieses Musters ist Spring MVC, das dieses Muster verwendet. Mehr dazu können Sie hier lesen: „ Spring: Mediator Pattern “. Dasselbe sieht man oft an Beispielen java.util.Timer:
import java.util.*;
class Main {
  public static void main(String[] args) {
    Timer mediator = new Timer("Mediator");
    TimerTask command = new TimerTask() {
      @Override
      public void run() {
        System.out.println("Command pattern");
        mediator.cancel();
      }
    };
    mediator.schedule(command, 1000);
  }
}
Das Beispiel ähnelt eher einem Befehlsmuster. Und die Essenz des „Mediator“-Musters liegt in der Umsetzung von Timer„a“ verborgen. Im Timer gibt es eine Aufgabenwarteschlange TaskQueueund einen Thread TimerThread. Als Clients dieser Klasse interagieren wir nicht mit ihnen, sondern mit Timerdem Objekt, das als Reaktion auf unseren Aufruf seiner Methoden auf die Methoden anderer Objekte zugreift, deren Vermittler es ist. Äußerlich mag es „Fassade“ sehr ähnlich erscheinen. Der Unterschied besteht jedoch darin, dass bei Verwendung einer Fassade die Komponenten nicht wissen, dass die Fassade existiert, und miteinander kommunizieren. Und wenn „Mediator“ verwendet wird, kennen und nutzen die Komponenten den Vermittler, nehmen jedoch keinen direkten Kontakt zueinander auf. Es lohnt sich, das Muster „ Vorlagenmethode “ in Betracht zu ziehen. Das Muster ist schon aus dem Namen ersichtlich. Die Quintessenz ist, dass der Code so geschrieben ist, dass den Benutzern des Codes (Entwicklern) eine Algorithmusvorlage zur Verfügung gestellt wird, deren Schritte neu definiert werden dürfen. Dadurch können Codebenutzer nicht den gesamten Algorithmus schreiben, sondern nur darüber nachdenken, wie sie den einen oder anderen Schritt dieses Algorithmus korrekt ausführen. Beispielsweise verfügt Java über eine abstrakte Klasse AbstractList, die das Verhalten eines Iterators definiert List. Der Iterator selbst verwendet jedoch Blattmethoden wie: get, set, remove. Das Verhalten dieser Methoden wird vom Entwickler der Nachkommen bestimmt AbstractList. Somit AbstractListist der Iterator in - eine Vorlage für den Algorithmus zum Iterieren über ein Blatt. Und Entwickler bestimmter Implementierungen AbstractListändern das Verhalten dieser Iteration, indem sie das Verhalten bestimmter Schritte definieren. Das letzte Muster, das wir analysieren, ist das „ Snapshot “-Muster (Momento). Sein Wesen besteht darin, einen bestimmten Zustand eines Objekts zu bewahren und diesen Zustand wiederherzustellen. Das bekannteste Beispiel aus dem JDK ist die Objektserialisierung, d. h. java.io.Serializable. Schauen wir uns ein Beispiel an:
import java.io.*;
import java.util.*;
class Main {
  public static void main(String[] args) throws IOException {
    ArrayList<String> list = new ArrayList<>();
    list.add("test");
    // Save State
    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    try (ObjectOutputStream out = new ObjectOutputStream(stream)) {
      out.writeObject(list);
    }
    // Load state
    byte[] bytes = stream.toByteArray();
    InputStream inputStream = new ByteArrayInputStream(bytes);
    try (ObjectInputStream in = new ObjectInputStream(inputStream)) {
      List<String> listNew = (List<String>) in.readObject();
      System.out.println(listNew.get(0));
    } catch (ClassNotFoundException e) {
      // Do something. Can't find class fpr saved state
    }
  }
}
Designmuster in Java - 8

Abschluss

Wie wir im Test gesehen haben, gibt es eine große Vielfalt an Mustern. Jeder von ihnen löst sein eigenes Problem. Und die Kenntnis dieser Muster kann Ihnen helfen, rechtzeitig zu verstehen, wie Sie Ihr System so schreiben müssen, dass es flexibel, wartbar und resistent gegen Änderungen ist. Und zum Schluss noch ein paar Links für einen tieferen Einblick: #Wjatscheslaw
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION