Lors de l'apprentissage des modèles de conception
[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 amélioré 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 = 156314653 & s = livres & sr = 1-1)
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é.
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.
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.
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));
}
}
}
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.
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.
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
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.");
}
}
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
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.
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.
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.
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
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.
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
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();
...
}
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
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.
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
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.
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
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.
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
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() {
...
}
}
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
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.
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
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.
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
(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.
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".
[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