Ladder: Design Patterns

Data study organized from: Station B is still Silicon Valley

I wrote a set of code to understand by myself, which has been uploaded Code cloud
Since the design pattern is a big topic with a lot of content, my plan is to learn some commonly used ones first, and then gradually make them comprehensive.

Design Patterns

Design Pattern

Concept introduction

1) Design patterns are useful experiences summed up by programmers in the face of similar software engineering design problems, and are general solutions to certain types of problems. Design patterns represent the best practices. These solutions are the result of a considerable period of trial and error by numerous software developers.

2) The nature of the design pattern improves the maintainability, versatility and scalability of the software, and reduces the complexity of the software.

type

The 23 design patterns are grouped into three types:

1) Creation mode: singleton mode, abstract factory mode, factory mode, prototype mode, builder mode;

2) Structural mode: adapter mode, bridge mode, decoration mode, combination mode, appearance mode, flyweight mode, proxy mode;

3) Behavioral patterns: template method pattern, command pattern, visitor pattern, iterator pattern, observer pattern, mediator pattern, memorandum pattern, interpreter pattern (Interpreter), state pattern, strategy pattern, responsibility chain pattern (responsibility chain pattern) chain).

1. Singleton pattern (creative type)

Definition: In the entire software system, ensure that there is only one instance of a class, and only provide one entry to obtain the instance.

Scenes

Heavyweight objects that don't require multiple instances. Such as: thread pool, database connection pool, etc.

Implementation:

1. Hungry Chinese style (static constant)

private Singleton(){}
private static Singleton singleton = new Singleton();
public Singleton getInstance(){
    return singleton;
}

Conclusion: May be a waste of memory.

2. Hungry Chinese style (static code block)

private Singleton(){}
private static Singleton singleton;
static{
    if(singleton==null){
	    singleton = new Singleton();
    }
}
public Singleton getInstance(){
    return singleton;
}

Conclusion: May be a waste of memory.

3. Lazy (single thread)

private Singleton(){}
private static Singleton singleton;
public Singleton getInstance(){
    if(singleton==null){
	    singleton = new Singleton();
    }
    return singleton;
}

Conclusion: Multithreading is not safe. Do not use.

4. Lazy (synchronized method)

private Singleton(){}
private static Singleton singleton;
public static synchronized Singleton getInstance(){
    if(singleton==null){
	    singleton = new Singleton();
    }
    return singleton;
}

Conclusion: The efficiency is too low. Not recommended.

5. Lazy man style (synchronized code block)

private Singleton(){}
private static Singleton singleton;
public static Singleton getInstance(){
    if(singleton==null){
        synchronized(Singleton.class){
	    	singleton = new Singleton();
        }
    }
    return singleton;
}

Conclusion: Multithreading is not safe. Do not use.

6. Lazy Man (DCL Double Check)

private Singleton(){}
private static volatile Singleton singleton;
public static Singleton getInstance(){
    if(singleton == null){
        synchronized(Singleton.class){
            if(singleton == null){
                singleton = new Singleton();
            }
        }
        return singleton;
    }
}

Conclusion: lazy loading, thread safety, and guaranteed efficiency. Recommended Use.

7. Static inner class

private Singleton(){}
private static class SingletonInstance{
    private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
    return SingletonInstance.INSTANCE;
}

1) The static inner class will not be executed when the outer class is loaded. When the getInstance is called, the static inner class will be loaded: lazy loading;

2) Use the mechanism of jvm class loading to ensure that only one thread executes when an instance is initialized, and static properties are only initialized when the class is loaded for the first time: thread safety;

Conclusion: lazy loading, thread-safe and efficient. Recommended Use.

8. Enumeration

enum Singleton(){
    INSTANCE;
}

The enumeration added by JDK1.5 is a singleton, and the re-creation of the instance is directly prohibited in the reflection source code. It is the way advocated by Effective Java author Josh Bloch.

Decompile the enumeration class and know that the enumeration belongs to the hungry Chinese style (static code block)

Application scenarios:

  • Objects that need to be created and destroyed frequently;

  • Objects that take a long time or consume too many resources when creating objects, but often used objects and tool classes

  • Frequently accessed objects that are databases or files (data sources, session factories, etc.)

2. Factory method pattern (creation type)

# Factory Pattern: Taking Songwriting as an Example
 The operation of writing a song is the same:
 - compose compose()
 - write lyrics lyrics()
 - arranger arrangement()
 - recording recording()
 
There are many genres of songs:
 - ballad ballad
 - R&B RB
 - rock rock
 - rap rap

Simple Factory Pattern

When the code is completed, a new song type needs to be added: jazz
 It only takes two steps and does not affect other original song types
1,create JazzSong Class that implements internal business methods
2,Add a type judgment to the factory class
/**
 * Simple Factory Pattern
 * @author: stone
 * @create: 2020-09-16 00:08
 */
public class SimpleFactory {

    ISong song;

    public ISong getSong(String songType){
        if("ballad".equalsIgnoreCase(songType)){
            song = new BalladSong();
        }else
        if("rb".equalsIgnoreCase(songType)){
            song = new RBSong();
        }else
        if("rock".equalsIgnoreCase(songType)){
            song = new RBSong();
        }else
        if("rap".equalsIgnoreCase(songType)){
            song = new RBSong();
        }else
        if("jazz".equalsIgnoreCase(songType)){
            song = new JazzSong();
        }else{
            song = null;
        }
        return song;
    }
}

Factory Method Pattern

Defines an abstract method for creating an object, and it is up to the subclass to decide which class to instantiate. The Factory Method pattern defers instantiation of objects to subclasses.

Personal understanding: create a sub-factory class to inherit the original simple factory class, the production methods in the original simple factory class are inherited and implemented by the sub-factory class, and the original factory class is upgraded to an abstract class. The sub-factory class represents an extension in one dimension, and the product class represents an extension in another dimension. And the method produced in the original simple factory class sinks into the concrete sub-factory class, which is called method sinking/postponing, hence the name factory method model!

ISong song;
String countryName;

//The method of creating a specific song sinks into the specific sub-factory class
public abstract ISong getSong(String songType);

public void makeSong(){
    do{
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("Please enter the type of song you want to write:");
        try {
            ISong song = getSong(bufferedReader.readLine());
            if(song==null){
                System.out.println("say good bye ~~");
                break;
            }
            song.compose();
            song.lyrics();
            song.arrangement();
            song.recording();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }while (true);
}
public class ChinaFactory extends BaseFactory {

    @Override
    public ISong getSong(String songType) {
        if("ballad".equalsIgnoreCase(songType)){
            song = new BalladSongChina();
        }else
        if("rap".equalsIgnoreCase(songType)){
            song = new RapSongChina();
        }else{
            song = null;
        }
        return song;
    }
}

3. Abstract Factory Pattern (creative)

An interface is defined for creating a cluster of related or dependent objects without specifying a specific class.

The abstract factory pattern can integrate the simple factory pattern and the factory method pattern.

The factory is abstracted into two layers, AbsFactory (abstract factory) and the factory subclass of concrete implementation. Engineers can use the corresponding factory subclass depending on the type of object being created. This turns a single simple factory class into a factory cluster, which is more conducive to code maintenance and expansion.

/**
 * abstract factory class
 * @author: stone
 * @create: 2020-09-17 22:52
 */
public interface AbsFactory {

    /**
     * The specific business of writing songs is implemented by subclasses
     * @param type
     * @return
     */
    ISong makeSong(String songType);
}

/**
 * specific factory class
 * @author: stone
 * @create: 2020-09-16 01:50
 */
public class ChinaFactory implements AbsFactory {

    @Override
    public ISong makeSong(String songType) {
        ISong song;
        if("ballad".equalsIgnoreCase(songType)){
            song = new BalladSongChina();
        }else
        if("rap".equalsIgnoreCase(songType)){
            song = new RapSongChina();
        }else{
            song = null;
        }
        return song;
    }
}

A key point is to establish a factory interface class, and subclasses inherit and implement specific content.
Use an interface factory to receive calls when used.

Factory Pattern Summary

Extract the code of the instantiated object and put it into a class for unified management and maintenance, so as to achieve decoupling from the main project dependency, thereby improving the extension and maintainability of the project. (relying on the principle of abstraction)

  • When creating an object instance, do not directly new the class, but put the new action in a method of ICBC and return it. (Some books say: variables do not directly hold specific class references);
  • Don't let classes inherit concrete classes, but inherit abstract classes or implement interfaces;
  • Do not override methods already implemented in base classes.

4. Prototype mode (creative type)

5. Builder mode (creative type)

6. Adapter mode (structural type)

7. Bridge mode (structural type)

8. Decorator pattern (structural)

Resolve class explosion

definition:

Decorated with patterns that dynamically attach new functionality to objects.

In terms of object function extension, it is more flexible than inheritance, and the decoration pattern also reflects the open-closed principle (ocp);

A bit of constructor recursion

need:

Starbucks Coffee Order Items
1)Type of coffee/Single Origin Coffee: Espresso(espresso), ShortBlack,LongBlack(American coffee), Decaf(decaf)
2)seasoning: Milk,Soy(soy milk), Chocolate
3)request to expand**new coffee types**It has good expansibility, easy modification and maintenance.
4)Use an object-oriented approach to calculate the cost of different types of coffee: the customer can order a single coffee, or a single coffee+seasoning

Use the decorator pattern to solve:

/**
 * base class drink
 * @author: stone
 * @create: 2020-09-18 00:03
 */
@Data
public abstract class Drink {

    public String name;
    public double price;

    /**
     * Calculate the price
     * @return
     */
    public abstract double cost();

}
/**
 * base class for decorators
 * @author: stone
 * @create: 2020-09-18 00:19
 */
@Data
public abstract class Decorator extends Drink {

    //Add composition relationship
    private Drink drinkObj;

    /**
     * Pass in the body that needs to be decorated
     * @param drinkObj
     */
    public Decorator(Drink drinkObj){
        this.drinkObj = drinkObj;
    }

    /**
     * Realize specific business
     * Calculation formula: price of seasoning + price of single-origin coffee
     */
    @Override
    public double cost() {
        return super.getPrice() + drinkObj.cost();
    }
}
/**
 * @author: stone
 * @create: 2020-09-18 00:36
 */
public class TestMain {

    public static void main(String args[]){
        //1. Have an Italian coffee
        Drink drink = new Espresso();
        System.out.println("1,have an Italian coffee:");
        System.out.println(drink.name + " The price is: " + drink.cost());
        System.out.println("=====================================");
        //2, add a milk
        drink = new PureMilk(drink);
        System.out.println("2,Add a serving of pure milk (double the price of milk)~):");
        System.out.println(drink.name + " The price is: " + drink.cost());
        System.out.println("=====================================");
        //3. Add a chocolate
        drink = new Chocolate(drink);
        System.out.println("3,add a chocolate:");
        System.out.println(drink.name + " The price is: " + drink.cost());
        System.out.println("=====================================");
        System.out.println("=== Business needs. To expand a decaffeinated coffee, you only need to add a main class, which can be matched with all ingredients arbitrarily ===");
        System.out.println("=====================================");
        //Business needs. Extend a Decaf
        //1. Have a decaf
        Drink drink2 = new Decaf();
        System.out.println("1,have a decaf:");
        System.out.println(drink2.name + " The price is: " + drink2.cost());
        System.out.println("=====================================");

    }

}

The code has been uploaded to the code cloud, so it will not be posted

console

1,have an Italian coffee:
Italian Coffee Price is: 15.5
=====================================
2,Add a serving of pure milk (double the price of milk)~):
Pure Milk Price is: 25.5
=====================================
3,add a chocolate:
Chocolate Price is: 28.5
=====================================
=== Business needs. To expand a decaffeinated coffee, you only need to add a main class, which can be matched with all ingredients arbitrarily ===
=====================================
1,have a decaf:
Decaf Coffee Price is: 10.88
=====================================

Process finished with exit code 0

summary

The key lies in the abstraction of the base class, and then the "combination" of the decoration class and the theme: passing the decorator into the decoration class.

//decorator
public abstract class Decorator extends Drink {
    //Add composition relationship
    private Drink drinkObj;
}

Solve the problem of class explosion caused by the full combination of seasonings and single products.

9. Combination mode (structural type)

10. Appearance mode (structural type)

11. Flyweight mode (structural type)

12. Proxy mode (structural type)

Proxy

basic introduction

  1. Proxy pattern: Provides a stand-in for an object to control access to this object. That is, the target object is accessed through the proxy object. In this way, additional functional operations can be enhanced based on the realization of the target object. (i.e. extend the functionality of the target object)
  2. The proxied object can be a remote object, an object that is expensive to create, or an object that requires security controls.
  3. There are three forms of proxy mode: static proxy, dynamic proxy, Cglib proxy
    • static proxy
    • Dynamic proxy: also called JDK proxy, interface proxy
    • Cglib proxy: Objects can be dynamically created in memory without implementing interfaces, and are also dynamic proxies.

static proxy

basic introduction:

When a static proxy is used, an interface or a parent class needs to be defined, and the proxied object (that is, the target object) implements the same interface or inherits the same parent class together with the proxy object.

/**
 * abstract class
 * Declare the method that needs to be executed
 * @author: stone
 * @create: 2020-09-18 21:42
 */
public abstract class ITeacherDao {

    public abstract void teach();

}
/**
 * @author: stone
 * @create: 2020-09-18 21:48
 */
public class TestMain {
    public static void main(String args[]){
        //target
        ITeacherDao teacherDao = new TeacherDao();

        //proxy class
        ITeacherDao teacherProxy = new TeacherProxy(teacherDao);

        //Execute the method of the proxy class to indirectly call the target class
        teacherProxy.teach();
    }
}

console

The agent teacher is here~~~
target teacher in class~~
The acting teacher is gone~~~

Process finished with exit code 0

Advantages and disadvantages of static proxy:

# advantage:
	On the premise of not modifying the function of the target object, the target function can be extended through the proxy object.
# shortcoming:
	Because the proxy class needs to implement or inherit the same interface or parent class as the target class, many proxy classes need to be written. Once the interface or parent class adds methods, both the target class and the proxy class need to be modified and added.

Dynamic proxy

JDK proxy, interface proxy

basic introduction:

  1. The proxy class does not need to implement the interface or inherit the parent class, but the target class still needs to implement the interface or inherit the parent class
  2. The generation of the proxy class is to use the JDK API to dynamically build the proxy object in memory

API for generating proxy objects in JDK:

  1. In the package: java.lang.reflect.Proxy
  2. JDK implementation of a proxy only needs to use the newProxyInstance method:
static Object newProxyInstacne(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

Code:

/**
 * Dynamic proxy mode
 * target class
 * @author: stone
 * @create: 2020-09-19 00:49
 */
public class TeacherDao implements ITeacherDao{
    @Override
    public void teach() {
        System.out.println("dynamic proxy is teaching !!!");
    }

    @Override
    public String sayHi(String word) {
        System.out.println("hi "+word);
        return "Qilixiang";
    }
}
/**
 * Dynamic proxy mode
 * agency factory
 * Does not inherit any class, can dynamically proxy any class
 * @author: stone
 * @create: 2020-09-19 00:51
 */
public class ProxyFactory {

    //Receive the target object that needs to be proxied
    Object target;

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

    /**
     * Create a proxy instance of the target class
     * @return
     */
    public Object getProxyInstance(){
        /**
         * Implemented using the Radiation API
         *
         * Source code: public static Object newProxyInstance(ClassLoader loader,
         *                                           Class<?>[] interfaces,
         *                                           InvocationHandler h)
         *    1,loader: class loader for the target class
         *    2,interfaces: target class
         *    3,Event processing object (that is, the specific method/function/event of the proxy)
         */
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //Here it is: When the method of the target object is called, the event is triggered
                        System.out.println("ProxyFactory invoke is action.   args: "+args.toString());
                        Object retuenVal = method.invoke(target, args);
                        System.out.println("ProxyFactory invoke is done.");
                        return retuenVal;
                    }
        });
    }
}
/**
 * application
 * @author: stone
 * @create: 2020-09-19 01:02
 */
public class Client {

    public static void main(String args[]){
        //target
        ITeacherDao teacherDao = new TeacherDao();
        //proxy class
        ProxyFactory proxyFactory = new ProxyFactory(teacherDao);
        //Create proxy objects using proxy classes
        ITeacherDao proxyTeacherDao = (ITeacherDao) proxyFactory.getProxyInstance();
        //Execute method using proxy object
//        proxyTeacherDao.teach();
        System.out.println("hello " +proxyTeacherDao.sayHi("stone like heart"));
    }
}

console:

ProxyFactory invoke is action.   args: [Ljava.lang.Object;@61bbe9ba
hi stone like heart
ProxyFactory invoke is done.
hello Qilixiang

Process finished with exit code 0

Cglib proxy

subclass proxy

basic introduction:

  1. Both the waiting proxy and the JDK proxy require the target object to implement an interface, but sometimes it is just a separate class and does not implement any interface. In this case, the target object subclass can be used to implement it;
  2. Cglib proxy is to build a subclass object in memory to realize the function extension of target object, so sometimes it can be said that Cglib proxy belongs to dynamic proxy;
  3. Cglib is a powerful high-performance code generation package that can extend java classes and implement java interfaces at runtime. Cglib is widely used in AOP frameworks, such as Spring AOP implementation method interception;
  4. The bottom layer of the Cglib package is to convert bytecode and generate new classes by using the bytecode processing framework ASM.

Instructions for use:

  1. Need to import jar packages: asm.jar, asm-commons.jar, asm-tree.jar, cglib-2.2.jar;
  2. Build a subclass in memory, and an error will be reported if the target object has final modification:
java.lang.IllegalArgumentException;
  1. If the method of the target object is final/static, it will not be intercepted, that is, the additional business methods of the target object will not be executed.

Code combat:

/**
 * Implement dynamic proxy using cglib framework
 *
 * @author: stone
 * @create: 2020-09-19 14:52
 */
public class ProxyFactory implements MethodInterceptor {

    //Receive proxy target object
    Object target;
    //Constructor
    public ProxyFactory(Object target){
        this.target = target;
    }

    /**
     * Methods for generating proxy objects
     * Here the proxy object is generated using the cglib method
     * Simple understanding: construct a subclass for the target class, and then create the parent class through the subclass to get the target object
     * @return
     */
    public Object getProxyInstance(){
        //1. Load the cglib tool class Enhancer
        Enhancer enhancer = new Enhancer();
        //2. Set the target class as the parent class
        enhancer.setSuperclass(target.getClass());
        //3. Set the callback method
        enhancer.setCallback(this);
        //4. Generate a proxy object for the target object
        return enhancer.create();
    }

    /**
     * intercept method
     * @param o
     * @param method Executed concrete class
     * @param objects parameter list
     * @param methodProxy
     * @return The return value of the target method may be null
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib Proxy mode interception starts~ ~");
        Object returnVal = method.invoke(target, objects);
        System.out.println("cglib Proxy mode interception ends~ ~");
        return returnVal;
    }
}

console:

cglib Proxy mode interception starts~ ~
hi stone like heart
cglib Proxy mode interception ends~ ~
hello Qilixiang

summary:

Simple understanding: construct a subclass for the target class, and then create the parent class through the subclass to get the target object

Examples of application scenarios

  • Firewall proxy

The intranet penetrates the firewall through the proxy to achieve access to the public network.

  • caching proxy

‚Äč When requesting resources such as image files, first go to the caching proxy to get them. If you cannot get the resources, then go to the public network or database to get them and store them in the caching proxy.

  • remote proxy

A local representation of a remote object through which the remote object can be invoked as a local object. The remote agent communicates with the real remote object over the network.

  • Sync agent

Mainly used in multi-threaded programming to encapsulate synchronization processing (locks) for resource objects.

13. Template method pattern (behavioral)

14. Command mode (behavioral)

15. Visitor pattern (behavioral)

16. Iterator mode (behavioral)

17. Observer mode (behavioral)

Business scene:

Weather forecast items:

  1. The weather station publishes the temperature, humidity, air pressure and other information measured every day in the form of announcements;
  2. An open API needs to be designed to facilitate other third parties to obtain measured meteorological data;
  3. Provide interface for temperature, humidity and air pressure;
  4. When the measurement data is updated, it should be able to notify the third party in real time.

Common solution implementation:

/**
 * weather information weather station
 * Enter the measured data into the system
 * @author: stone
 * @create: 2020-09-19 15:48
 */
@Data
public class WeatherData {

    private float temperatrue;//temperature
    private float humidity;//humidity
    private float pressure;//air pressure

    //third party
    CCTV cctv = new CCTV();

    /**
     * update data
     */
    public void updateData(float temperatrue,float humidity,float pressure){
        this.temperatrue = temperatrue;
        this.humidity = humidity;
        this.pressure = pressure;
        //The weather has been updated, and the data will be pushed out
        //CCTV
        cctv.showWeather(temperatrue,humidity,pressure);
    }

}

Code test

Observer pattern principle

Weather station is Subject

The third party is Observer

Third party to observe the weather station

/**
 * Use the observer pattern
 * observer
 * @author: stone
 * @create: 2020-09-19 17:14
 */
public interface Observer {

    // Receive weather information
    public void showWeather(float temperatrue,float humidity,float pressure);
}
/**
 * Use the observer pattern
 * data provider
 * @author: stone
 * @create: 2020-09-19 17:13
 */
public interface Subject {

    /**
     * update data
     */
    public void updateData(float temperatrue,float humidity,float pressure);
}
/**
 * @author: stone
 * @create: 2020-09-19 15:56
 */
public class ClientMain {

    public static void main(String args[]){
        //third party
        Observer cctv = new CCTV();
        Observer baidu = new Baidu();
        Observer tencent = new Tencent();

        WeatherData weatherData = new WeatherData();
        //Registering a third party is very easy to expand
        weatherData.registerObserver(cctv);
        weatherData.registerObserver(baidu);
        weatherData.registerObserver(tencent);

        //New weather information measured
        weatherData.updateData(29,70,1006);
    }
}

summary:

Similar to the message queue mode, the observer registers with the publishing center, and then when I have a message, I push it to the publishing center, and the publishing center sends it to all registered observers.

18. Mediator model (behavioral)

19. Memo mode (behavioral)

20. Interpreter mode (behavioral)

Interpreter mode

21. State mode (behavioral)

22. Strategy mode (behavioral)

23. Chain of Responsibility Model (Behavioral)

Chain of Responsibility Model

Tags: Design Pattern

Posted by wenxi on Mon, 16 May 2022 17:10:42 +0300