"Design Pattern Zen" Reading Notes-22 Design Patterns

Posted May 25, 202022 min read

Cover Picture-Zen.jpg

Recently reading Qin Xiaobo's Zen of Design Mode. This article is a watery and long note with a length of 2,000 lines. It records 22 of the 23 design patterns in the book. It basically defines the definitions, advantages, disadvantages, and applicable scenarios of each design pattern in the book. , The demo was copied. It is recommended to read the original book. This is written by an author with a lot of knowledge and an interesting soul. The explanation of each design pattern in the original book has a very interesting example. With these examples, you can deepen your understanding and understanding. memory.
The design patterns feel the same. I have read a book "JavaScript Design Patterns and Development Practices" before, but they are all about the same thing, but there are differences in implementation due to language features. An interesting soul, I still remember the story of "Xiaoming chasing the crush" in the agent mode, and I also recommend reading it.

01 Singleton Mode

definition:

Make sure there is only one instance of a certain class, and instantiate it yourself and provide this instance to the entire system

Examples:

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

    //privatized construction
    private Singleton() {

    }

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

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

    }
}

advantage:

  1. The singleton mode only generates one instance, so the system performance overhead is reduced
  2. Singleton mode can avoid multiple occupation of resources
  3. In memory, avoid occupying the same resource

Disadvantages:

  1. Generally no interface, difficult to expand
  2. Not good for testing
  3. Violation of the principle of single responsibility

Applicable scene:

  1. Require a unique serial number environment
  2. Too many resources are needed to create an object
  3. Need to define a large number of static constants and static methods(such as tools) environment

02 Factory 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

Examples:

/**
* Abstract products
* /
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 createProduct(Class clazz);
}

/**
* Specific factory
* /
public class ProductFactory1 extends Factory {
public T createProduct(Class 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, clear code structure
  2. The extensibility of the factory method pattern is excellent, and the product category can be easily added
  3. Shield product category. Only need to care about the interface of the product, no need to care about the implementation
  4. The factory method pattern is a typical decoupling framework

scenes to be used:

  1. The factory method pattern is an alternative to new, but you need to consider whether you need to use a factory to increase code complexity
  2. When you need a flexible and extensible framework, you can consider using the factory method pattern
  3. The factory method pattern can be used in heterogeneous projects
  4. Can be used in the framework of test-driven development

Factory mode expansion:

  1. Use a static factory

    /**

    • Static factory

    • /
      public class ProductFactory2 {
      private ProductFactory2() {
      }

      static Product createProduct(Class 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;

      }
      }

  2. Multi-factory model

  3. Implement the singleton pattern

  4. Delayed initialization

03 Abstract Factory Pattern

definition:

Provides an interface for creating a group of related or interdependent objects, without specifying their specific classes

Examples:

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 is
  2. The constraints within the product family are private. The specific product category was created in private

Disadvantages:

  1. It is difficult to add products, and you need to change the factory without adding a product

Applicable scene:

An object family(or a group of objects without any relationship) has the same constraints, you can use the abstract factory pattern. Is that a bunch of classes have the same parent class

04 template method pattern

definition:

Define the framework of the algorithm in an operation, and delay some steps into subclasses. Allows subclasses to redefine some specific steps of an algorithm without changing the structure of an algorithm

Examples:

/**
* 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. Encapsulate the unchanged part and expand the variable part
  2. Extract the common part code for easy maintenance
  3. The 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 reading the code, which will make it difficult for novices to adapt.

Applicable scene:

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

Template method pattern expansion:

You can adjust the execution flow of the template method by defining the form of the switch

05 Builder Mode

definition:

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

Examples:

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 mode allows the client not to know the details of the internal composition of the product
  2. The builder is independent and easy to expand
  3. Because the specific builder is independent, it can gradually refine the construction process without any impact on other modules

Disadvantages:

  1. If the difference between products is large, it is not suitable to use the builder model, so its scope of use is subject to certain restrictions
  2. If the internal changes of the product are complex, it may lead to the definition of many specific builder classes to achieve this change, resulting in a very large system

Applicable scene:

  1. The same method, different execution order, and different event results can use the builder mode
  2. The product category is very complex, or the different calling order in the product category produces different performance
  3. Some other objects in the system will be used in the process of object creation. These objects are not easy to obtain during the creation of product objects

06 Agent Mode

definition:

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

Examples:

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

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

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

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

    public void doSomething() {
        System.out.println("doSomething is proxied");
        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 the dirty work is exhausted to the agent class
  2. High scalability. Changes to the real class, no need to move the agent class

Applicable scene:

  1. Need to make some extensions and enhancements to the real class without intrusion
  2. Expect to control access to real classes

Proxy mode extension:

  1. General Agent

This is the example, the caller knows the existence of a proxy(similar to a forward proxy)

  1. Transparent proxy

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

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

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

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

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

    public void doSomething() {
        System.out.println("doSomething is proxied");
        this.subject.doSomething();
    }
}

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

    @Test
    public void test2() throws ClassNotFoundException {
        //will report an error
//RealSubject realSubject = new RealSubject();
//realSubject.doSomething();
    }
}
  1. Mandatory Agency

The proxy role must be found through the real role, otherwise you cannot access

I do n’t think it means much

Dynamic agent

Dynamic proxy does not need to care who the proxy is in the implementation phase, but specifies which object of the proxy in the run phase

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

/**
* Notification
* /
public class BeforeAdvice implements Advice {
public void exec() {
System.out.println("The pre-notification is executed");
}
}

/**
* Dynamic proxy handler
* /
public class MyInvocationHandler implements InvocationHandler {
//proxy object
private Object target;

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

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

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

/**
* Dynamic proxy 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 use of jdk dynamic proxy requires that the proxy class must implement an interface, CGLIB dynamic proxy does not have this requirement.

Spring's aop is implemented using dynamic proxy technology

07 Prototype Mode

definition:

Use prototype instances to specify the types of objects created, and create new objects by copying these prototypes. Create objects based on objects, not classes

Examples:

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 key 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 the constructor

Applicable scene:

  1. Resource optimization scenarios. Class initialization requires a lot of resources
  2. An object with multiple modifiers

be careful:

  1. Deep copy and shallow copy
  2. Clone and final cannot coexist
  3. The constructor is not executed when copying

08 intermediary model

definition:

Encapsulate a series of object interactions with an intermediary object. The intermediary allows each object to interact without explicit display, thereby loosening its coupling and allowing it to independently change the interaction

Examples:

/**
* 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 to 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 to deal with the intermediary
        super.mediator.doSomething2();
    }
}

/**
* Abstract intermediary
* /
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 the intermediary model is to reduce dependencies between classes, turn the original one-to-many dependencies into one-to-one dependencies, and realize decoupling

Applicable scene:

  1. Airport Control Center
  2. MVC framework
  3. Media Gateway
  4. Intermediary services

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, and can provide the function of command undo and recovery

Examples:

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

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

/**
* Specific recipient
* /
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 commands
* /
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 commands
* /
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:there is no dependency between the caller and the receiver
  2. Commands are easy to extend

Disadvantages:

  • The increase in orders will increase the number of classes

10 Chain of Responsibility Model

definition:

Multiple objects have the opportunity to process the request, thereby avoiding the coupling relationship between the sender and receiver of the request. Connect these objects into a chain and pass the request along the chain until an object processes it

Examples:

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

/**
* Process 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 it yourself
            response = this.doRequest(request);
        } else {
            if(this.nextHandler! = null) {
                //Hand over to the next person
                response = this.nextHandler.handleMessage(request);
            } else {
                response = new Response(-1, "No one handles");
            }
        }
        return response;
    }

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

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

/**
* Classes that specifically handle requests
* /
public class Level1Handler extends Handler {
protected Level getHandleLevel() {
return Level.LEVEL1;
}

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

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

    protected Response doRequest(Request request) {
        return new Response(1, "The second level was processed in Level2Handler");
    }
}

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

    protected Response doRequest(Request request) {
        return new Response(1, "The second level was processed in Level3Handler");
    }
}

/**
* 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 the request from the processing:the requester does not need to know who processed it, and the processor does not need to know the full picture

11 Decorator Mode

definition:

Add some additional responsibilities to an object dynamically. In terms of increased functionality, decorative patterns are more flexible than generating subclasses

Examples:

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

/**
* Specific decorator
* /
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. Decorative and decorated classes can be developed independently without coupling each other
  2. Decoration pattern is an alternative to inheritance
  3. Decoration mode can dynamically extend the functionality of an implementation class

Disadvantages:

Multi-layer decoration is more complicated

Applicable scene:

  1. Need to extend a class

12 Strategy Mode

definition:

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

Examples:

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

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

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

/**
* Packaging 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. The algorithm can be switched freely
  2. Avoid using multiple conditions
  3. Good scalability, you can easily add a strategy

Disadvantages:

  1. Increased number of strategy classes
  2. All strategy classes need to be exposed

Applicable scene:

  1. The algorithm can switch scenes freely
  2. Scenarios where algorithm rules need to be masked

13 Adapter mode

definition:

Transform the interface of one class into another interface expected by the client, so that the two classes that could not work together because of the interface mismatch can work together

Examples:

/**
* Target character
* /
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 classes with no relationship to run together
  2. Added class transparency
  3. Improve the reuse of classes
  4. Very flexible

Applicable scene:

When you want to change a class that is already applicable in production, you can consider

14 Iterator mode

definition:

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

Examples:

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

advantage:

Easy to traverse, hide internal details

15 Combination Mode

definition:

Combine objects into a tree structure to represent a "part-whole" hierarchy, so that users have consistent use of single objects and combined objects

Examples:

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

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

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

    //delete node
    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. Simple high-level module calling
  2. Nodes increase freely

Applicable scene:

  • Maintain and display part-overall relationship scene
  • Scenes where some modules or functions can be isolated from a whole

16 Observer Mode

definition:

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

Examples:

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

/**
* Specific observer
* /
public class ConcreteObserver implements Observer {
public void update(Object object) {
System.out.println("Notification 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);
        }
    }
}

/**
* The specific subject
* /
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 an abstract coupling between the observer and the observed
  2. Establish a trigger mechanism

Disadvantages:

  1. Development efficiency
  2. Operational efficiency

Applicable scene:

  1. Cross-system message exchange scenario
  2. Related behavior scenarios

17 Facade Mode

definition:

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

Examples:

/**
* 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 interdependence
  2. Increased flexibility
  3. Increased safety

Disadvantages:

  1. Does not comply with the principle of opening and closing

Applicable scene:

  1. Provide an interface for outside access for a complex module or subsystem
  2. The subsystem is relatively independent
  3. Prevent the spread of risks caused by low-level personnel

18 memo mode

definition:

Under the premise of not damaging the encapsulation, capture the internal state of an object and save this state outside the object. In this way, the object can be restored to the original saved state later

Examples:

/**
* The role of promoter
* /
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);
    }
}

/**
* Memo
* /
public class Memento {
private String state; //Internal state of the initiator

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

    public String getState() {
        return state;
    }

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

/**
* Memo Manager 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 scene:

  1. Relevant status scenarios where data needs to be saved and restored

19 Visitor Mode

definition:

Encapsulate some operations that act on each element in a certain data structure, it can define new operations that act on these elements without changing the data structure

Examples:

/**
* 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 to visit
}

/**
* 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. Meet the principle of single responsibility
  2. High scalability
  3. High flexibility

Disadvantages:

  1. It is difficult to change specific elements
  2. Specific elements announce details to visitors
  3. Violation of the principle of dependency inversion

Applicable scene:

Business rules require traversing multiple different objects

20 State Mode

definition:

Allows an object to change its behavior when its internal state changes

Examples:

/**
* 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 handle2 in state 1");
    }
}

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

    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. There are many classes

Applicable scene:

  1. Scenes where behavior changes with state
  2. Alternatives to conditional and branch judgment statements

21 Flyweight model

definition:

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

Examples:

/**
* Abstract Flyweight 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 Flyweight role
* /
public class ConcreteFlyWeight extends FlyWeight {
public ConcreteFlyWeight(String extrinsic) {
super(extrinsic);
}

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

/**
* Flyweight 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:

可以大大减少应用程序创建的对象,降低程序内存的占用,增强程序的性能

Disadvantages:

提高了系统复杂性,需要分离出外部状态和内部状态

适用场景:

  • 系统中存在大量相似的对象
  • 需要缓冲池的场景

22桥梁模式

定义:

将抽象和实现解耦,使得两者可以独立地变化

Examples:

/**
* 实现化角色
* /
public interface Implementor {
void doSomething();
void doAnyThing();
}

/**
* 具体实现化角色
* /
public class ConcreteImplementor implements Implementor{

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

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

/**
* 抽象化角色
* /
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;
    }
}

/**
* 具体的抽象化角色
* /
public class ConcreteAbstraction extends Abstraction {
public ConcreteAbstraction(Implementor implementor) {
super(implementor);
}

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

advantage:

  1. 抽象和实现分离
  2. 优秀的扩充能力
  3. 实现细节对客户透明

适用场景:

  1. 不希望或不适用使用继承的场景
  2. 接口或抽象类不稳定的场景
  3. 重用性要求较高的场景

公众号二维码.png