Beim Erlernen von Designmustern
[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 & Stichwö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 = 156314653 & s = Bücher & sr = 1-1)
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.
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.
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.
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));
}
}
}
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.
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.
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
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.");
}
}
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
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.
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.
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.
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
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.
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
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();
...
}
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
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.
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
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.
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
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.
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
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() {
...
}
}
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
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.
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
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.
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
(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.
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.
[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