Die Geschichte, dass GoF-Designmuster nichts anderes sind als "Einführung abstrakter Ebenen"

Einführung

Beim Erlernen von Designmustern

Ich denke, dass Sie lernen werden, indem Sie ein solches Meisterwerk lesen, aber es gibt 23 Arten von sogenannten GoF-Designmustern, die in diesen vorkommen, und es ist schwer, sich daran zu erinnern. Am Ende kann das, was Sie tun, jedoch in zwei Muster eingeteilt werden, und ich werde darüber sprechen, wie Sie mehr verstehen können, wenn Sie wissen, in welches (oder beide) jedes Muster eingeteilt ist.

Die Theorie, dass es unter dem Gesichtspunkt der "Einführung abstrakter Ebenen" nur zwei GoF-Entwurfsmuster gibt

In der Welt der Software: "Wir können jedes Problem lösen, indem wir eine zusätzliche Indirektionsebene einführen." (Https://en.wikipedia.org) Es scheint / wiki / Fundamental_theorem_of_software_engineering) zu geben, aber das GoF-Entwurfsmuster ist ein konkretes Beispiel für diese Maxime. Rückblickend auf das Entwurfsmuster aus der Perspektive von ** "Einführung von Klassen / Schnittstellen, die abstrakte Ebenen sind" **, besteht der Zweck der Einführung abstrakter Ebenen, die das GoF-Entwurfsmuster ausführt, in zwei oder beiden der folgenden Punkte.

  1. Führen Sie eine Zwischenschicht (Klasse / Methode) ein, um eine Verarbeitung durchzuführen
  2. Einführung einer Schnittstelle zum Trennen und Aggregieren ähnlicher Codes

Es ist natürlich zu sagen, dass es natürlich ist, aber im Extremfall kann man denken, dass das ** GoF-Designmuster nur eine Kombination dieser beiden Muster ** ist. Es ist möglicherweise einfacher zu verstehen, wenn Sie wissen, zu welchem Muster jedes Muster gehört.

Die folgende Tabelle fasst zusammen, ob jedes Muster 1 oder 2 (oder beiden) entspricht.

Pattern 1.Mittelklasse/Methodeneinführung 2 Einführung der Trennungs- / Aggregationsschnittstelle
Iterator
Adapter
Template Method
Factory Method
Singleton
Prototype
Builder
Abstract Factory
Bridge
Strategy
Composite
Decorator
Visitor
Chain of Responsibility
Facade
Mediator
Observer
Memento
State
Flyweight
Proxy
Command
Interpreter

Ich habe alles gesagt, was ich oben sagen möchte, aber als Bonus werde ich jedes Muster aus der Perspektive der "Einführung einer abstrakten Ebene" erklären.

Iterator Einführung einer Schnittstelle für den Elementzugriff.

Status

Angenommen, Sie haben eine JunkList-Klasse für Ihre eigene Sammlung.

JunkList.java


public class JunkList<E> {
    private Object[] elements = new Object[1024];
    private int index = 0;

    public void add(E e) {
        this.elements[index] = e;
        ++index;
    }

    @SuppressWarnings("unchecked")
    public E getElement(int index) {
        return (E)(this.elements[index]);
    }

    public int getIndex() {
        return this.index;
    }
}

Wenn Sie alle Elemente scannen möchten, müssen Sie sich die Implementierung ansehen, um zu sehen, wie Sie auf die Elemente zugreifen und wie sie ermittelt werden. In diesem Fall müssen Sie sich die Implementierung ansehen, um festzustellen, ob getIndex () die Anzahl der Elemente und getElement () jedes Element abruft, und dann die Schleife durchlaufen.

Before.java


public class Before {
    public static void main(String[] args) {
        JunkList<String> junkList = new JunkList<>();
        junkList.add("Element1");
        junkList.add("Element2");
        junkList.add("Element3");
        ...
        ...
        //Greifen Sie auf alle Elemente zu
        for (int i = 0; i < junkList.getIndex(); ++i) {
            System.out.println(junkList.getElement(i));
        }
    }
}

Musteranwendung

Führt die "Iterator" -Schnittstelle ein und fasst das Elementzugriffsverhalten in "JunkListIterator" zusammen, dem "Iterator" für "JunkList". Elementzugriffsvorgänge werden aggregiert und können über eine einheitliche Schnittstelle aufgerufen werden.

Iterator.java


public interface Iterator<E> {
    boolean hasNext();
    E next();
}

BaseCollection.java


public interface BaseCollection<E> {
    Iterator<E> iterator();
}

JunkList.java


public class JunkList<E> implements BaseCollection<E> {
    private Object[] elements = new Object[1024];
    private int index = 0;

    public void add(E e) {
        this.elements[index] = e;
        ++index;
    }

    @SuppressWarnings("unchecked")
    public E getElement(int index) {
        return (E)(this.elements[index]);
    }

    public int getIndex() {
        return this.index;
    }

    @Override
    public Iterator<E> iterator() {
        return new JunkListIterator<E>(this);
    }
}

JunkListIterator.java


public class JunkListIterator<E> implements Iterator<E> {
    private final JunkList<E> junkList;
    private int index;

    public JunkListIterator(JunkList<E> junkList) {
        this.junkList = junkList;
    }

    @Override
    public boolean hasNext() {
        return (this.index < this.junkList.getIndex());
    }

    @Override
    public E next() {
        E element = this.junkList.getElement(this.index);
        ++(this.index);
        return element; 
    }
}

After.java


public class Main {
    public static void main(String[] args) {
        JunkList<String> junkList = new JunkList<>();
        junkList.add("Element1");
        junkList.add("Element2");
        junkList.add("Element3");
        ...
        //Greifen Sie auf alle Elemente zu
        Iterator<String> itr = junkList.iterator();
        while (itr.hasNext()) {
            System.out.println(itr.next());
        }
    }
}

Adapter Führen Sie eine Klasse ein, die eine Zwischenschicht zum Ändern der Schnittstelle darstellt.

Status

Angenommen, Sie haben eine Situation, in der Ihr vorhandener Code die Schnittstelle "Ladegerät" (Ladegerät) "Charger.getPower100V ()" zu verwenden scheint.

Charger.java


public interface Charger {
    Power100V getPower100V();
}

Angenommen, die Klasse "Generator" (Generator) der neu eingeführten Bibliothek verfügt nur über die Methode "Generator.getPower200V ()", die 200 V liefert.

Generator.java


public class Generator {
    public Power200V getPower200V() {
        return new Power200V(1000);
    }
}

In solchen Fällen können Sie eine Methode wie "getPower100V ()" zu "Generator" hinzufügen, aber Sie möchten nicht mit dem Code in der vorhandenen Bibliothek herumspielen.

Musteranwendung

Einführung in die Transformer-Klasse, Anpassung der Schnittstelle an die vorherige getPower100V () und dann Intern delegiert es die Verarbeitung an "Generator". Durch die Verwendung von "Generator" über "Transformer" kann der Benutzer weiterhin die Schnittstelle von "getPower100V ()" verwenden.

Transformer.java


public class Transformer implements Charger {
    private final Generator generator;

    public Transformer(Generator generator) {
        this.generator = generator;
    }

    @Override
    public Power100V getPower100V() {
        final Power200V power200V = this.generator.getPower200V();
        return new Power100V(power200V.getWatt());
    }
}

After.java


public class After {
    public static void main(String[] args) {
        final Charger charger = new Transformer(new Generator());
        final Power100V power = charger.getPower100V();
        ...
    }
}

Bridge Führen Sie eine Klasse ein, um die Implementierung zu delegieren und von der Vererbungsbeziehung zu trennen

Status

Angenommen, Sie haben eine "Buch" -Schnittstelle und die Unterklassen "EBook" und "PaperBook".

Book.java


public interface Book {
    void showType();
    void showContent();
}

EBook.java


public abstract class EBook implements Book {
    @Override
    public void showType() {
        System.out.println("# Electroical Book");
    }
}

PaperBook.java


public abstract class PaperBook implements Book {
    @Override
    public void showType() {
        System.out.println("# Paper Book");
    }
}

Wenn Sie die Klassen "Novel" und "Nonfiction" als Inhalt des Buches erstellen möchten, müssen Sie vom "EBook" bzw. "PaperBook" erben. Sie müssten zwei Klassen mit derselben Implementierung erstellen, was redundant wäre.

EBookNovel.java


public class EBookNovel extends EBook {
    @Override
    public void showContent() {
        System.out.println("I'm Novel.");
    }
}

EBookNonfiction.java


public class EBookNonfiction extends EBook {
    @Override
    public void showContent() {
        System.out.println("I'm Nonfiction.");
    }
}

PaperBookNovel.java


public class PaperBookNovel extends PaperBook {
    @Override
    public void showContent() {
        System.out.println("I'm Novel.");
    }
}

PaperBookNonfiction.java


public class PaperBookNonfiction extends PaperBook {
    @Override
    public void showContent() {
        System.out.println("I'm Noonfiction.");
    }
}

Musteranwendung

Die Implementierung von "showContent ()" in den Inhalten des Buches führt die "BookImpl" -Schnittstelle ein und delegiert sie an diese, wobei sie von der Vererbungsbeziehung getrennt wird. Dadurch entfällt die Notwendigkeit, Klassen für Novel und Nonfiction für "EBook" bzw. "PaperBook" zu erstellen.

Book.java


public abstract class Book {
    private final BookImpl impl;

    public Book(BookImpl impl) {
        this.impl = impl;
    }

    public abstract void showType();

    public void showContent() {
        this.impl.showContent();
    }
}

Novel.java


public class Novel implements BookImpl {                                                                                                                                                                                                                                           
    @Override
    public void showContent() {
        System.out.println("I'm Novel.");
    }
}

Nonfiction.java


public class Nonfiction implements BookImpl {
    @Override
    public void showContent() {
        System.out.println("I'm Nonfiction.");
    }
}

PaperBook.java


public class PaperBook extends Book {                                                                                                                                                                                                                                            
    public PaperBook(BookImpl impl) {
        super(impl);
    }

    @Override
    public void showType() {
        System.out.println("# Paper Book");
    }
}

EBook.java


public class EBook extends Book {                                                                                                                                                                                                                                               
    public EBook(BookImpl impl) {        
        super(impl);
    }

    @Override
    public void showType() {
        System.out.println("# Electronic Book");
    }
}

Builder Führen Sie eine Klasse ein, die eine Zwischenschicht der Instanzgenerierung darstellt

Status

Angenommen, Sie möchten eine Anweisung im HTML- oder Markdown-Format generieren. Ich generiere eine einfache Anweisung mit nur Überschriften und Absätzen.

Before.java


public class Before {
    public static void main(String[] args) {
        {
            final StringBuilder sb = new StringBuilder();
            sb.append("<h1>Header</h1>\n");
            sb.append("<p>Paragraph</p>\n");
            System.out.println(sb.toString());
        }
        {
            final StringBuilder sb = new StringBuilder();
            sb.append("# Header\n");
            sb.append("Paragraph\n");
            System.out.println(sb.toString());
        }
    }
}

Dies ist kein Problem, aber ich bin besorgt darüber, dass der Tag-Teil und der Inhaltsteil mit verschiedenen Systemen in derselben Zeile behandelt werden. Aus abstrakter Sicht sind beide HTML / Markdown-Prozesse in der Reihenfolge des Inhalts der Kopfzeile und des Absatzes angeordnet. Es scheint, dass Sie Ihren Code organisieren können, indem Sie darauf basierende Schnittstellen und Klassen einführen.

Musteranwendung

Wir werden die Builder-Oberfläche einführen und den Code zusammenstellen.

Builder.java


public interface Builder {
    void makeHeader(String header);
    void makeParagraph(String paragraph);
}

HTMLBuilder.java


public class HTMLBuilder implements Builder {
    private final StringBuilder sb = new StringBuilder();

    @Override
    public void makeHeader(String header) {
        this.sb.append(String.format("<h1>%s</h1>\n", header));
    }

    @Override
    public void makeParagraph(String paragraph) {
        this.sb.append(String.format("<p>%s</p>\n", paragraph));
    }

    public String getResult() {
        return this.sb.toString();
    }
}

MarkdownBuilder.java


public class MarkdownBuilder implements Builder {
    private final StringBuilder sb = new StringBuilder();

    @Override
    public void makeHeader(String header) {
        this.sb.append(String.format("# %s\n", header));
    }

    @Override
    public void makeParagraph(String paragraph) {
        this.sb.append(String.format("%s\n", paragraph));
    }

    public String getResult() {
        return this.sb.toString();
    }
}

Director.java


public class Director {
    private final Builder builder;

    public Director(Builder builder) {
        this.builder = builder;
    }

    public void construct() {
        this.builder.makeHeader("Header");
        this.builder.makeParagraph("This is Paragraph.");
    }
}

After.java


public class After {
    public static void main(String[] args) {
        // HTML
        {
            final HTMLBuilder builder = new HTMLBuilder();
            final Director director = new Director(builder);
            director.construct();
            System.out.println(builder.getResult());
        }

        // Markdown
        {
            final MarkdownBuilder builder = new MarkdownBuilder();
            final Director director = new Director(builder);
            director.construct();
            System.out.println(builder.getResult());
        }
    }
}

Der HTML / Markdown-Tag-Teil wurde getrennt und in der Klasse "HTMLBuilder" / "MarkdownBuilder" zusammengefasst. Außerdem könnte der Inhaltsteil getrennt werden, um in "Director.construct ()" festgelegt zu werden.

Strategy Führen Sie eine Schnittstelle für "Strategie" ein, konsolidieren Sie die Implementierungsteile und vereinheitlichen Sie die Schnittstelle.

Status

Stellen Sie sich eine Situation vor, in der jede Klasse, die eine Fußballmannschaft repräsentiert, eine Strategie umsetzt.

PosessionSoccerTeam.java


public class PosessionSoccerTeam {
    public void execute() {
        System.out.println("Posession strategy.");   
    }
}

CounterSoccerTeam.java


public class CounterSoccerTeam {
    public void execute() {
        System.out.println("Counter strategy.");   
    }
}

Dies erfordert mehr Klassen, wenn die Strategie wächst. Der strategische Teil scheint die Schnittstelle zu definieren und zu trennen.

Musteranwendung

Führt die "Strategie" -Schnittstelle ein und delegiert die Verarbeitung des Strategieteils. SoccerTeam muss nur Strategy.execute () aufrufen, sodass für jede Strategie keine Klasse erstellt werden muss.

Strategy.java


public interface Strategy {
    void execute();
}

Posession.java


public class Possession implements Strategy {
    @Override
    public void execute() {
         System.out.println("Posession strategy.");       
    }
}

Counter.java


public class Counter implements Strategy {
    @Override
    public void execute() {
        System.out.println("Counter strategy.");
     }
}

SoccerTeam.java


public class SoccerTeam {
    private final Strategy strategy;

    public SoccerTeam(Strategy strategy) {
        this.strategy = strategy;
    }

    public void execute() {
        this.strategy.execute();
    }
}

Singleton Einführung der Methode "Instanzen abrufen", um die Anzahl der Instanzen auf eins zu beschränken

Status

Sie können beliebig viele Instanzen erstellen, wenn Sie sie mit new erstellen können. Angenommen, Sie haben eine Situation, in der Sie die Anzahl der Instanzen auf 1 beschränken möchten.

Musteranwendung

Machen Sie den Konstruktor privat und führen Sie eine Methode namens "getIntance ()" ein, die die abstrakte Operation "Abrufen einer Instanz" bedeutet. Sie können die Anzahl der Instanzen auf 1 beschränken, indem Sie über diese Methode nur eine erstellte Instanz zurückgeben.

Singleton.java


public class Singleton {
    private static Singleton singleton = new Singleton();

    //Von außen neu verbieten
    private Singleton() {
    }

    //Gibt nur eine Instanz zurück
    public static Singleton getInstance() {
        return singleton;
    }
}

Prototype Führen Sie Klassen wie Registrierung und Kopiergenerierung ein

Status

Angenommen, Sie benötigen eine große Anzahl von "Slime" -Instanzen, die die "Monster" -Schnittstelle implementieren, und möchten aufgrund der hohen Kosten für die Erstellung einer neuen Instanz eine Instanz mit einer Kopie einer vorhandenen Instanz erstellen. Sie können eine Instanz nur am Anfang erstellen und dann eine Instanz mit "createClone ()" erstellen. Die Verwaltung scheint jedoch schwierig zu sein, da die Anzahl der Instanztypen zunimmt.

Before.java


public class Before {
    public static void main(String[] args) {
        ...
        final Monster normalSlime = new Slime("Normal");
        final Monster metalSlime  = new Slime("Metal");
        ...

        final Monster normalSlime2 = normalSlime.createClone();
        final Monster normalSlime3 = normalSlime.createClone();
        final Monster metalSlime2  = metalSlime.createClone();
        ...
    }

Musteranwendung

Durch die Einführung der MonsterManager-Klasse für die Instanzverwaltung wird Ihr Code übersichtlicher und verständlicher. Wenn in MonsterManager die erstellte Instanz registriert ist, wird sie zurückgegeben, und nur wenn sie nicht registriert ist, wird die Instanz erstellt und zurückgegeben.

MonsterManager.java


public class MonsterManager {
    private final Map<String, Monster> monsterMap = new HashMap<>();

    public void addPrototype(String name, Monster monster) {
        this.monsterMap.put(name, monster);
    }

    public Monster createMonster(String name) throws CloneNotSupportedException {
        final Monster monster = this.monsterMap.get(name);
        if (monster == null) {
            throw new IllegalArgumentException("Invalid monster name");
        }

        return monster.createClone();
    }
}

After.java


public class After {
    public static void main(String[] args) {
        final MonsterManager manager = new MonsterManager();
        manager.addPrototype("NormalSlime", new Slime("Normal"));
        manager.addPrototype("MetalSlime",  new Slime("Metal"));
        ..
        final Monster normalSlime  = manager.createMonster("NormalSlime");
        final Monster metalSlime   = manager.createMonster("MetalSlime");
        ...
    }
}

Facade Stellen Sie eine Methode vor, die mehrere Prozesse kombiniert

Status

Angenommen, Sie möchten eine CSV-Datei lesen -> alle Daten hexadezimal mit den Klassen CsvParser und StringDumper sichern.

Before.java


public class Before {
    public static void main(String[] args) {
        final CsvParser csvParser = new CsvParser();
        csvParser.parse("test.csv");

        for (final List<String> row : csvParser.getRows()) {
            for (final String col : row) {
                System.out.println(StringDumper.hexdump(col));
            }
        }
    }
}

Wenn dieser Lese-> Speicherauszugsprozess ein typischer Anwendungsfall ist, erscheint es zweckmäßig, eine Methode einzuführen, die alle zusammen ausführt.

Musteranwendung

Führen Sie CsvHexDumper mit der Methode hexdumpCsvFile ein, die den Prozess als Facade-Klasse zusammenfasst.

CsvHexDumper.java


public class CsvHexDumper {
    //Machen Sie es zu einer Klasse für statische Methoden
    private CsvHexDumper() {}

    public static void hexdumpCsvFile(String filePath) {
        CsvParser csvParser = new CsvParser();
        csvParser.parse(filePath);

        for (final List<String> row : csvParser.getRows()) {
            for (final String col : row) {
                System.out.println(StringDumper.hexdump(col));
            }
        }
    }
}

Der Benutzer kann eine Reihe von Operationen ausführen, indem er einfach "CsvHexDumper.hexdumpCsvFile ()" aufruft.

After.java


public class After {
    public static void main(String[] args) {
        CsvHexDumper.hexdumpCsvFile("test.csv");
    }
}

Mediator Einführung einer Klasse, die die Kommunikation zwischen Instanzen vermittelt

Status

Es gibt die Klassen "Fußgänger" (Fußgänger) und "Auto", "Auto" kann ohne Stoppanforderung von "Fußgänger" fortgesetzt werden, und "Fußgänger" rückt vor, wenn "Auto" angehalten wird. Angenommen, es gibt eine Situation, in der eine gegenseitige Kommunikation zwischen Instanzen erforderlich ist. Stellen Sie sich eine Implementierung vor, bei der sich "Auto" und "Fußgänger" auf die Instanzen des jeweils anderen beziehen und ihre Zustände mitteilen.

Car.java


public class Car {
    private Pedestrian pedestrian;
    private boolean stopFlag = false;

    public void setPedestrian(Pedestrian pedestrian) {
        this.pedestrian = pedestrian;
    }

    public void stopRequested() {
        this.stopFlag = true;
        this.pedestrian.notifyCarStopped();
    }

    public void notifyCrossFinished() {
        this.stopFlag = false;
    }
    
    public void proceed() {
        if (this.stopFlag) {
            System.out.println("Car stops.");
        } else {
            System.out.println("Car proceeds.");
        }
    }
}

Pedestrian.java


public class Pedestrian {
    private Car car;
    private boolean stopFlag = true;

    public void setCar(Car car) {
        this.car = car;
    }
    
    public void notifyCarStopped() {
        this.stopFlag = false;
    }

    public void stopRequest() {
        this.car.stopRequested();
    }

    public void proceed() {
        if (this.stopFlag) {
            System.out.println("Pedestrian stops.");
        } else {
            System.out.println("Pedestrian proceeds.");
            this.car.notifyCrossFinished();
            this.stopFlag = true;
        }
    }
}

Wenn die Anzahl der Instanzen und die Anzahl der gegenseitigen Kommunikationen zunimmt, wird die Verarbeitung kompliziert und es gibt einen Geruch (?), Der zusammenzubrechen scheint.

Musteranwendung

Stellen Sie die TrafficLight-Schnittstelle und ihre Implementierung als Mediator vor und organisieren Sie Ihren Code. Diese vermitteln die Kommunikation zwischen Instanzen.

TrafficLight.java


public interface TrafficLight {
    void waiting();
    void crossed();
}

TrafficLightImpl.java


public class TrafficLightImpl implements TrafficLight {
    private Car        car;
    private Pedestrian pedestrian;

    public void setCar(Car car) {
        this.car = car;
    }

    public void setPedestrian(Pedestrian pedestrian) {
        this.pedestrian = pedestrian;
    }

    @Override
    public void waiting() {
        if (this.car != null) {
            this.car.setStopFlag(true);
        }

        this.pedestrian.setStopFlag(false);
    }

    @Override
    public void crossed() {
        if (this.car != null) {
            this.car.setStopFlag(false);
        }

        this.pedestrian.setStopFlag(true);
    }
}

Sowohl "Auto" als auch "Fußgänger" reduzieren die Komplexität der Kommunikation zwischen Instanzen, indem sie über "TrafficLight" kommunizieren.

Car.java


public class Car implements Participant {
    private boolean stopFlag = false;
    
    @Override 
    public void setStopFlag(boolean stopFlag) {
        this.stopFlag = stopFlag;
    }

    @Override
    public void proceed() {
        if (this.stopFlag) {
            System.out.println("Car stops.");
        } else {
            System.out.println("Car proceeds.");
        }
    }
}

Pedestrian.java


public class Pedestrian implements Participant {
    private final TrafficLight trafficLight;
    private boolean stopFlag = true;

    public Pedestrian(TrafficLight trafficLight) {
        this.trafficLight = trafficLight;
    }
    
    @Override 
    public void setStopFlag(boolean stopFlag) {
        this.stopFlag = stopFlag;
    }

    @Override
    public void proceed() {
        if (this.stopFlag) {
            System.out.println("Pedestrian stops.");
        } else {
            System.out.println("Pedestrian proceeds.");
            this.trafficLight.crossed();
        }
    }

    public void stopRequest() {
        this.trafficLight.stopRequested();
    }
}

Memento Führen Sie eine Klasse zum Speichern / Wiederherstellen des Instanzstatus ein

Status

Angenommen, Sie haben eine Heldenklasse mit einem Level und HP, und Sie möchten das Level und die HP irgendwann speichern und diesen Status später wiederherstellen.

Before.java


public class Before {
    public static void main(String[] args) {
        Hero hero = new Hero();
        final int level = hero.getLevel();
        final int hp    = hero.getHP();
        ...
        hero.levelUp();
        hero.receiveDamage(5);
        ...
        hero.restore(level, hp);
        ...
    }
}

Levels und HP werden als Set behandelt, sodass es einfacher zu verstehen scheint, wenn sie in Instanzen gruppiert sind.

Musteranwendung

Einführung der "Memento" -Klasse, die das Level und die HP zusammenfasst und den Status speichert / wiederherstellt, um das Erstellen / Laden von "Memento" zu unterstützen. Wenn Sie zu diesem Zeitpunkt die generierten Memento-Informationen unsichtbar machen möchten (nur zur Wiederherstellung verwendet), trennen Sie Pakete oder verwenden Sie innere Klassen. Hier ist ein Beispiel mit einer inneren Klasse (siehe auch Wikipedia Beispiel)

Hero.java


public class Hero {
    private int level = 1;
    private int hp    = 10;

    public void levelUp() {
        ++(this.level);
    }

    public void receiveDamage(int point) {
        this.hp -= point;
    }

    public void healDamange(int point) {
        this.hp += point;
    }

    public Memento createMemento() {
        return new Memento(this.level, this.hp);
    }

    public void restoreMemento(Memento memento) {
        this.level = memento.getLevel();
        this.hp    = memento.getHP();
    }

    @Override
    public String toString() {
        return String.format("Level=%d,HP=%d", this.level, this.hp);
    }

    public static class Memento {
        private final int level;
        private final int hp;

        public Memento(int level, int hp) {
            this.level = level;
            this.hp    = hp;
        }

        private int getLevel() {
            return this.level;
        }

        private int getHP() {
            return this.hp;
        }
    }
}

After.java


public class After {
    public static void main(String[] args) {
        final Hero hero = new Hero();
        final Hero.Memento memento = hero.createMemento(); //Der Inhalt dieses Erinnerungsstücks ist wichtig()Kann nicht referenziert werden von
        
        hero.levelUp();
        hero.receiveDamage(3);
        System.out.println(hero);
        ... 
        hero.restoreMemento(memento);
        System.out.println(hero);
    }        
}

State Führen Sie eine Schnittstelle ein, die Zustände und eine separate Verarbeitung für jeden Zustand darstellt

Status

Angenommen, Sie haben eine Situation, in der Sie ein Konto auf einer Website erstellen-> Anmelden-> Abmelden. Je nach Situation Vorgänge ausführen, z. B. die Anmeldung vor dem Erstellen eines Kontos oder die erneute Anmeldung während der Anmeldung. Es ist gut, eine Variable zu haben, die den Status angibt und die Verarbeitung entsprechend dem Status durch eine if-Anweisung oder eine switch-Anweisung unterteilt. Es kann jedoch schwierig sein, diese zu verstehen, da es viele Zweige gibt.

Context.java


public class Context {
  public void createAccount() {
      switch (this.state) {
      case NO_ACCOUNT: {
          System.out.println("Created Account.");
          this.state = EnumState.LOGOUT;
          break;
      }
      case LOGIN: {
          System.err.println("Already logged in.");
          break;
      }
      case LOGOUT: {
          System.err.println("Account is already created.");
          break;
      }
      default: {
          break;
      }
      }
  }

  public void login() {
      ...
  }

  public void logout() {
      ...
  }
}

Musteranwendung

Führt die "State" -Schnittstelle ein, die den Status angibt, und trennt und aggregiert die Operation in jedem Status in die Implementierungsklasse "State".

State.java


public interface State {
    void createAccount(Context context);
    void login(Context context);
    void logout(Context context);
}

LoginState.java


public class LoginState implements State {
    @Override
     public void createAccount(Context context) {
        System.err.println("Account is already created.");
    }

    @Override
    public void login(Context context) {
        System.err.println("Already logged in.");
    }

    @Override
    public void logout(Context context) {
        System.out.println("Logged out.");
        context.setState(new LogoutState());
    }
}

Die Verarbeitung von "createAccount ()", "login ()", "logout ()" wird an "State.createAccount ()", "State.login ()", "State.logout ()" delegiert, und der Status ändert sich. Durch Umschalten der Implementierungsklasse werden die Operationen für jeden Zustand für jede Klasse getrennt.

Context.java


public class Context {
    private State state = new NoAccountState();

    public void setState(State state) {
        this.state = state;
    }

    public void createAccount() {
        this.state.createAccount(this);
    }

    public void login() {
        this.state.login(this);
    }

    public void logout() {
        this.state.logout(this);
    }
}

Flyweight Führen Sie die generierte Instanzverwaltungsklasse und die Freigabeinstanz ein

Status

Stellen Sie sich eine Situation vor, in der Sie viele speicherintensive "HeavyObject" -Klassen benötigen.

Before.java


public class Before {
    public static void main(String[] args) {
        final HeavyObject heavyObject1 = new HeavyObject("Hello");
        final HeavyObject heavyObject2 = new HeavyObject("World");
        final HeavyObject heavyObject3 = new HeavyObject("Hello");
        final HeavyObject heavyObject4 = new HeavyObject("World");
        ...
    }
}

Da die Instanz mit demselben Inhalt jedes Mal neu generiert wird, scheint es eine Verschwendung von Ressourcenverbrauch zu geben.

Musteranwendung

Wenn die Instanz gemeinsam genutzt werden kann, können Sie "HeavyObjectFactory" einführen, um die Instanz zurückzugeben, wenn sie bereits erstellt wurde. Es ist möglich, nicht extra neu zu machen.

HeavyObjectFactory.java


public class HeavyObjectFactory {
    private final Map<String, HeavyObject> objMap = new HashMap<>();

    public HeavyObjectFactory create(String str) {
        if (this.objMap.containsKey(str)) {
            return this.objMap.get(str);
        } else {
            final HeavyObject heavyObject = new HeavyObject(str);
            this.objMap.put(str, heavyObject);

            return  heavyObject;
        }
    }
}

After.java


public class After {
    public static void main(String[] args) {
        final HeavyObjectFactory factory = new HeavyObjectFactory();

        final HeavyObject heavyObject1 = factory.create("Hello");
        final HeavyObject heavyObject2 = factory.create("World");
        final HeavyObject heavyObject3 = factory.create("Hello");
        final HeavyObject heavyObject4 = factory.create("World");
        ...
    }
}

Proxy Fügen Sie eine Klasse mit derselben Schnittstelle ein, um die Verarbeitung zu verzögern

Status

Bedenken Sie, dass es einen "HeavyDumper" gibt, der einen starken Initialisierungsprozess hat, und dump () wird nach verschiedenen anderen Prozessen nach der Initialisierung ausgeführt.

Dumper.java


public interface Dumper {
    public String getName();
    public void dump();
}

HeavyDumper.java


public class HeavyDumper implements Dumper {
    private final String name;

    public HeavyDumper(String name) {
        this.name = name;
        //
        // ... heavy process
        //
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public void dump() {
        ...
    }
}

Before.java


public static void main(String[] args) {
    final Dumper dumper = new HeavyDumper("Akebono");
    ...
    String str = dumper.getName();
    ...
    dumper.dump();
}

Selbst wenn Sie die Funktionalität von "HeavyDumper" wirklich benötigen, wenn Sie "dump ()" aufrufen, erledigt es viel schwere Arbeit, wenn Sie es instanziieren.

Musteranwendung

Führt die Proxy-Klasse "ProxyDumper" ein, um die Instanziierung von "HeavyDumper" zu verzögern, bis sie vom Aufruf "dump ()" benötigt wird.

ProxyDumper.java


public class ProxyDumper implements Dumper {
    private final String name;
    private Dumper heavyDumper;

    public ProxyDumper(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public void dump() {
        if (this.heavyDumper == null) {
            this.heavyDumper = new HeavyDumper(this.name);
        }
        this.heavyDumper.dump();
    }
}

After.java


public class After {
    public static void main(String[] args) {
        final Dumper dumper = new ProxyDumper("Akebono"); //HeavyDumper wird hier nicht generiert
        ...
        String str = dumper.getName(); //Auch hier wird HeavyDumper nicht generiert
        ...
        dumper.dump(); //HeavyDumper Generation zum ersten Mal hier
    } 
}

Composite

Interpreter Einführung einer "Sprache", die auf die spezifische Verarbeitung spezialisiert ist

Musteranwendung

(Spezifische Implementierung weggelassen) Das Muster soll die Grammatikregeln in einer Klasse ausdrücken, aber ich bekomme im Vergleich zu anderen Mustern einen etwas anderen Eindruck. Unter dem Gesichtspunkt der "Einführung einer abstrakten Ebene" können wir jedoch verstehen, warum dieses Muster auftritt.

Bisher haben wir Entwurfsmuster aus der Perspektive der "Einführung abstrakter Ebenen" erklärt, aber ich denke, die ultimative Form ist das Interpreter-Muster. Es ist ein Muster, dass wir nicht müde sind, nur Schnittstellen und Klassen einzuführen (?), Aber wir werden eine Sprache einführen, die auf irgendeine Art von Verarbeitung spezialisiert ist.

Die Veröffentlichung von "Design Patterns for Reuse in Object-Oriented" im Jahr 1994 und das Aufkommen von Java im Jahr 1995 scheinen den Fluss der Zeit zu symbolisieren.

Zusammenfassung

Es war ein Versuch, der leichter zu verstehen wäre, wenn das Entwurfsmuster aus der Perspektive der "Einführung einer abstrakten Ebene" erklärt würde.

Referenz

[Entwurfsmuster für die Wiederverwendung in der Objektorientierung](https://www.amazon.co.jp/%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7% E3% 82% AF% E3% 83% 88% E6% 8C% 87% E5% 90% 91% E3% 81% AB% E3% 81% 8A% E3% 81% 91% E3% 82% 8B% E5% 86% 8D% E5% 88% A9% E7% 94% A8% E3% 81% AE% E3% 81% 9F% E3% 82% 81% E3% 81% AE% E3% 83% 87% E3% 82% B6% E3% 82% A4% E3% 83% B3% E3% 83% 91% E3% 82% BF% E3% 83% BC% E3% 83% B3-% E3% 82% A8% E3% 83% AA % E3% 83% 83% E3% 82% AF-% E3% 82% AC% E3% 83% B3% E3% 83% 9E / dp / 4797311126 / ref = sr_1_2? __Mk_ja_JP =% E3% 82% AB% E3 % 82% BF% E3% 82% AB% E3% 83% 8A & Keywords =% E3% 83% 87% E3% 82% B6% E3% 82% A4% E3% 83% B3% E3% 83% 91% E3% 82% BF% E3% 83% BC% E3% 83% B3 & qid = 1563164653 & s = Bücher & sr = 1-2) [Einführung in Entwurfsmuster, die in der erweiterten und überarbeiteten Java-Sprache gelernt wurden](https://www.amazon.co.jp/%E5%A2%97%E8%A3%9C%E6%94%B9%E8%A8%82% E7% 89% 88Java% E8% A8% 80% E8% AA% 9E% E3% 81% A7% E5% AD% A6% E3% 81% B6% E3% 83% 87% E3% 82% B6% E3% 82% A4% E3% 83% B3% E3% 83% 91% E3% 82% BF% E3% 83% BC% E3% 83% B3% E5% 85% A5% E9% 96% 80-% E7% B5 % 90% E5% 9F% 8E-% E6% B5% A9 / dp / 4797327030 / ref = sr_1_1? __Mk_ja_JP =% E3% 82% AB% E3% 82% BF% E3% 82% AB% E3% 83% 8A & Schlüsselwörter =% E3% 83% 87% E3% 82% B6% E3% 82% A4% E3% 83% B3% E3% 83% 91% E3% 82% BF% E3% 83% BC% E3% 83% B3 & qid = 1563164653 & s = Bücher & sr = 1-1) Memento pattern

Recommended Posts

Die Geschichte, dass GoF-Designmuster nichts anderes sind als "Einführung abstrakter Ebenen"
Einführung einer datengesteuerten Controller-Entwurfsmethode
Grobe Zusammenfassung des GoF-Java-Entwurfsmusters
[Viererbande] Design Pattern Learning - Singleton
[Viererbande] Design Pattern Learning --Decorator
[Viererbande] Designmuster lernen --Besucher
[Viererbande] Design Pattern Learning - Vermittler
[Viererbande] Designmusterlernen --Iterator
GoF-Entwurfsmuster aus dem Problem 2. Struktur
[Viererbande] Designmuster lernen - Fassade
[Viererbande] Design Pattern Learning - Composite
[Viererbande] Designmuster lernen - Prototyp
GoF-Entwurfsmuster aus dem Problem 1. Generation
[Viererbande] Designmuster lernen - Andenken
[Viererbande] Designmuster lernen - Staat
[Vierergruppe] Design Pattern Learning - Interpreter
[Viererbande] Design Pattern Learning - Builder
[Viererbande] Designmuster lernen - Brücke
[Viererbande] Designmuster lernen --Proxy
[Viererbande] Design Pattern Learning - Strategie
[Viererbande] Designmuster lernen --Adapter
[Viererbande] Design Pattern Learning - Beobachter
[Viererbande] Designmuster lernen - Befehl
GoF-Entwurfsmuster aus dem Problem 3. Verhalten