[JAVA] GoF design pattern is just an "introduction of abstraction layer"

Introduction

When learning about design patterns

-[Design pattern for reuse in object orientation](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 & keywords =% 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 = 1563614653 & s = books & sr = 1-2)

-[Introduction to Design Patterns Learned in the Augmented and Revised Java Language](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 & keywords =% 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 = books & sr = 1-1)

I think that you will study by reading such a masterpiece, but there are 23 kinds of so-called GoF design patterns that appear in these, and it is difficult to remember. However, in the end, what you are doing can be classified into two patterns, and I will talk about how you can understand more by being aware of which (or both) each pattern is classified into.

The theory that there are only two GoF design patterns from the viewpoint of "introduction of abstract layer"

In the world of software, "We can solve any problem by introducing an extra level of indirection." (Https://en.wikipedia.org) There seems to be / wiki / Fundamental_theorem_of_software_engineering), but the GoF design pattern is a concrete example of this maxim. Looking back at the design pattern from the perspective of ** "Introducing a class / interface that is an abstract layer" **, the purpose of introducing an abstract layer that the GoF design pattern is doing is two or both of the following.

  1. Introduce an intermediate layer (class / method) to do some processing
  2. Introduced an interface for separating and aggregating similar codes

It is natural to say that it is natural, but in the extreme, you can think of ** GoF design patterns as only a combination of these two patterns **. Understanding which pattern each pattern falls into may help you understand.

The table below summarizes whether each pattern corresponds to 1 or 2 (or both).

Pattern 1.Middle class/Method introduction 2 Introduced separation / aggregation interface
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

I've said everything above, but as a bonus, I'll explain each pattern from the perspective of "introducing an abstraction layer."

Iterator Introduce an interface for element access.

Status

Suppose you have a JunkList class for your own 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;
    }
}

If you want to scan all the elements, you need to look at the implementation to see how to access and get the size of the elements. In this case, you have to look at the implementation to see that getIndex () gets the number of elements and getElement () gets each element, and then loop around.

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");
        ...
        ...
        //Access all elements
        for (int i = 0; i < junkList.getIndex(); ++i) {
            System.out.println(junkList.getElement(i));
        }
    }
}

Pattern application

Introduces the ʻIterator interface and summarizes the element access behavior in JunkListIterator, which is the ʻIterator for JunkList. Element access operations are aggregated and can be accessed with a unified interface.

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");
        ...
        //Access all elements
        Iterator<String> itr = junkList.iterator();
        while (itr.hasNext()) {
            System.out.println(itr.next());
        }
    }
}

Adapter Introduce a class that is an intermediate layer for changing the interface.

Status

Suppose you have a situation where your existing code seems to be using the Charger (charger) interfaceCharger.getPower100V ().

Charger.java


public interface Charger {
    Power100V getPower100V();
}

However, suppose that the class Generator (generator) of the newly introduced library only has theGenerator.getPower200V ()method that supplies 200V power.

Generator.java


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

In such cases, you could add a method like getPower100V () to Generator, but you don't want to mess with the code in your existing library.

Pattern application

Introducing the Transformer class, matching the interface to the previousgetPower100V (), and then Internally delegates processing to Generator. By using Generator via Transformer, the user can still use the interface of getPower100V ().

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 Introduce a class to delegate the implementation and separate it from the inheritance relationship

Status

Suppose you have a Book interface and you have subclasses ʻEBook and 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");
    }
}

If you want to create Novel and Nonfiction classes as the contents of a book, you need to inherit from ʻEBook and PaperBook` respectively. You would have to create two classes with the same implementation, which would be redundant.

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

Pattern application

The implementation of showContent () on the contents of the book introduces and delegates the BookImpl interface to it, separating it from inheritance relationships. This eliminates the need to create classes for Novel and Nonfiction for ʻEBook and PaperBook`, respectively.

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 Introduce a class that is an intermediate layer of instance generation

Status

Consider the case of generating HTML or Markdown format statements. It produces a simple statement with only headers and paragraphs.

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

This is not a problem, but I am concerned about the fact that the tag part and the content part are handled in the same line with different systems. From an abstract point of view, both HTML / Markdown processing is arranged in the order of header and paragraph contents. It seems that you can organize your code by introducing interfaces and classes based on it.

Pattern application

We will introduce the Builder interface and put the code together.

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

The HTML / Markdown tag part has been separated and aggregated into the HTMLBuilder / MarkdownBuilder class. Also, the content part could be separated to be set in Director.construct ().

Strategy Introduce an interface for "strategy", consolidate implementation parts and unify the interface.

Status

Consider a situation where each class representing a soccer team implements some strategy.

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

This requires more classes as the strategy grows. The strategic part seems to define and separate the interface.

Pattern application

Introduces the Strategy interface and delegates the processing of the strategy part. SoccerTeam only needs to callStrategy.execute (), eliminating the need to create a class for each strategy.

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 Introduce "Get Instances" method to limit the number of instances to one

Status

You can create any number of instances if you can use new to create the instance. Suppose you have a situation where you want to limit the number of instances to 1.

Pattern application

Make the constructor private and introduce a method called getIntance () that means the abstract operation of "getting an instance". You can limit the number of instances to 1 by returning only one instance that has been created via that method.

Singleton.java


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

    //Prohibit new from the outside
    private Singleton() {
    }

    //Returns only instance
    public static Singleton getInstance() {
        return singleton;
    }
}

Prototype Introduce a class for instance registration and its copy generation

Status

Suppose you need a lot of Slime instances that implement the Monster interface & you want to create an instance with a copy of an existing instance because the cost of creating a new instance is high. You can create an instance only at the beginning and then create an instance with createClone (), but it seems to be difficult to manage as the number of instance types increases.

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

Pattern application

Introducing the MonsterManager class for instance management makes your code more organized and easier to understand. In MonsterManager, if the created instance is registered, it is returned, and only if it is not registered, the instance is created and returned.

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 Introduce a method that combines multiple processes

Status

Suppose you have a situation where you want to read a CSV file-> dump each data in hexadecimal with the CsvParser and StringDumper classes, respectively.

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

If this read-> dump process is a typical usage example, it seems to be convenient to introduce a method that performs them all together.

Pattern application

Introduce CsvHexDumper with the method hexdumpCsvFile that summarizes the processing as a Facade class.

CsvHexDumper.java


public class CsvHexDumper {
    //Make it a class dedicated to static methods
    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));
            }
        }
    }
}

The user can do a series of operations simply by calling this CsvHexDumper.hexdumpCsvFile ().

After.java


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

Mediator Introduced a class that mediates communication between instances

Status

There is a Pedestrian (pedestrian) class and a Car class, Car can proceed without a stop request from Pedestrian, and Pedestrian advances if Car is stopped. Suppose you have a situation where you need to communicate with each other, such as. Consider an implementation where Car and Pedestrian refer to each other's instances and communicate their state.

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

As the number of instances increases and the number of mutual communications increases, the processing becomes complicated and there is a smell (?) That seems to collapse.

Pattern application

Introduce the TrafficLight interface and its implementation as a Mediator and organize your code. These mediate communication between 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);
    }
}

Both Car and Pedestrian reduce the complexity of inter-instance communication by communicating 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 Introduce class for saving / restoring instance state

Status

Suppose you have a Hero class with a level and HP, and you want to save the level and HP at some point and restore that state later.

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

Levels and HP are treated as a set, so it seems easier to understand if they are grouped into instances.

Pattern application

Introduce the Memento class, which combines levels and HP, and save / restore state to support the creation / loading of Memento. At this time, if you want to make the generated Memento information invisible (only used for restoration), separate packages or use inner classes. Here is an example using an inner class (see also Wikipedia's Example).

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(); //The content of this memento is main()Cannot be referenced from
        
        hero.levelUp();
        hero.receiveDamage(3);
        System.out.println(hero);
        ... 
        hero.restoreMemento(memento);
        System.out.println(hero);
    }        
}

State Introduce an interface that represents states and separate processing for each state

Status

Suppose you have a situation where you want to create an account on your website-> Login-> Logout. To perform operations depending on the situation, such as being unable to log in before creating an account, or being unable to log in again while logged in. It is good to have a variable that indicates the state and divide the processing according to the state with an if statement or switch statement, but it may be difficult to understand because there are many 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() {
      ...
  }
}

Pattern application

Introduce a State interface that indicates the state, and separate and aggregate the operation in each state into the implementation class of 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());
    }
}

The processing of createAccount (), login (), logout () is delegated to State.createAccount (), State.login (), State.logout (), and the state changes. By switching the implementation class, operations for each state are separated for each class.

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 Introduce generated instance management class and share instance

Status

Consider a situation where you need a lot of memory-consuming HeavyObject classes.

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

Since the instance with the same content is regenerated every time, it seems that there is wasteful resource consumption.

Pattern application

If the instance is sharable, you can introduce HeavyObjectFactory to return it if the instance has already been created. It is possible not to do extra new.

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 Insert a class with the same interface and delay heavy processing

Status

Consider that there is a HeavyDumper that has a heavy initialization process, and dump () is performed after various other processes after initialization.

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

Even if the functionality of HeavyDumper is really needed when callingdump (), heavy processing is done when instantiating.

Pattern application

Introduces the Proxy class ProxyDumper to delay instantiation of HeavyDumper until it is needed by the dump () call.

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 is not generated here
        ...
        String str = dumper.getName(); //Again, HeavyDumper is not generated
        ...
        dumper.dump(); //HeavyDumper generation for the first time here
    } 
}

Composite

Interpreter Introducing a "language" specialized for specific processing

Pattern application

(Specific implementation omitted) The pattern is to express the grammar rules in a class, but it gives a slightly different impression when compared to other patterns. However, from the perspective of "introducing an abstraction layer," I think we can see why this pattern appears.

So far, we have explained design patterns from the perspective of "introducing an abstract layer," but I think the ultimate form is the Interpreter pattern. It is not enough to introduce an interface or class (?), But it is a pattern to introduce a language specialized for some processing.

The publication of "Design Patterns for Reuse in Object-Oriented Programming" in 1994 and the advent of Java in 1995 seems to symbolize the flow of the times.

Summary

The attempt was to explain the design pattern from the perspective of "introducing an abstract layer" so that it would be easier to understand.

reference

[Design pattern for reuse in object orientation](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 & keywords =% 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 = books & sr = 1-2) [Introduction to Design Patterns Learned in the Augmented and Revised Java Language](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 & keywords =% 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 = books & sr = 1-1) Memento pattern

Recommended Posts

GoF design pattern is just an "introduction of abstraction layer"
Introduction of data-driven controller design method
GoF java design pattern rough summary
[Gang of Four] Design pattern learning --Singleton
[Gang of Four] Design Pattern Learning --Decorator
[Gang of Four] Design pattern learning --Visitor
[Gang of Four] Design pattern learning --Mediator
[Gang of Four] Design pattern learning --Iterator
GoF design pattern from the problem 2. Structure
[Gang of Four] Design pattern learning --Facade
[Gang of Four] Design pattern learning --Composite
[Gang of Four] Design pattern learning --Prototype
GoF design pattern from the problem 1. Generation
[Gang of Four] Design pattern learning --Memento
[Gang of Four] Design pattern learning --State
[Gang of Four] Design pattern learning --Interpreter
[Gang of Four] Design pattern learning --Builder
[Gang of Four] Design pattern learning --Bridge
[Gang of Four] Design pattern learning --Proxy
[Gang of Four] Design pattern learning --Strategy
[Gang of Four] Design pattern learning --Adapter
[Gang of Four] Design pattern learning --Observer
[Gang of Four] Design pattern learning --Command
GoF design pattern from the problem 3. Behavior