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.
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.
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.
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));
}
}
}
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.
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.
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
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.");
}
}
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
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.
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.
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.
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
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.
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
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();
...
}
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
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.
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
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.
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
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.
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
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() {
...
}
}
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
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.
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
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.
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
(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.
The attempt was to explain the design pattern from the perspective of "introducing an abstract layer" so that it would be easier to understand.
[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