Modèle de conception GoF à partir du problème 3. Comportement

C'est le comportement de la série de modèles de conception du GoF du point de vue des problèmes.

  1. Generation
  2. Structure
  3. Comportement

: question: je souhaite séparer les données et le traitement

before

public class FooAmusementPark {

  private FooZoo zoo;
  private FooAquarium aquarium;

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

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

Si le nombre de visiteurs autres que la famille et le couple augmente, nous devons changer de parc d'attractions.

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();
  }
}

Même si le nombre de clients autres que Famille et Couple augmente, la classe augmentée n'a besoin que de mettre en œuvre la méthode de visite sans changer AmusementPark. C'est une idée importante, qui ne se limite pas à ce modèle, de découper les parties susceptibles de changer à l'avenir. L'intérêt du modèle Visiteur est que la structure des données est fondamentalement difficile à modifier.

: question: je souhaite séparer les processus dont les spécifications changent facilement

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éfecture de Hoge
     */
    return place.getPostalCode()
      + System.lineSeparator()
      + place.getPrefecture() 
      + " "
      + place.getCity();
  }
}

Les spécifications de la pièce façonnée sont susceptibles de changer. Puisqu'il s'agit d'un exemple, vous pouvez facilement trouver ce qu'il faut changer, mais en réalité, il existe d'autres processus, donc cela peut être difficile à trouver.

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éfecture de Hoge
     */
    return place.getPostalCode()
      + System.lineSeparator()
      + place.getPrefecture() 
      + " "
      + place.getCity();
  }
}

J'ai fait un cours juste pour le modelage. Si la mise en forme change, il vous suffit de changer cette classe. Le fait est qu'en coupant vers une classe qui n'a qu'un seul rôle, il est facile de savoir quelle classe et où changer lorsque les spécifications sont modifiées. Au fait, si vous voulez faciliter la compréhension de ce qui doit être changé, mais pas assez pour créer une nouvelle classe, je fais ce qui suit.

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);
  }
}

En définissant Function comme une constante et en l'écrivant en haut de la classe, il n'est pas nécessaire de rechercher des modifications.

: question: je souhaite utiliser correctement un traitement similaire en fonction de la situation

before

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

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

  default:
    return text;
  }
}

Si vous souhaitez augmenter les types de mise en forme, vous devez augmenter la branche de cette instruction switch. En outre, si la méthode de mise en forme change en raison d'une modification du langage de balisage, vous devez également la modifier.

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 n'a plus besoin d'être modifié à mesure que les types de mise en forme augmentent ou que la méthode change. Comme dans cet exemple, Strategy est souvent utilisé en combinaison avec Factory. Si vous ne souhaitez pas créer une classe pour chaque type de mise en forme, combinez-la avec une interface fonctionnelle pour plus de simplicité.

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);
}

: question: je souhaite réutiliser certains des résultats précédents

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) {
    //En traitement
    return //résultat
  }
}

getNext () effectue la requête suivante en fonction du nextId inclus dans le résultat précédent. Si le nombre ou le type de valeurs requis lors de la création d'une demande change, vous devez modifier les valeurs détenues par FooController, le traitement à conserver et le traitement pour utiliser les valeurs conservées.

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) {
    //En traitement
    return //résultat
  }
}

J'ai créé une classe qui contient les résultats précédents. Si les valeurs requises changent lors de votre prochaine demande, il vous suffit de modifier FooMemento.

Recommended Posts

Modèle de conception GoF à partir du problème 3. Comportement
Modèle de conception du GoF à partir du problème 2. Structure
Modèle de conception du GoF à partir du problème 1. Génération
Résumé du modèle de conception Java GoF
Apprenez le modèle de conception "Prototype" avec Python
Apprenez le modèle de conception "Builder" avec Python
Apprenez le modèle de conception "Flyweight" en Python
Apprenez le modèle de conception "Observer" en Python
Apprenez le modèle de conception "Memento" avec Python
Apprenez le modèle de conception "Proxy" en Python
Apprenez le modèle de conception "Commande" en Python
Apprenez le modèle de conception "Visiteur" avec Python
Apprenez le modèle de conception "Bridge" avec Python
Apprenez le modèle de conception "Décorateur" avec Python
Apprenez le modèle de conception "Iterator" avec Python
Apprenez le modèle de conception «Stratégie» avec Python
Apprenez le modèle de conception "Composite" avec Python
Apprenez le modèle de conception "Singleton" avec Python
Apprenez le modèle de conception "État" en Python
Apprenez le modèle de conception "Adapter" avec Python
Apprenez le modèle de conception "Façade" avec Python
Modèle de conception #Builder
Modèle de conception #Adapter
Modèle de conception #Decorator
Apprenez le modèle de conception "Méthode de modèle" en Python
Apprenez le modèle de conception "Méthode d'usine" en Python
Modèle de conception #Facade
Modèle de conception #Strategy
Modèle de conception #Proxy
Apprenez le modèle de conception «Chaîne de responsabilité» en Python
Design Pattern #Factory, méthode
Problème de conception du réseau logistique
Design Pattern #Template, méthode
À propos du modèle de visiteur
Examiner le double problème
Problème de placement d'ambulance - Tiré du numéro d'octobre du magazine OR