GoF-Entwurfsmuster aus dem Problem 2. Struktur

Dies ist die Struktur der GoF-Entwurfsmusterreihe aus der Perspektive von Problemen.

  1. Generation
  2. Struktur
  3. Verhalten

: Frage: Ich möchte, dass die API-Antwort domänenfreundlich ist

before

FooAPI fooAPI = new FooAPI(lat, lng);
FooPlace place = new FooPlace();
place.setAddress(fooAPI.getPostalCode() + " " + fooAPI.getAddress()); // 〒012-3456 Hoge City, Präfektur Hoge
place.setStation(
  new StringJoiner(",")
    .add(fooAPI.getStation1())
    .add(fooAPI.getStation2())
    .add(fooAPI.getStation3())
    .toString());  //Diese Station,Kakiku Station,Sashisu Station

Dies ist ein Fall, in dem das Ergebnis der API formatiert und verwendet wird. Dies ist der einzige Ort, an dem die FooAPI verwendet wird. Wenn sich die Formungsmethode in Zukunft nicht ändert, ist dies in Ordnung. In Anbetracht der Wartbarkeit ist dies jedoch kein gutes Design.

after :bulb: Adapter

public class FooAPIAdapter {

  private final FooAPI fooAPI;

  public FooAPIAdapter(double latitude, double longitude) {
    this.fooAPI = new FooAPI(latitude, longitude);
  }

  public String getAddress() {
    return fooAPI.getPostalCode() + fooAPI.getAddress();
  }

  public String getStation() {
    return new StringJoiner(",")
      .add(fooAPI.getStation1())
      .add(fooAPI.getStation2())
      .add(fooAPI.getStation3())
      .toString();
  }
}
FooAPIAdapter fooAPI = new FooAPIAdapter(lat, lng);
FooPlace place = new FooPlace();
place.setAddress(fooAPI.getAddress());
place.setStation(fooAPI.getStation());

Wir haben einen Adapter zum Formatieren von API-Antworten vorbereitet. Wenn sich die Formatierungsmethode ändert, müssen Sie nur diesen Adapter ändern, was sich nicht auf den Benutzer auswirkt.

: Frage: Ich möchte nicht, dass die API-Benutzer über die Reihenfolge der Operationen nachdenken

before

public class FooSorter {

  private List<FooStudent> students = new List<>();

  public void add(FooStudent student) {
    students.add(student);
  }

  public void sort() {
    students.sort(
      Comparator.comparingInt(
        student -> student.getJapaneseScore()
          + student.getMathScore()
          + student.getEnglishScore())
      .reversed());
  }

  public List<FooStudent> getResult() {
    return students;
  }
}
FooSorter sorter = new FooSorter();
sorter.add(student1);
sorter.add(student2);
sorter.add(student3);
sorter.sort();
sorter.getResult();

Der schlechte Punkt ist, dass der Benutzer die Prozedur zum Erstellen einer Instanz kennen muss → add () → sort () → getResult ().

after :bulb: Facade

public class FooSorter {

  private List<FooStudent> students = new List<>();

  private FooSorter() {}

  public static List<FooStudent> sort(FooStudent... students) {
    for (FooStudent student : students)
      add(student);

    sort();
    return getResult();
  }

  private void add(FooStudent student) {
    students.add(student);
  }

  private void sort() {
    students.sort(
      Comparator.comparingInt(
        student -> student.getJapaneseScore()
          + student.getMathScore()
          + student.getEnglishScore())
      .reversed());
  }

  private List<FooStudent> getResult() {
    return students;
  }
}
FooSorter.sort(student1, student2, student3);

Der Benutzer muss nicht über die Bestellung nachdenken. In dieser Größenordnung können Sie in einer Zeile schreiben, sodass Sie die Methoden nicht in Facade unterteilen müssen. Dies ist jedoch ein Beispiel. Schauen Sie also genau hin. Facade ist in erster Linie nützlich, um komplexe Prozesse zu gruppieren, bei denen mehrere Klassen nacheinander verwendet werden.

: question: Ich möchte das Ergebnis der Multiplikation der Ergebnisse von Geschwisterklassen zurückgeben

before

public class FooPosition {

  private int x;
  private int y;

  // x,y Accessor

  public void moveAs(FooMove move) {
    move.move(this);
  }
}

public abstract class FooMove {

  private final int addition;

  public FooMove(int addition) {
    this.addition = addition;
  }

  public abstract void move(FooPosition position);
}

public class FooMoveHorizontal extends FooMove {

  public FooMoveHorizontal(int addition) {
    super(addition);
  }
  
  @Override
  public void move(FooPosition position) {
    position.setX(position.getX() + addition);
  }
}

public class FooMoveVertical extends FooMove {

  public FooMoveVertical(int addition) {
    super(addition);
  }

  @Override
  public void move(FooPosition position) {
    position.setY(position.getY() + addition);
  }
}
FooMove moveHorizontal = new FooMoveHorizontal(x);
FooMove moveVertical = new FooMoveVertical(y);

FooPosition position = new FooPosition();
position.moveAs(moveHorizontal);
position.moveAs(moveVertical);

Dies ist ein einfaches und gutes Design für sich, aber nehmen wir an, Sie möchten eine Klasse erstellen, die Bewegungen in x- und y-Richtung gleichzeitig verarbeitet. (Das Design, das die Operation des Werts von FooPosition in FooMove unterteilt und mit moveAs akzeptiert, ist übrigens das Besuchermuster.)

after :bulb: Decorator

public class FooPosition {

  private int x;
  private int y;

  // x,y Accessor

  public void moveAs(FooMove move) {
    move.move(this);
  }
}

public abstract class FooMove {

  private final int addition;
  private final FooMove move;

  public FooMove(int addition) {
    this.addition = addition;
  }

  public FooMove(int addition, FooMove move) {
    this.addition = addition;
    this.move = move;
  }

  public abstract void move(FooPosition position);
}

public class FooMoveHorizontal extends FooMove {

  public FooMoveHorizontal(int addition) {
    super(addition);
  }

  public FooMoveHorizontal(int addition, FooMove move) {
    super(addition, move);
  }
  
  @Override
  public void move(FooPosition position) {
    if (move != null)
      move.move(position);

    position.setX(position.getX() + addition);
  }
}

public class FooMoveVertical extends FooMove {

  public FooMoveVertical(int addition) {
    super(addition);
  }

  public FooMoveVertical(int addition, FooMove move) {
    super(addition, move);
  }

  @Override
  public void move(FooPosition position) {
    if (move != null)
      move.move(position);

    position.setY(position.getY() + addition);
  }
}
FooMove move = new FooMoveHorizontal(x, new FooMoveVertical(y));

FooPosition position = new FooPosition();
position.moveAs(move);

Die Bewegungen von x und y wurden zusammengefasst. Außerdem müssen Sie bei diesem Design nur "new FooMoveHorizontal (x, move)" verwenden, um weitere Züge hinzuzufügen. (Rufen Sie position.moveAs nur einmal auf) Auf dieser Skala ist es einfacher zu verstehen, ob Sie eine Klasse erstellen, die gleichzeitig x und y verarbeitet, aber da es sich um ein Beispiel handelt (weggelassen)

: Frage: Ich möchte die Implementierung mit Geschwisterklassen teilen (oder ich möchte die Funktion entsprechend den Bedingungen einschränken)

before

public class FooTestUser extends FooUser {

  @Override
  public void foo1() {
    //Normaler Benutzer foo1
  }

  @Override
  public void foo2() {
    throw new RuntimeException("this operation is not permitted.");
  }

  @Override
  public void foo3() {
    throw new RuntimeException("this operation is not permitted.");
  }
}

public class FooNormalUser extends FooUser {

  @Override
  public void foo1() {
    //Normaler Benutzer foo1
  }

  @Override
  public void foo2() {
    //Normaler Benutzer foo2
  }

  @Override
  public void foo3() {
    throw new RuntimeException("this operation is not permitted.");
  }
}

public class FooSuperUser extends FooUser {

  @Override
  public void foo1() {
    //SuperUser foo1
  }

  @Override
  public void foo2() {
    //SuperUser foo2
  }

  @Override
  public void foo3() {
    //Verarbeitung von foo3
  }
}

TestUser möchte seine Funktionalität einschränken, damit nur NormalUser foo1 ausgeführt werden kann. Zu diesem Zeitpunkt möchte ich verwalten, dass die Implementierung von foo1 von TestUser und NormalUser zu einer Kopie wird.

after :bulb: Proxy

public class FooTestUser extends FooUser {

  private FooUser normalUser = new FooNormalUser();

  @Override
  public void foo1() {
    normalUser.foo1();
  }

  @Override
  public void foo2() {
    throw new RuntimeException("this operation is not permitted.");
  }

  @Override
  public void foo3() {
    throw new RuntimeException("this operation is not permitted.");
  }
}

//Normal User und Super User sind die gleichen wie zuvor

Es gibt kein Kopieren und Einfügen der Implementierung. Wenn sich die Anforderungen von foo1 ändern, müssen Sie nur den normalen Benutzer ändern. Da Java 8 jedoch eine Standardimplementierung der Schnittstelle hinzugefügt hat, ist es möglicherweise besser, wenn Sie nur die Implementierung freigeben möchten. (Gleiches gilt für das Brückenmuster) Wenn Sie keinen SuperUser haben, ist es meiner Meinung nach einfacher, foo1 in der übergeordneten Klasse (FooUser) zu implementieren.

Recommended Posts

GoF-Entwurfsmuster aus dem Problem 2. Struktur
GoF-Entwurfsmuster aus dem Problem 1. Generation
GoF-Entwurfsmuster aus dem Problem 3. Verhalten
Grobe Zusammenfassung des GoF-Java-Entwurfsmusters
Lernen Sie das Entwurfsmuster "Prototype" mit Python
Lernen Sie das Designmuster "Flyweight" in Python
Lernen Sie das Entwurfsmuster "Memento" mit Python
Lernen Sie das Entwurfsmuster "Proxy" in Python
Lernen Sie das Entwurfsmuster "Befehl" in Python
Lernen Sie das Entwurfsmuster "Besucher" mit Python
Lernen Sie das Entwurfsmuster "Bridge" mit Python
Lernen Sie das Entwurfsmuster "Mediator" mit Python
Lernen Sie das Designmuster "Decorator" mit Python
Lernen Sie das Entwurfsmuster "Iterator" mit Python
Lernen Sie das Entwurfsmuster "Strategie" mit Python
Lernen Sie das Entwurfsmuster "Composite" mit Python
Lernen Sie das Entwurfsmuster "Singleton" mit Python
Lernen Sie das Entwurfsmuster "State" in Python
Lernen Sie das Entwurfsmuster "Adapter" mit Python
Lernen Sie das Designmuster "Facade" mit Python
Entwurfsmuster #Adapter
Lernen Sie das Entwurfsmuster "Abstract Factory" mit Python
Entwurfsmuster #Decorator
Lernen Sie das Entwurfsmuster "Vorlagenmethode" in Python
Entwurfsmuster #Observer
Lernen Sie das Entwurfsmuster "Factory Method" in Python
Entwurfsmuster #Facade
Entwurfsmuster #Strategie
Entwurfsmuster #Singleton
Entwurfsmuster #Proxy
Berechnen Sie das Volumen aus der zweidimensionalen Struktur einer Verbindung
Lernen Sie das Entwurfsmuster "Chain of Responsibility" in Python
Entwurfsmuster #Factory-Methode
Problem beim Entwurf eines Logistiknetzwerks
Entwurfsmuster # Template-Methode
Über das Besuchermuster
Untersuchen Sie das doppelte Problem
Die Geschichte, dass GoF-Designmuster nichts anderes sind als "Einführung abstrakter Ebenen"
Problem bei der Platzierung von Krankenwagen - Aus der Oktoberausgabe des OR-Magazins