[JAVA] L'histoire selon laquelle les modèles de conception du GoF ne sont rien de plus qu'une «introduction de couches abstraites»

introduction

Lors de l'apprentissage des modèles de conception

Je pense que vous étudierez en lisant un tel chef-d'œuvre, mais il y a 23 types de modèles de conception dits GoF qui apparaissent dans ceux-ci, et il est difficile de s'en souvenir. Cependant, à la fin, ce que vous faites peut être classé en deux modèles, et je vais expliquer comment vous pouvez mieux comprendre en sachant dans quel (ou les deux) chaque modèle est classé.

La théorie selon laquelle il n'y a que deux modèles de conception du GoF du point de vue de "l'introduction de couches abstraites"

Dans le monde du logiciel, "nous pouvons résoudre n'importe quel problème en introduisant un niveau supplémentaire d'indirection." (Https://en.wikipedia.org) Il semble y avoir / wiki / Fundamental_theorem_of_software_engineering), mais le modèle de conception du GoF est un exemple concret de cette maxime. En examinant le modèle de conception du point de vue de ** «Présentation de classes / interfaces qui sont des couches abstraites» **, le but de l'introduction de couches abstraites que le modèle de conception GoF fait est deux ou les deux.

  1. Introduisez une couche intermédiaire (classe / méthode) pour effectuer certains traitements
  2. Introduction d'une interface pour séparer et regrouper des codes similaires

Il est naturel de dire que c'est naturel, mais à l'extrême, on peut penser que le ** modèle de conception GoF n'est qu'une combinaison de ces deux modèles **. Il peut être plus facile de comprendre si vous savez à quel modèle appartient chaque modèle.

Le tableau ci-dessous résume si chaque motif correspond à 1 ou 2 (ou les deux).

Pattern 1.Classe moyenne/Introduction de la méthode 2 Introduction de l'interface de séparation / agrégation
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

J'ai dit tout ce que je voulais dire ci-dessus, mais en prime, j'expliquerai chaque motif dans la perspective de "l'introduction d'une couche abstraite".

Iterator Introduisez une interface pour l'accès aux éléments.

Statut

Supposons que vous ayez une classe JunkList pour votre propre collection.

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

Si vous souhaitez analyser tous les éléments, vous devez examiner l'implémentation pour voir comment accéder aux éléments et comment obtenir leur taille. Dans ce cas, vous devez regarder l'implémentation pour voir l'implémentation obtenir le nombre d'éléments avec getIndex () et obtenir chaque élément avec getElement (), puis parcourir la boucle.

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");
        ...
        ...
        //Accéder à tous les éléments
        for (int i = 0; i < junkList.getIndex(); ++i) {
            System.out.println(junkList.getElement(i));
        }
    }
}

Application de modèle

Introduit l'interface ʻIterator et résume le comportement d'accès aux éléments dans JunkListIterator, qui est l''Iterator pour JunkList. Les opérations d'accès aux éléments sont agrégées et sont accessibles avec une interface unifiée.

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");
        ...
        //Accéder à tous les éléments
        Iterator<String> itr = junkList.iterator();
        while (itr.hasNext()) {
            System.out.println(itr.next());
        }
    }
}

Adapter Introduisez une classe qui est une couche intermédiaire pour changer l'interface.

Statut

Supposons que vous ayez une situation où votre code existant semble utiliser l'interface Charger (chargeur ) Charger.getPower100V ().

Charger.java


public interface Charger {
    Power100V getPower100V();
}

Cependant, supposons que la classe Generator (générateur) de la bibliothèque nouvellement introduite ne possède que la méthode Generator.getPower200V () qui fournit une alimentation de 200V.

Generator.java


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

Dans de tels cas, vous pouvez ajouter une méthode comme getPower100V () à Generator, mais vous ne voulez pas jouer avec le code de la bibliothèque existante.

Application de modèle

Présentation de la classe Transformer, faisant correspondre l'interface à la précédente getPower100V (), puis En interne, il délègue le traitement à Generator. En utilisant Generator via Transformer, l'utilisateur peut toujours utiliser l'interface degetPower100V ().

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 Introduire une classe pour déléguer l'implémentation et la séparer de la relation d'héritage

Statut

Supposons que vous ayez une interface Book et que vous ayez des sous-classes ʻEBook et 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");
    }
}

Si vous voulez créer les classes Novel et Nonfiction comme contenu du livre, vous devez hériter de chacun des ʻEBook et PaperBook`. Il faudrait créer deux classes avec la même implémentation, ce qui serait redondant.

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

Application de modèle

L'implémentation de showContent () sur le contenu du livre introduit et lui délègue l'interface BookImpl, la séparant de la relation d'héritage. Cela élimine le besoin de créer des classes pour Novel et Nonfiction pour ʻEBook et PaperBook`, respectivement.

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 Introduire une classe qui est une couche intermédiaire de génération d'instances

Statut

Prenons le cas de la génération d'instructions au format HTML ou Markdown. Il produit une instruction simple avec uniquement des en-têtes et des paragraphes.

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

Ce n'est pas un problème, mais je suis préoccupé par le fait que la partie balise et la partie contenu sont gérées dans la même ligne avec des systèmes différents. D'un point de vue abstrait, les deux processus HTML / Markdown sont organisés dans l'ordre du contenu de l'en-tête et du paragraphe. Il semble que vous puissiez organiser votre code en introduisant des interfaces et des classes basées sur celui-ci.

Application de modèle

Nous allons introduire l'interface Builder et assembler le code.

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

La partie balise HTML / Markdown a été séparée et agrégée dans la classe HTMLBuilder / MarkdownBuilder. De plus, la partie de contenu pourrait être séparée pour être définie dans Director.construct ().

Strategy Introduire une interface pour «stratégie» et consolider les parties d'implémentation et unifier l'interface.

Statut

Prenons une situation où chaque classe représentant une équipe de football met en œuvre une stratégie.

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

Cela nécessite plus de classes à mesure que la stratégie se développe. La partie stratégique semble définir et séparer l'interface.

Application de modèle

Introduit l'interface «Stratégie» et délègue le traitement de la partie stratégie. SoccerTeam n'a besoin que d'appeler Strategy.execute (), éliminant ainsi le besoin de créer une classe pour chaque stratégie.

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 Introduction de la méthode "Get Instances" pour limiter le nombre d'instances à une

Statut

Vous pouvez créer n'importe quel nombre d'instances si vous pouvez utiliser new pour les créer. Supposons que vous ayez une situation dans laquelle vous souhaitez limiter le nombre d'instances à 1.

Application de modèle

Rendez le constructeur privé et introduisez une méthode appelée getIntance () qui signifie l'opération abstraite consistant à "obtenir une instance". Vous pouvez limiter le nombre d'instances à 1 en renvoyant une seule instance créée via cette méthode.

Singleton.java


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

    //Interdire le neuf de l'extérieur
    private Singleton() {
    }

    //Renvoie uniquement l'instance
    public static Singleton getInstance() {
        return singleton;
    }
}

Prototype Introduire des classes pour l'enregistrement des instances et la génération de copies

Statut

Supposons que vous ayez besoin d'un grand nombre d'instances Slime qui implémentent l'interface Monster et que vous souhaitiez créer une instance avec une copie d'une instance existante en raison du coût élevé de création d'une nouvelle instance. Vous ne pouvez créer une instance qu'au début, puis créer une instance avec createClone (), mais cela semble difficile à gérer à mesure que le nombre de types d'instances augmente.

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

Application de modèle

L'introduction de la classe MonsterManager pour la gestion des instances rend votre code plus organisé et plus compréhensible. Dans MonsterManager, si l'instance créée est enregistrée, elle est retournée, et seulement si elle n'est pas enregistrée, l'instance est créée et retournée.

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 Introduire une méthode qui combine plusieurs processus

Statut

Supposons que vous ayez une situation où vous voulez lire un fichier CSV -> vider chaque donnée en hexadécimal avec les classes CsvParser et StringDumper, respectivement.

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

Si ce processus read-> dump est un cas d'utilisation typique, il semble pratique d'introduire une méthode qui les effectue tous ensemble.

Application de modèle

Introduisez CsvHexDumper avec la méthode hexdumpCsvFile qui résume le processus comme une classe Facade.

CsvHexDumper.java


public class CsvHexDumper {
    //Faites-en une classe dédiée aux méthodes statiques
    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));
            }
        }
    }
}

L'utilisateur peut effectuer une série d'opérations simplement en appelant cela CsvHexDumper.hexdumpCsvFile ().

After.java


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

Mediator Introduction d'une classe qui sert d'intermédiaire dans la communication entre les instances

Statut

Il existe une classe «Piéton» (piéton) et une classe «Voiture», «Voiture» peut continuer sans demande d'arrêt de «Piéton» et «Piéton» avance si «Voiture» est arrêtée. Supposons qu'il existe une situation où une communication mutuelle entre les instances est requise. Considérez une implémentation dans laquelle «Voiture» et «Piéton» se réfèrent aux instances de l'autre et communiquent leurs états.

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

Au fur et à mesure que le nombre d'instances augmente et que le nombre de communications mutuelles augmente, le traitement devient compliqué et il y a une odeur (?) Qui semble se décomposer.

Application de modèle

Présentez l'interface TrafficLight et son implémentation en tant que médiateur et organisez votre code. Ceux-ci assurent la communication entre les instances.

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

«Voiture» et «Piéton» réduisent la complexité de la communication entre instances en communiquant via «TrafficLight».

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 Introduire une classe pour enregistrer / restaurer l'état de l'instance

Statut

Supposons que vous ayez une classe Hero avec un niveau et des HP, et que vous souhaitiez enregistrer le niveau et les HP à un moment donné et restaurer cet état plus tard.

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

Les niveaux et les HP sont traités comme un ensemble, il semble donc plus facile de comprendre s'ils sont regroupés en instances.

Application de modèle

Présentation de la classe Memento, qui résume le niveau et HP, et enregistre / restaure l'état pour correspondre à la création / chargement de Memento. À ce stade, si vous voulez rendre les informations générées Memento invisibles (utilisées uniquement pour la restauration), séparez les packages ou utilisez des classes internes. Voici un exemple utilisant une classe interne (voir aussi Wikipedia Exemple)

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(); //Le contenu de ce souvenir est principal()Ne peut pas être référencé depuis
        
        hero.levelUp();
        hero.receiveDamage(3);
        System.out.println(hero);
        ... 
        hero.restoreMemento(memento);
        System.out.println(hero);
    }        
}

State Introduire une interface qui représente les états et un traitement séparé pour chaque état

Statut

Supposons que vous ayez une situation où vous créez un compte sur un site Web-> Connexion-> Déconnexion. Pour effectuer des opérations en fonction de la situation, telles que l'impossibilité de se connecter avant de créer un compte ou l'impossibilité de se reconnecter pendant la connexion. Il est bon d'avoir une variable qui indique l'état et de diviser le traitement en fonction de l'état avec l'instruction if ou l'instruction switch, mais cela peut être difficile à comprendre car il existe de nombreuses branches.

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() {
      ...
  }
}

Application de modèle

Introduit l'interface «State» qui indique l'état, et sépare et agrège l'opération dans chaque état dans la classe d'implémentation de «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());
    }
}

Le traitement de createAccount (), login (), logout () est délégué à State.createAccount (), State.login (), State.logout (), et l'état change. En changeant la classe d'implémentation, les opérations pour chaque état sont séparées pour chaque classe.

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 Introduire la classe de gestion d'instance générée et l'instance de partage

Statut

Considérez une situation où vous avez besoin de beaucoup de classes HeavyObject gourmandes en mémoire.

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

Comme l'instance avec le même contenu est régénérée à chaque fois, il semble qu'il y ait un gaspillage de consommation de ressources.

Application de modèle

Si l'instance est partageable, vous pouvez introduire HeavyObjectFactory pour renvoyer l'instance si elle a déjà été créée. Il est possible de ne pas faire de nouveautés supplémentaires.

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 Insérez une classe avec la même interface pour retarder le traitement lourd

Statut

Considérez qu'il existe un «HeavyDumper» qui a un processus d'initialisation lourd, et dump () est effectué après divers autres processus après l'initialisation.

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

Même si vous avez vraiment besoin de la fonctionnalité de HeavyDumper lorsque vous appelez dump (), cela fait beaucoup de travail lorsque vous l'instanciez.

Application de modèle

Introduit la classe Proxy ProxyDumper pour retarder l'instanciation de HeavyDumper jusqu'à ce qu'elle soit requise par l'appeldump ().

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 n'est pas généré ici
        ...
        String str = dumper.getName(); //Encore une fois, HeavyDumper n'est pas généré
        ...
        dumper.dump(); //Génération HeavyDumper pour la première fois ici
    } 
}

Composite

Interpreter Présentation d'un «langage» spécialisé pour des traitements spécifiques

Application de modèle

(Implémentation spécifique omise) Le modèle consiste à exprimer les règles de grammaire dans une classe, mais il donne une impression légèrement différente par rapport aux autres modèles. Cependant, du point de vue de "l'introduction d'une couche abstraite", je pense que nous pouvons comprendre pourquoi ce modèle apparaît.

Jusqu'à présent, nous avons expliqué les modèles de conception du point de vue de «l'introduction de couches abstraites», mais je pense que la forme ultime est le modèle Interpreter. C'est un modèle dont nous ne sommes pas fatigués de simplement introduire des interfaces et des classes (?), Mais nous allons introduire un langage spécialisé pour une sorte de traitement.

La publication de «Design Patterns for Reuse in Object-Oriented» en 1994 et l'avènement de Java en 1995 semblent symboliser l'écoulement de l'époque.

Résumé

C'était une tentative qu'il serait plus facile de comprendre si le modèle de conception était expliqué du point de vue de "l'introduction d'une couche abstraite".

référence

[Modèle de conception pour réutilisation dans l'orientation objet](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 & mots-clés =% 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 = livres & sr = 1-2) [Introduction aux modèles de conception appris dans le langage Java augmenté et révisé](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 & mots-clés =% 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 = livres & sr = 1-1) Memento pattern

Recommended Posts

L'histoire selon laquelle les modèles de conception du GoF ne sont rien de plus qu'une «introduction de couches abstraites»
Introduction d'une méthode de conception de contrôleur pilotée par les données
Résumé du modèle de conception Java GoF
[Gang of Four] Apprentissage des modèles de conception --Singleton
[Gang of Four] Apprentissage des modèles de conception - Décorateur
[Gang of Four] Apprentissage des modèles de conception - Visiteur
[Gang of Four] Apprentissage des modèles de conception - Médiateur
[Gang of Four] Apprentissage des modèles de conception - Itérateur
Modèle de conception du GoF à partir du problème 2. Structure
[Gang of Four] Apprentissage des modèles de conception - Façade
[Gang of Four] Apprentissage des modèles de conception - Composite
[Gang of Four] Apprentissage des modèles de conception - Prototype
Modèle de conception du GoF à partir du problème 1. Génération
[Gang of Four] Apprentissage des modèles de conception --Mémento
[Gang of Four] Apprentissage des modèles de conception - État
[Gang of Four] Apprentissage des modèles de conception - Interprétation
[Gang of Four] Apprentissage des modèles de conception - Constructeur
[Gang of Four] Apprentissage des modèles de conception - Pont
[Gang of Four] Apprentissage des modèles de conception - Proxy
[Gang of Four] Apprentissage des modèles de conception - Stratégie
[Gang of Four] Apprentissage des modèles de conception - Adaptateur
[Gang of Four] Apprentissage des modèles de conception --Observer
[Gang of Four] Apprentissage des modèles de conception - Commande
Modèle de conception GoF à partir du problème 3. Comportement