GoF-Entwurfsmuster aus dem Problem 3. Verhalten

Dies ist das Verhalten der GoF-Entwurfsmusterreihe aus der Perspektive von Problemen.

  1. Generation
  2. Struktur
  3. Verhalten

: Frage: Ich möchte Daten und Verarbeitung trennen

before

public class FooAmusementPark {

  private FooZoo zoo;
  private FooAquarium aquarium;

  public void enjoy(FooFamily family) {
    zoo.enjoy();
  }

  public void enjoy(FooCouple couple) {
    aquarium.enjoy();
  }
}

Wenn die Anzahl der Besucher außer Familie und Paar steigt, müssen wir den Vergnügungspark wechseln.

after :bulb: Visitor

public class FooAmusementPark {

  private FooZoo zoo;
  private FooAquarium aquarium;

  public void accept(FooVisitor visitor) {
    visitor.visit(this);
  }
}

public class FooFamily extends FooVisitor {

  @Override
  public void visit(FooAmusementPark park) {
    park.getZoo().enjoy();
  }
}

public class FooCouple extends FooVisitor {

  @Override
  public void visit(FooAmusementPark park) {
    park.getAquarium().enjoy();
  }
}

Selbst wenn die Anzahl der Kunden außer Familie und Paar zunimmt, muss die erhöhte Klasse nur die Besuchsmethode implementieren, ohne den AmusementPark zu ändern. Es ist eine wichtige Idee, die nicht auf dieses Muster beschränkt ist, die Teile auszuschneiden, die sich in Zukunft wahrscheinlich ändern werden. Der Punkt des Besuchermusters ist, dass die Datenstruktur grundsätzlich schwer zu ändern ist.

: Frage: Ich möchte Prozesse trennen, deren Spezifikationen sich leicht ändern

before

public class FooController {

  private FooLatLngToPlaceAPI api;

  public String getFormattedAddress(double latitude, double longitude) {
    FooPlace place = api.getPlace(latitude, longitude);

    /*
     * 123-4567
     *Hoge City, Präfektur Hoge
     */
    return place.getPostalCode()
      + System.lineSeparator()
      + place.getPrefecture() 
      + " "
      + place.getCity();
  }
}

Die Spezifikationen des Formteils können sich ändern. Da es sich um ein Beispiel handelt, können Sie leicht herausfinden, was geändert werden muss. In Wirklichkeit gibt es jedoch andere Prozesse, sodass es schwierig sein kann, diese zu finden.

after :bulb: Strategy

public class FooController {

  private FooLatLngToPlaceAPI api;
  private FooAddressFormatter formatter = new FooAddressFormatter();

  public String getFormattedAddress(double latitude, double longitude) {
    FooPlace place = api.getPlace(latitude, longitude);

    return formatter.format(place);
  }
}

public class FooAddressFormatter {

  public String format(FooPlace place) {
    /*
     * 123-4567
     *Hoge City, Präfektur Hoge
     */
    return place.getPostalCode()
      + System.lineSeparator()
      + place.getPrefecture() 
      + " "
      + place.getCity();
  }
}

Ich habe eine Klasse nur zum Formen gemacht. Wenn sich die Formatierung ändert, müssen Sie nur diese Klasse ändern. Der Punkt ist, dass durch Ausschneiden auf eine Klasse, die nur eine Rolle hat, leicht zu erkennen ist, welche Klasse und wo geändert werden muss, wenn die Spezifikationen geändert werden. Übrigens, wenn Sie es einfacher machen möchten, zu verstehen, was geändert werden muss, aber nicht genug, um eine neue Klasse zu erstellen, gehe ich wie folgt vor.

public class FooController {

  private FooLatLngToPlaceAPI api;

  private static final Function<FooPlace, String> FORMAT_ADDRESS =
    place -> place.getPostalCode()
      + System.lineSeparator()
      + place.getPrefecture() 
      + " "
      + place.getCity();

  public String getFormattedAddress(double latitude, double longitude) {
    FooPlace place = api.getPlace(latitude, longitude);

    return FORMAT_ADDRESS.apply(place);
  }
}

Wenn Sie Function als Konstante definieren und an die Spitze der Klasse schreiben, müssen Sie nicht nach Änderungen suchen.

: Frage: Ich möchte eine ähnliche Verarbeitung je nach Situation richtig verwenden

before

public String getFormattedText(String text, FormatType type) {
  switch (type) {
  case BOLD:
    return "**" + text + "**";

  case ITALIC:
    return "*"  + text + "*";

  default:
    return text;
  }
}

Wenn Sie die Formatierungsarten erhöhen möchten, müssen Sie den Zweig dieser switch-Anweisung vergrößern. Wenn sich die Formatierungsmethode aufgrund einer Änderung der Auszeichnungssprache ändert, müssen Sie dies ebenfalls ändern.

after :bulb: Strategy

public class FooFormatterFactory {

  private FooFormatterFactory() {}

  public static FooFormatter create(FormatType type) {
    switch (type) {
    case BOLD:
      return new FooBoldFormatter();

    case ITALIC:
      return new FooItalicFormatter();

    default:
      return new FooFormatter() {
        @Override
        public String format(String text) {
          return text;
        }
      }
    }
  }
}

public class FooBoldFormatter extends FooFormatter {

  @Override
  public String format(String text) {
    return "**" + text + "**";
  }
}

public class FooItalicFormatter extends FooFormatter {

  @Override
  public String format(String text) {
    return "*" + text + "*";
  }
}
public String getFormattedText(String text, FormatType type) {
  return FooFormatterFactory.create(type).format(text);
}

GetFormattedText muss nicht mehr geändert werden, wenn die Formatierungstypen zunehmen oder sich die Methode ändert. Wie in diesem Beispiel wird Strategie häufig in Kombination mit Factory verwendet. Wenn Sie nicht für jede Formatierungsart eine Klasse erstellen möchten, kombinieren Sie diese der Einfachheit halber mit einer funktionalen Oberfläche.

public class FooFormatterFactory {

  private FooFormatterFactory() {}

  public static UnaryOperator<String> create(FormatType type) {
    switch (type) {
    case BOLD:
      return text -> "**" + text + "**";

    case ITALIC:
      return text -> "*"  + text + "*";

    default:
      return text -> text;
    }
  }
}
public String getFormattedText(String text, FormatType type) {
  return FooFormatterFactory.create(type).apply(text);
}

: Frage: Ich möchte einige der vorherigen Ergebnisse wiederverwenden

before

public class FooController {

  private int nextId;

  public FooResponse get(FooRequest request) {
    FooResponse response = getResponse(request);
    nextId = response.getNextId();
    return response;
  }

  public FooResponse getNext() {
    FooRequest request = new FooRequest(nextId);
    return get(request);
  }

  private FooResponse getResponse(FooRequest request) {
    //wird bearbeitet
    return //Ergebnis
  }
}

getNext () stellt die nächste Anforderung basierend auf der im vorherigen Ergebnis enthaltenen nextId. Wenn sich die Anzahl oder Art der Werte ändert, die beim Erstellen einer Anforderung erforderlich sind, müssen Sie die von FooController gehaltenen Werte, die beizubehaltende Verarbeitung und die Verarbeitung zur Verwendung der beibehaltenen Werte ändern.

after :bulb: Memento

public class FooMemento {

  private int nextId;

  public void update(FooResponse response) {
    this.nextId = response.getNextId();
  }

  public FooRequest createNextRequest() {
    return new FooRequest(nextId);
  }
}
public class FooController {

  private FooMemento memento = new FooMemento();

  public FooResponse get(FooRequest request) {
    FooResponse response = getResponse(request);
    memento.update(response);
    return response;
  }

  public FooResponse getNext() {
    return get(memento.createNextRequest());
  }

  private FooResponse getResponse(FooRequest request) {
    //wird bearbeitet
    return //Ergebnis
  }
}

Ich habe eine Klasse erstellt, die die vorherigen Ergebnisse enthält. Wenn sich die erforderlichen Werte bei Ihrer nächsten Anfrage ändern, müssen Sie nur FooMemento ändern.

Recommended Posts

GoF-Entwurfsmuster aus dem Problem 3. Verhalten
GoF-Entwurfsmuster aus dem Problem 2. Struktur
GoF-Entwurfsmuster aus dem Problem 1. Generation
Grobe Zusammenfassung des GoF-Java-Entwurfsmusters
Lernen Sie das Entwurfsmuster "Prototype" mit Python
Lernen Sie das Entwurfsmuster "Builder" mit Python
Lernen Sie das Designmuster "Flyweight" in Python
Lernen Sie das Entwurfsmuster "Observer" 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 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 #Builder
Entwurfsmuster #Adapter
Entwurfsmuster #Decorator
Lernen Sie das Entwurfsmuster "Vorlagenmethode" in Python
Lernen Sie das Entwurfsmuster "Factory Method" in Python
Entwurfsmuster #Facade
Entwurfsmuster #Strategie
Entwurfsmuster #Proxy
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
Problem bei der Platzierung von Krankenwagen - Aus der Oktoberausgabe des OR-Magazins