Zen reading notes of design patterns - 22 design patterns

Recently, I am reading Qin Xiaobo's Zen of design pattern. This article is another 2000 line long and watery note, recording 22 of the 23 design patterns mentioned in the book, basically copying the definition, advantages, disadvantages, applicable scenarios and demo of each design pattern mentioned in the book. It is recommended to read the original book, which is written by an author with rich knowledge and an interesting soul. There is a very interesting example in the explanation of each design pattern in the original book. With these examples, we can deepen our understanding and memory.
Design patterns are interlinked. I read a book "JavaScript Design Patterns and development practices" before, which is actually the same thing. It's only because of the differences in implementation due to the language features. The author of this book also has an interesting soul. I still remember the story of "Xiaoming chasing Meimei" in the proxy mode, and I recommend reading it.

01 single case mode

definition:

Ensure that there is only one instance of a class, and that it instantiates itself and provides the instance to the whole system

Example:

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

    // Privatization structure
    private Singleton() {

    }

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

    // Other methods in the class should be static as much as possible
    public static void otherMethod() {

    }
}

advantage:

  1. Single instance mode only generates one instance, so it reduces the system performance overhead
  2. Single instance mode can avoid multiple occupation of resources
  3. In memory, avoid using the same resource

Disadvantages:

  1. Generally, there is no interface, so it is difficult to expand
  2. Not good for testing
  3. Against the principle of single responsibility

Applicable scenarios:

  1. Require unique serial number environment to be generated
  2. Too many resources needed to create an object
  3. An environment where a large number of static constants and static methods (such as tool classes) need to be defined

02 plant method mode

definition:

Define an interface for creating objects, and let subclasses decide which class to instantiate. Factory methods delay the instantiation of a class to its subclasses

Example:

/**
 * Abstract product
 */
public abstract class Product {
    public void method1() {
        System.out.println("method1");
    }
    abstract public void method2();
}

/**
 * A specific product
 */
public class ProductA extends Product {
    public void method2() {
        System.out.println("productA method2");
    }
}

/**
 * A specific product
 */
public class ProductB extends Product {
    public void method2() {
        System.out.println("productB method2");
    }
}

/**
 * Abstract factory
 */
public abstract class Factory {
    public abstract <T extends Product> T createProduct(Class<T> clazz);
}

/**
 * Specific plant
 */
public class ProductFactory1 extends Factory {
    public <T extends Product> T createProduct(Class<T> clazz) {
        Product product = null;
        try {
            product = (Product) Class.forName(clazz.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return (T) product;
    }
}

/**
 * Test class
 */
public class FactoryTest {
    @Test
    public void test() {
        Factory factory = new ProductFactory1();
        ProductA productA = factory.createProduct(ProductA.class);
        ProductB productB = factory.createProduct(ProductB.class);
        productA.method2();
        productB.method2();
    }
}

advantage:

  1. Good encapsulation and clear code structure
  2. The extensibility of the factory method mode is very excellent, which can easily add product categories
  3. Shielding products. You only need to care about the interface of the product, and you don't need to care about the implementation
  4. Factory method pattern is a typical decoupling framework

Usage scenario:

  1. The factory method pattern is an alternative to the new object, but you need to consider whether you need to use a factory to increase code complexity
  2. When flexible and extensible framework is needed, factory method mode can be considered
  3. Factory method patterns can be used in heterogeneous projects
  4. It can be used in the framework of Test Driven Development

Factory mode extension:

  1. Use static factory
/**
 * Static factory
 */
public class ProductFactory2 {
    private ProductFactory2() {
    }

    static Product createProduct(Class<Product> clazz) {
        Product product = null;
        try {
            product = (Product) Class.forName(clazz.getName()).newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return product;
    }
}
  1. Multi factory mode

  2. Implement singleton mode

  3. Delay initialization

03 abstract factory mode

definition:

Provides an interface for creating a set of related or interdependent objects without specifying their concrete classes

Example:

public class Factory {
    public ProductA createProductA() {
        return new ProductA();
    }

    public ProductB createProductB() {
        return new ProductB();
    }
}

advantage:

  1. Encapsulation, as long as you know who the factory class is, you can create objects
  2. Constraints within the product family are non-public. Which product class is used to create it is private

Disadvantages:

  1. It's hard to add products. If you don't add a product, you need to change the factory

Applicable scenarios:

Abstract factory pattern can be used if an object family (or a group of objects without any relationship) has the same constraints. It's a bunch of classes that have the same parents

04 template method mode

definition:

Define the framework of the algorithm in an operation, and delay some steps to the subclass. So that the subclass can redefine some specific steps of an algorithm without changing the structure of the algorithm

Example:

/**
 * template class
 */
public abstract class TemplateClass {
    protected abstract void method1();
    protected abstract void method2();

    /**
     * Template method
     */
    public void templateMethod() {
        method1();
        method2();
    }
}

/**
 * Classes created from template classes
 */
public class TemplateSubClass1 extends TemplateClass {
    protected void method1() {
        System.out.println("1 method1");
    }

    protected void method2() {
        System.out.println("1 method2");
    }
}

/**
 * Classes created from template classes
 */
public class TemplateSubClass2 extends TemplateClass {
    protected void method1() {
        System.out.println("2 method1");
    }

    protected void method2() {
        System.out.println("2 method2");
    }
}

/**
 * Test class
 */
public class TemplateTest {
    @Test
    public void test() {
        TemplateSubClass1 templateSubClass1 = new TemplateSubClass1();
        templateSubClass1.templateMethod();
        TemplateSubClass2 templateSubClass2 = new TemplateSubClass2();
        templateSubClass2.templateMethod();
    }
}

advantage:

  1. Encapsulation invariant part
  2. Extract common part code for easy maintenance
  3. Behavior is controlled by the parent class and implemented by the child class

Disadvantages:

Subclasses have an impact on the parent class. In complex projects, it will bring difficulty in code reading and make it difficult for novices to adapt

Applicable scenarios:

Multiple subclasses have public methods, and the logic is basically the same

Template method mode extension:

You can adjust the execution process of the template method by defining the form of switches

05 builder mode

definition:

Separate the construction of a complex object from its representation, so that the same construction process can create different representations

Example:

public class Product {
    private String name;
    private int price;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }
}

public class ProductBuilder {
    private Product product = new Product();

    public void buildName(String name) {
        product.setName(name);
    }

    public void buildPrice(Integer price) {
        product.setPrice(price);
    }

    public Product create() {
        return product;
    }
}

advantage:

  1. Using the builder pattern, clients do not have to know the details of the product's internal composition
  2. Independent builder, easy to expand
  3. As the specific builder is independent, it can gradually refine the construction process without any impact on other modules

Disadvantages:

  1. If there are great differences between products, it is not suitable to use builder mode, so its use scope is limited
  2. If the internal change of the product is complex, it may lead to the need to define many specific builder classes to achieve this change, resulting in a very large system

Applicable scenarios:

  1. When the same method, different execution order and different event results are generated, the builder mode can be adopted
  2. The product class is very complex, or the call order in the product class is different, resulting in different performance
  3. Some other objects in the system will be used in the process of object creation. These objects are not easy to get in the process of product object creation

06 agent mode

definition:

Provides a proxy for other objects to control access to this object

Example:

/**
 * Proxied class interface
 */
public interface Subject {
    void doSomething();
}

/**
 * Real agent class
 */
public class RealSubject implements Subject {
    public void doSomething() {
        System.out.println("RealSubject doSomething");
    }
}

/**
 * proxy class
 */
public class SubjectProxy implements Subject {
    private Subject subject = null;

    public SubjectProxy() {
        this.subject = new RealSubject();
    }

    public void doSomething() {
        System.out.println("doSomething Was represented");
        this.subject.doSomething();
    }
}

/**
 * Test class
 */
public class SubjectProxyTest {
    @Test
    public void test() {
        SubjectProxy subjectProxy = new SubjectProxy();
        subjectProxy.doSomething();
    }
}

advantage:

  1. Clear responsibilities. The real class pays attention to its own logic and hands over dirty work to the agent class
  2. High scalability. For changes to real classes, there is no need to move the proxy class

Applicable scenarios:

  1. We need to extend and enhance the real class without intrusion
  2. Expect to control access to real classes

Agent mode extension:

  1. General agent

That's the example where the caller knows that a proxy exists (like a forward proxy)

  1. Transparent agent

The caller does not know the existence of the proxy (similar to reverse proxy)

/**
 * Real agent class
 */
public class RealSubject implements Subject {
    public RealSubject(Subject subject) throws ClassNotFoundException {
        // Pay attention here
        if (subject == null) {
            throw new ClassNotFoundException();
        }
    }

    public void doSomething() {
        System.out.println("RealSubject doSomething");
    }
}

/**
 * proxy class
 */
public class SubjectProxy implements Subject {
    private Subject subject = null;

    public SubjectProxy() {
        // Pay attention here
        try {
            this.subject = new RealSubject(this);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public void doSomething() {
        System.out.println("doSomething Was represented");
        this.subject.doSomething();
    }
}

/***
 * Test class
 */
public class ProxyTest {
    @Test
    public void test() {
        SubjectProxy subjectProxy = new SubjectProxy();
        subjectProxy.doSomething();
    }

    @Test
    public void test2() throws ClassNotFoundException {
        // It's going to be wrong
//        RealSubject realSubject = new RealSubject();
//        realSubject.doSomething();
    }
}
  1. Mandatory agency

Proxy role must be found through real role, otherwise you cannot access

It doesn't mean much

  1. Dynamic agent

Dynamic proxy is to specify which object to proxy at run time without caring about who to proxy at implementation time

/**
 * Notification interface
 */
public interface Advice {
    public void exec();
}

/**
 * Notification class
 */
public class BeforeAdvice implements Advice {
    public void exec() {
        System.out.println("Advance notice executed");
    }
}

/**
 * handler of dynamic agent
 */
public class MyInvocationHandler implements InvocationHandler {
    // Represented object
    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // Find JoinPoint connection
        if (true) {
            new BeforeAdvice().exec();
        }
        // Execute the delegated method
        return method.invoke(this.target, args);
    }
}

/**
 * Dynamic agent class
 */
public class SubjectDynamicProxy {
    public static Subject newInstance(Subject subject) {
        ClassLoader classLoader = subject.getClass().getClassLoader();
        Class<?>[] interfaces = subject.getClass().getInterfaces();
        InvocationHandler invocationHandler = new MyInvocationHandler(subject);
        return (Subject) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
    }
}

/**
 * Test class
 */
public class ProxyTest {
    @Test
    public void test() {
        Subject subject = new RealSubject();
        Subject proxySubject = SubjectDynamicProxy.newInstance(subject);
        proxySubject.doSomething();
    }
}

The dynamic proxy using jdk requires that the class to be proxy must implement an interface, which is not required by CGLIB dynamic proxy.

spring's aop is realized by using dynamic agent technology

07 prototype mode

definition:

Use prototype instances to specify the kind of objects to create, and create new objects by copying these prototypes. That is, create objects based on objects, not classes

Example:

public class Prototype implements Cloneable {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    protected Prototype clone() throws CloneNotSupportedException {
        // The point is this thing
        return (Prototype) super.clone();
    }
}

advantage:

  1. Excellent performance: Based on binary stream copy, higher performance than new
  2. Escape the constraints of constructors

Applicable scenarios:

  1. Asset optimization scenarios. Class initialization consumes a lot of resources
  2. Scene with multiple modifiers for one object

Note:

  1. Deep copy and shallow copy
  2. clone and final cannot coexist
  3. Construction method will not execute on copy

08 intermediary mode

definition:

Using a mediation object to encapsulate a series of object interactions, the mediator makes the objects interact without display, so that they are loosely coupled and can independently change their interactions

Example:

/**
 * Colleague abstract class
 */
public abstract class Colleagure {
    protected Mediator mediator;

    public Colleagure(Mediator mediator) {
        this.mediator = mediator;
    }
}

/**
 * Specific colleagues
 */
public class ConcreteColleagure1 extends Colleagure {
    public ConcreteColleagure1(Mediator mediator) {
        super(mediator);
    }

    public void selfMethod1() {
        System.out.println("ConcreteColleagure1 Own business logic");
    }

    public void depMethod1() {
        // Rely on the logic of other objects and deal with the intermediary
        super.mediator.doSomething1();
    }
}

/**
 * Specific colleagues
 */
public class ConcreteColleagure2 extends Colleagure {
    public ConcreteColleagure2(Mediator mediator) {
        super(mediator);
    }

    public void selfMethod1() {
        System.out.println("ConcreteColleagure2 Own business logic");
    }

    public void depMethod1() {
        // Rely on the logic of other objects and deal with the intermediary
        super.mediator.doSomething2();
    }
}

/**
 * Abstract mediator
 */
public abstract class Mediator {
    protected ConcreteColleagure1 concreteColleagure1;
    protected ConcreteColleagure2 concreteColleagure2;

    public abstract void doSomething1();
    public abstract void doSomething2();

    public ConcreteColleagure1 getConcreteColleagure1() {
        return concreteColleagure1;
    }

    public void setConcreteColleagure1(ConcreteColleagure1 concreteColleagure1) {
        this.concreteColleagure1 = concreteColleagure1;
    }

    public ConcreteColleagure2 getConcreteColleagure2() {
        return concreteColleagure2;
    }

    public void setConcreteColleagure2(ConcreteColleagure2 concreteColleagure2) {
        this.concreteColleagure2 = concreteColleagure2;
    }
}

/**
 * Specific intermediary
 */
public class ConcreteMediator extends Mediator {
    public void doSomething1() {
        super.concreteColleagure1.selfMethod1();
        super.concreteColleagure2.selfMethod1();
    }

    public void doSomething2() {
        super.concreteColleagure2.selfMethod1();
        super.concreteColleagure1.selfMethod1();
    }
}

/**
 * Test class
 */
public class MediatorTest {
    @Test
    public void test() {
        Mediator mediator = new ConcreteMediator();
        ConcreteColleagure2 concreteColleagure2 = new ConcreteColleagure2(mediator);
        ConcreteColleagure1 concreteColleagure1 = new ConcreteColleagure1(mediator);
        mediator.setConcreteColleagure1(concreteColleagure1);
        mediator.setConcreteColleagure2(concreteColleagure2);
        concreteColleagure1.depMethod1();
        concreteColleagure2.depMethod1();
    }
}

advantage:

The advantage of mediator pattern is to reduce the dependency between classes, change the original one-to-many dependency into one-to-one dependency, and realize decoupling

Applicable scenarios:

  1. Airport Control Center
  2. MVC framework
  3. Media gateway
  4. Intermediary service

09 command mode

definition:

Encapsulate a request into an object, so that you can use different requests to parameterize the client, queue the request or record the request log, which can provide command revocation and recovery functions

Example:

/**
 * Abstract receiver
 */
public abstract class Recevier {
    public abstract void doSomething();
}

/**
 * Specific recipients
 */
public class ConcreteRecevier1 extends Recevier {
    public void doSomething() {
        System.out.println("ConcreteRecevier1 Business logic");
    }
}

/**
 * Specific recipients
 */
public class ConcreteRecevier2 extends Recevier {
    public void doSomething() {
        System.out.println("ConcreteRecevier2 Business logic");
    }
}

/**
 * Abstract command class
 */
public abstract class Command {
    public abstract void execute();
}

/**
 * Specific command class
 */
public class ConcreteCommand1 extends Command {

    Recevier recevier;

    public ConcreteCommand1(Recevier recevier) {
        this.recevier = recevier;
    }

    public ConcreteCommand1() {
        this(new ConcreteRecevier1());
    }

    public void execute() {
        recevier.doSomething();
    }
}

/**
 * Specific command class
 */
public class ConcreteCommand2 extends Command {

    Recevier recevier;

    public ConcreteCommand2(Recevier recevier) {
        this.recevier = recevier;
    }

    public ConcreteCommand2() {
        this(new ConcreteRecevier2());
    }

    public void execute() {
        recevier.doSomething();
    }
}

/**
 * caller 
 */
public class Invoker {
    private Command command;

    public void setCommand(Command command) {
        this.command = command;
    }

    public void action() {
        this.command.execute();
    }
}

/**
 * Test class
 */
public class CommandTest {
    @Test
    public void test() {
        Command command = new ConcreteCommand1();
        Command command1 = new ConcreteCommand2();
        Invoker invoker = new Invoker();
        invoker.setCommand(command);
        invoker.action();
        invoker.setCommand(command1);
        invoker.action();
    }
}

advantage:

  1. Decoupling: no dependency between caller and receiver
  2. Commands are easy to extend

Disadvantages:

  • The increase of commands will increase the number of classes

10 responsibility chain mode

definition:

The coupling relationship between the sender and the receiver of the request is avoided. Link these objects into a chain and pass the request along the chain until it is processed by an object

Example:

/**
 * Processing level
 */
public enum Level {
    LEVEL1(1, "Level 1"),
    LEVEL2(2, "Level 2"),
    LEVEL3(3, "Level 3");
    private int code;
    private String msg;

    private Level(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

/**
 * Request encapsulation
 */
public class Request {
    Level level;

    public Request(Level level) {
        this.level = level;
    }

    public Level getRequestLevel() {
        return this.level;
    }
}

/**
 * Response encapsulation
 */
public class Response {
    private int code;
    private String msg;

    public Response(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

/**
 * Processing request abstraction
 */
public abstract class Handler {
    private Handler nextHandler;

    public void setNextHandler(Handler nextHandler) {
        this.nextHandler = nextHandler;
    }

    public final Response handleMessage(Request request) {
        Response response = null;
        if (this.getHandleLevel().getCode() == request.getRequestLevel().getCode()) {
            // Handle by yourself
            response = this.doRequest(request);
        } else {
            if (this.nextHandler != null) {
                // Leave it to the next person
                response = this.nextHandler.handleMessage(request);
            } else {
                response = new Response(-1, "Unattended");
            }
        }
        return response;
    }

    // Gets the level that the current processor can handle
    protected abstract Level getHandleLevel();

    // Process request
    protected abstract Response doRequest(Request request);
}

/**
 * Class that specifically processes the request
 */
public class Level1Handler extends Handler {
    protected Level getHandleLevel() {
        return Level.LEVEL1;
    }

    protected Response doRequest(Request request) {
        return new Response(1, "The first level is at Level1Handler Handled");
    }
}

public class Level2Handler extends Handler {
    protected Level getHandleLevel() {
        return Level.LEVEL2;
    }

    protected Response doRequest(Request request) {
        return new Response(1, "Level 2 at Level2Handler Handled");
    }
}

public class Level3Handler extends Handler {
    protected Level getHandleLevel() {
        return Level.LEVEL3;
    }

    protected Response doRequest(Request request) {
        return new Response(1, "Level 2 at Level3Handler Handled");
    }
}

/**
 * Test class
 */
public class RequestChainTest {
    @Test
    public void test() {
        Level1Handler level1Handler = new Level1Handler();
        Level2Handler level2Handler = new Level2Handler();
        Level3Handler level3Handler = new Level3Handler();
        level1Handler.setNextHandler(level2Handler);
        level2Handler.setNextHandler(level3Handler);

        Response response = level1Handler.handleMessage(new Request(Level.LEVEL2));
        System.out.println(response.getCode());
        System.out.println(response.getMsg());
    }
}

advantage:

  1. Separate request and processing: the requester may not know who processes the request, and the processor may not know the whole picture of the request

11 decorator mode

definition:

Add some additional responsibilities to an object dynamically. In terms of adding functions, decoration mode is more flexible than subclass generation

Example:

/**
 * The abstraction of the decorated class
 */
public abstract class Component {
    public abstract void doSomething();
}

/**
 * Specific decorated person
 */
public class ConcreteComponent extends Component {
    public void doSomething() {
        System.out.println("Method of decorated class");
    }
}


/**
 * Decoration
 */
public class Decorator extends Component {
    private Component component;

    public Decorator(Component component) {
        this.component = component;
    }

    @Override
    public void doSomething() {
        this.component.doSomething();
    }

    public void newMethod() {
        System.out.println("Add a new method");
    }
}

advantage:

  1. Decoration class and decorated class can develop independently without coupling each other
  2. Decoration pattern is an alternative to inheritance
  3. Decoration pattern can dynamically extend the function of an implementation class

Disadvantages:

The multi-layer decoration is more complicated

Applicable scenarios:

  1. Need to extend a class

12 strategic model

definition:

Define a set of algorithms, encapsulate each algorithm and make them interchangeable

Example:

/**
 * Policy interface
 */
public interface Strategy {
    void doSomething();
}

/**
 * Specific strategies
 */
public class Strategy1 implements Strategy {
    public void doSomething() {
        System.out.println("First strategy");
    }
}

/**
 * Specific strategies
 */
public class Strategy2 implements Strategy {
    public void doSomething() {
        System.out.println("First strategy");
    }
}

/**
 * Encapsulation strategy
 */
public class Context {
    private Strategy strategy;

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public void execute() {
        this.strategy.doSomething();
    }
}

/**
 * Test class
 */
public class StrategyTest {
    @Test
    public void test() {
        Context context = new Context();
        context.setStrategy(new Strategy1());
        context.execute();
    }
}

advantage:

  1. Algorithm can switch freely
  2. Avoid using multiple conditional judgments
  3. It has good scalability and can easily add a strategy

Disadvantages:

  1. Increase in the number of policy categories
  2. All policy classes need to be exposed

Applicable scenarios:

  1. The algorithm can switch scenes freely
  2. Scenarios that need to mask algorithm rules

13 adapter mode

definition:

The interface of one class is transformed into another interface expected by the client, so that two classes that could not work together originally due to interface mismatch can work together

Example:

/**
 * Target role
 */
public interface Target {
    void request();
}

/**
 * Source role
 */
public class Adaptee {
    public void doAnyThing() {
        System.out.println("doAnyThing");
    }
}

/**
 * Adapter role
 */
public class Atapter extends Adaptee implements Target {
    public void request() {
        super.doAnyThing();
    }
}

advantage:

  1. The adapter pattern allows two unrelated classes to run together
  2. Added class transparency
  3. Improve the reusability of classes
  4. Very flexible

Applicable scenarios:

Consider changing a class that is already in production

14 iterator mode

definition:

It provides a way to access elements of a container object without exposing the internal details of the object

Example:

Many collection classes implement the iteratable interface, which is the iterator pattern

advantage:

Easy to traverse and hide internal details

15 combined mode

definition:

Combine objects into a tree structure to represent the "part whole" hierarchy, so that users can use single and combined objects consistently

Example:

/**
 * Abstract component
 */
public abstract class Component {
    public void doSomething() {
        System.out.println("Some business logic");
    }
}

/**
 * branch
 */
public class Composite extends Component {
    /**
     * Child node container
     */
    private ArrayList<Component> componentArrayList = new ArrayList<Component>();

    // Add node
    public void add(Component component) {
        this.componentArrayList.add(component);
    }

    // Abridgement point
    public void remove(Component component) {
        this.componentArrayList.remove(component);
    }

    // Get child nodes
    public ArrayList<Component> getChildren() {
        return this.componentArrayList;
    }
}

public class Leaf extends Component {
    @Override
    public void doSomething() {
        System.out.println("leaf");
        super.doSomething();
    }
}

/**
 * Test class
 */
public class CompositeTest {
    @Test
    public void test() {
        Composite root = new Composite();
        root.doSomething();

        Composite branch = new Composite();
        Leaf leaf = new Leaf();
        root.add(branch);
        branch.add(leaf);
        display(root);
    }

    private void display(Composite root) {
        for (Component component1 : root.getChildren()) {
            if (component1 instanceof Leaf) {
                component1.doSomething();
            } else {
                display((Composite) component1);
            }
        }
    }
}

advantage:

  1. High level module is easy to call
  2. Free increase of nodes

Applicable scenarios:

  • Scenarios for maintaining and presenting part whole relationships

  • A scenario in which some modules or functions can be separated from the whole

16 observer mode

definition:

Define a one to many dependency between objects, so that every time an object changes state, all objects that depend on it will be notified and updated automatically

Example:

/**
 * Observer interface
 */
public interface Observer {
    // to update
    void update(Object object);
}

/**
 * Specific observer
 */
public class ConcreteObserver implements Observer {
    public void update(Object object) {
        System.out.println("Notice received"+object.toString());
    }
}

/**
 * Observed abstract class
 */
public abstract class Subject {

    private ArrayList<Observer> observers = new ArrayList<Observer>();

    public void addObserver(Observer observer) {
        this.observers.add(observer);
    }

    public void removeObserver(Observer observer) {
        this.observers.remove(observer);
    }

    public void notifyObservers(Object object) {
        for (Observer observer : this.observers) {
            observer.update(object);
        }
    }
}

/**
 * Specific observed
 */
public class ConcreteSubject extends Subject {
    public void doSomething() {
        super.notifyObservers("doSomething");
    }
}

/**
 * Test class
 */
public class ObserverTest {
    @Test
    public void test() throws InterruptedException {
        Observer observer = new ConcreteObserver();
        ConcreteSubject subject = new ConcreteSubject();
        subject.addObserver(observer);
        Thread.sleep(1000);
        subject.doSomething();
    }
}

advantage:

  1. There is abstract coupling between the observer and the observed
  2. Set up a trigger mechanism

Disadvantages:

  1. Development efficiency
  2. Operational efficiency issues

Applicable scenarios:

  1. Cross system message exchange scenario
  2. Associating behavior scenarios

17 facade mode

definition:

It is required that the external communication of a subsystem and its internal communication must be carried out through a unified object. Facade mode provides a high-level interface, making the subsystem easier to use

Example:

/**
 * Subsystem
 */
public class SubSystemA {
    public void doSomethingA() {
        System.out.println("doSomethingA");
    }
}

/**
 * Subsystem
 */
public class SubSystemB {
    public void doSomethingB() {
        System.out.println("doSomethingB");
    }
}


/**
 * Subsystem
 */
public class SubSystemC {
    public void doSomethingC() {
        System.out.println("doSomethingC");
    }
}

/**
 * Facade
 */
public class Facade {
    private SubSystemA subSystemA = new SubSystemA();
    private SubSystemB subSystemB = new SubSystemB();
    private SubSystemC subSystemC = new SubSystemC();

    public void methodA() {
        subSystemA.doSomethingA();
    }

    public void methodB() {
        subSystemB.doSomethingB();
    }

    public void methodC() {
        subSystemC.doSomethingC();
    }
}

advantage:

  1. Reduce system interdependency
  2. Increased flexibility
  3. Improved security

Disadvantages:

  1. Does not conform to the opening and closing principle

Applicable scenarios:

  1. Provide an interface for external access to a complex module or subsystem
  2. Subsystem is relatively independent
  3. Prevent the spread of risks brought by low-level personnel

18 memo mode

definition:

Without destroying encapsulation, capture the internal state of an object and save the state outside the object. In this way, the object can be restored to the original saved state later

Example:

/**
 * Initiator role
 */
public class Originator {
    private String state;   // Internal state

    public Originator() {
    }

    public Originator(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

    // Create memo
    public Memento createMemento() {
        return new Memento(this.state);
    }

    // Resume memo
    public void restoreMemento(Memento memento) {
        this.setState(memento.getState());
    }

    public void changeState(String state) {
        this.setState(state);
    }
}

/**
 * memorandum
 */
public class Memento {
    private String state; // Initiator internal status

    public Memento(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }
}

/**
 * Memo administrator role
 */
public class Caretaker {
    private Memento memento;

    public Memento getMemento() {
        return memento;
    }

    public void setMemento(Memento memento) {
        this.memento = memento;
    }
}

/**
 * Test class
 */
public class MementoTest {
    @Test
    public void test() {
        Originator originator = new Originator("1");
        System.out.println(originator.getState());
        Caretaker caretaker = new Caretaker();
        caretaker.setMemento(originator.createMemento());
        originator.changeState("3");
        System.out.println(originator.getState());
        originator.restoreMemento(caretaker.getMemento());
        System.out.println(originator.getState());
    }
}

Applicable scenarios:

  1. Relevant state scenarios that need to save and recover data

19 visitor mode

definition:

Encapsulates operations that act on elements of a data structure. It can define new operations that act on these elements without changing the data structure

Example:

/**
 * Visitor interface
 */
public interface IVisitor {
    void visit(ConcreteElement1 concreteElement1);
    void visit(ConcreteElement2 concreteElement2);
}

/**
 * Specific visitors
 */
public class Visitor implements IVisitor {
    public void visit(ConcreteElement1 concreteElement1) {
        concreteElement1.doSomething();
    }

    public void visit(ConcreteElement2 concreteElement2) {
        concreteElement2.doSomething();
    }
}

/**
 * Abstract elements
 */
public abstract class Element {
    public abstract void doSomething(); // Business logic

    public abstract void accept(IVisitor visitor); // Who is allowed access
}

/**
 * Specific elements
 */
public class ConcreteElement1 extends Element {
    public void doSomething() {
        System.out.println("element1 Business logic");
    }

    public void accept(IVisitor visitor) {
        visitor.visit(this);
    }
}

/**
 * Specific elements
 */
public class ConcreteElement2 extends Element {
    public void doSomething() {
        System.out.println("element2 Business logic");
    }

    public void accept(IVisitor visitor) {
        visitor.visit(this);
    }
}

/**
 * Structural objects
 */
public class ObjectStructure {
    public static Element createElement() {
        Random random = new Random();
        if(random.nextInt(100) < 50) {
            return new ConcreteElement1();
        } else {
            return new ConcreteElement2();
        }
    }
}

/**
 * Test class
 */
public class VisitorTest {
    @Test
    public void test() {
        for (int i = 0; i < 10; i++) {
            Element e = ObjectStructure.createElement();
            e.accept(new Visitor());
        }
    }
}

advantage:

  1. Conform to the principle of single responsibility
  2. High scalability
  3. High flexibility

Disadvantages:

  1. It is difficult to change specific elements
  2. Details of specific elements to visitors
  3. Contrary to the principle of Dependence Inversion

Applicable scenarios:

Business rules require traversing multiple different objects

20 state mode

definition:

When an object's internal state changes, it is allowed to change its behavior. The object looks like it has changed its class

Example:

/**
 * State abstract class
 */
public abstract class State {
    protected Context context;

    public void setContext(Context context) {
        this.context = context;
    }

    // Behavior 1
    public abstract void handle1();
    // Behavior 2
    public abstract void hanle2();
}

/**
 * Specific status
 */
public class State1 extends State {
    public void handle1() {

    }

    public void hanle2() {
        super.context.setCurrentState(Context.STATE2);
        System.out.println("Can be executed in state 1 handle2");
    }
}

/**
 * Specific status
 */
public class State2 extends State {
    public void handle1() {
        super.context.setCurrentState(Context.STATE1);
        System.out.println("Can be executed in state 2 handle1");
    }

    public void hanle2() {

    }
}

/**
 * Environmental role
 */
public class Context {
    public static final State STATE1 = new State1();
    public static final State STATE2 = new State2();

    private State currentState;

    public State getCurrentState() {
        return currentState;
    }

    public void setCurrentState(State state) {
        this.currentState = state;
        this.currentState.setContext(this);
    }

    public void handle1() {
        this.currentState.handle1();
    }

    public void handle2() {
        this.currentState.hanle2();
    }
}

/**
 * Test class
 */
public class StateTest {
    @Test
    public void test() {
        Context context = new Context();
        context.setCurrentState(new State1());
        context.handle1();
        context.handle2();
        context.handle2();
        context.handle1();
    }
}

advantage:

  1. Clear structure
  2. Good encapsulation
  3. Follow design principles

Disadvantages:

  1. More classes

Applicable scenarios:

  1. A scene in which behavior changes as states change
  2. Substitute of condition and branch judgment statement

21 enjoy yuan mode

definition:

Using shared objects can effectively support a large number of fine-grained objects

Example:

/**
 * Abstract sharing role
 */
public abstract class FlyWeight {
    private String intrinsic;
    protected final String extrinsic;

    public FlyWeight(String extrinsic) {
        this.extrinsic = extrinsic;
    }

    public abstract void operate(); // Business logic

    public String getIntrinsic() {
        return intrinsic;
    }

    public void setIntrinsic(String intrinsic) {
        this.intrinsic = intrinsic;
    }
}

/**
 * Specific role
 */
public class ConcreteFlyWeight extends FlyWeight {
    public ConcreteFlyWeight(String extrinsic) {
        super(extrinsic);
    }

    public void operate() {
        System.out.println("Business logic");
    }
}

/**
 * Xiangyuan object factory
 */
public class FlyWeightFactory {
    private static HashMap<String, FlyWeight> flyWeightHashMap = new HashMap<String, FlyWeight>();

    public static FlyWeight getFlyWeight(String extrinsic) {
        FlyWeight flyWeight = null;
        if (flyWeightHashMap.containsKey(extrinsic)) {
            flyWeight = flyWeightHashMap.get(extrinsic);
        } else {
            flyWeight = new ConcreteFlyWeight(extrinsic);
            flyWeightHashMap.put(extrinsic, flyWeight);
        }
        return flyWeight;
    }
}

advantage:

It can greatly reduce the objects created by the application, reduce the memory consumption of the program, and enhance the performance of the program

Disadvantages:

To improve the complexity of the system, it is necessary to separate the external state and the internal state

Applicable scenarios:

  • There are many similar objects in the system
  • Scenarios requiring buffer pools

22 bridge mode

definition:

Decouple abstraction and implementation so that they can change independently

Example:

/**
 * Realized role
 */
public interface Implementor {
    void doSomething();
    void doAnyThing();
}

/**
 * Specific roles
 */
public class ConcreteImplementor implements Implementor{

    public void doSomething() {
        System.out.println("doSomething");
    }

    public void doAnyThing() {
        System.out.println("doAnything");
    }
}

/**
 * Abstract role
 */
public abstract class Abstraction {
    private Implementor implementor;

    public Abstraction(Implementor implementor) {
        this.implementor = implementor;
    }

    public void request() {
        this.implementor.doSomething();
    }

    public Implementor getImplementor() {
        return implementor;
    }
}

/**
 * Concrete abstract role
 */
public class ConcreteAbstraction extends Abstraction {
    public ConcreteAbstraction(Implementor implementor) {
        super(implementor);
    }

    @Override
    public void request() {
        super.request();
        super.getImplementor().doAnyThing();
    }
}

advantage:

  1. Separation of abstraction and Implementation
  2. Excellent expansion ability
  3. Make details transparent to customers

Applicable scenarios:

  1. Do not want or apply inherited scenarios
  2. Unstable scenario of interface or abstract class
  3. Scenarios with high reusability requirements

Tags: Java Javascript JDK Spring

Posted on Mon, 25 May 2020 08:52:40 -0700 by depsipher