Design pattern (based on crazy monk Silicon Valley)

1. Object oriented principle

Principle of single responsibility:

That is, a class should be responsible for only one responsibility. For example, class A is responsible for two different responsibilities: Responsibility 1 and responsibility 2. When a is changed due to the change of responsibility 1 requirements, the implementation error of responsibility 2 may be caused. Therefore, the granularity of class a needs to be decomposed into A1 and A2

Interface isolation principle:

The client should not rely on interfaces it does not need, that is, the dependence of one class on another should be based on the smallest interface

Dependency reversal principle:

Interface oriented programming

Richter substitution principle:

Inheritance must ensure that the properties of the superclass are still valid in the subclass. When using inheritance, follow the principle of Richter substitution. Try not to rewrite the methods of the parent class in the subclass. Under appropriate circumstances, the problem can be solved through aggregation, combination and dependency.

Opening and closing principle:

Extension open, modification closed

Demeter's Law:

The least known principle, that is, the less a class knows about the class it depends on, the better. In other words, no matter how complex the dependent class is, try to encapsulate the logic inside the class. In addition to the public method provided, it will not disclose any information

There is a simpler definition of Dimitri's Law: only communicate with direct friends

Composite Reuse Principle:

The principle is to try to use composition / aggregation instead of inheritance

1. Single case mode

The singleton mode is divided into hungry type and lazy type. The hungry type generates an instance when the class is loaded, and the lazy type generates an instance only when it is necessary to generate an instance.

1.1 hungry Han style

package com.single;
//Hungry man style, it may waste memory
public class Hungry {
    private byte[] data1=new byte[1024*1024];
    private byte[] data2=new byte[1024*1024];
    private byte[] data3=new byte[1024*1024];
    private byte[] data4=new byte[1024*1024];
    //Load as soon as it comes up, wasting memory resources
    private Hungry(){

    }
    private static Hungry hungry=new Hungry();
    public static Hungry getInstance(){
        return hungry;
    }

}

1.2 lazy style

public class LazyMan{
    private LazyMan(){
    }
    private static LazyMan lazyMan;
    public static LazyMan getInstance(){
    if(lazyMan==null){
        if(lazyMan==null){
            lazyMan=new LazyMan();
        }
        return lazyMan;
    
     }

    }

Under single thread, this code is valid. When the running environment is multithreaded, errors will occur.

1.3 double detection lock DCL

 //Dual detection lock mode lazy single case DCL
    public static LazyMan getInstance(){//volatile avoid instruction replay
        if (lazyMan==null){
            synchronized (LazyMan.class){
                if(lazyMan==null){
                    lazyMan=new LazyMan();
                   
                }
            }
        }
        return lazyMan;
    }

The theory behind double check locking is that the second check at / / 2 makes it impossible (as in Listing 3) to create two different {Singleton} objects. Assume the following sequence of events:

Thread 1 enters the getInstance() method.  

Since lazyMan is null, thread 1 enters the synchronized block at / / 1.  

Thread 1 is preempted by thread 2.

Thread 2 enters the getInstance() method.

Thread 2 attempted to acquire the lock at / / 1 because instance is still null. However, since thread 1 holds the lock, thread 2 blocks at / / 1.

Thread 2 is preempted by thread 1.

Thread 1 executes. Since the instance is still null at / / 2, thread 1 also creates a Singleton object and assigns its reference to instance.

Thread 1 exits the synchronized block and returns an instance from the getInstance() method.  

Thread 1 is pre occupied by thread 2.

Thread 2 acquires the lock at / / 1 and checks if instance is null.  

Since instance is not null, the second Singleton object is not created, and the object created by thread 1 is returned.
The theory behind double check locking is perfect. Unfortunately, the reality is completely different. The problem with double check locking is that there is no guarantee that it will run smoothly on single processor or multiprocessor computers.

The problem of double check locking failure is not attributed to the implementation bug in the JVM, but to the memory model of the Java platform. The memory model allows so-called "unordered writes", which is a major reason why these idioms fail.

Java write out of order: analyze this line of code lazyMan=new LazyMan();, This line of code is not an atomic operation. Behind this line of code:

*1 allocate memory space

*2 execute the construction method and initialize the object

*3 point this object to this space

It is possible to execute 132. At this time, lazyMan is empty, so it is necessary to avoid instruction rearrangement.

After using volatile keyword, reordering is prohibited, and all write operations will occur before read operations.

  private volatile static LazyMan lazyMan;
    /*public static LazyMan getInstance(){
        if(lazyMan==null)
            lazyMan=new LazyMan();
        return lazyMan;
    }*/
    //Dual detection lock mode lazy single case DCL
    public static LazyMan getInstance(){//volatile avoid instruction replay
        if (lazyMan==null){
            synchronized (LazyMan.class){
                if(lazyMan==null){
                    lazyMan=new LazyMan();
                    /*
                    * Not atomic operation
                    *1 Allocate memory space
                    * 2 Execute the construction method and initialize the object
                    * 3 Think of this object only in this space
                    * 123
                    * 132A
                    * B Since it has pointed to this space, it will be considered that this object is not empty
                    *
                    * */

                }
            }
        }
        return lazyMan;
    }

1.4 using inner classes to implement singleton mode

package com.single;
//Static inner class implementation
//unsafe
public class Holder {
    private Holder(){
    }
    public static Holder getInstance(){
        return InnerClass.HOLDER;
    }
    public static class InnerClass{
        private static final Holder HOLDER=new Holder();
    }
}

The reason why using static inner classes can ensure thread safety
Since the internal static class will only be loaded once, this implementation is thread safe
The initialization phase of class loading is single threaded
Class loading timing
When using the new, invokestatic, putstatic and getstatic instructions, if the class is not loaded, it will be triggered
When reflection uses a class, it is triggered if the class is not loaded
Triggered if the parent class is not loaded when the child class is loaded
When the program starts, the class where the main method is located will be loaded
   ...
Lazy loading of static inner classes should be the first case. Why is the static internal class not loaded when the external class is loaded? effective java says that the static internal class is just written in another class. In fact, it has no subsidiary relationship with the external class. (but put it directly on the outside. 1. If it is set to public, access is not restricted. 2. If it is private, access is restricted)
Thread safety is because the initialization phase of class loading is single threaded. The assignment statement of class variables is written in the function when compiling and generating bytecode. During initialization, single thread calls this to complete the assignment of class variables.

Static internal classes will not be initialized with the loading and initialization of external classes. They need to be loaded and initialized separately
Because it is created when the internal class is loaded and initialized, it is thread safe

Because jdk is thread safe when loading classes

1.5 single case mode of destruction by reflection

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        
        LazyMan instance=LazyMan.getInstance();
        Constructor<LazyMan> declaredConstructor=LazyMan.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);//In Java, the Field value in the entity class can be obtained through reflection. When the setAccessible method of Field is not set to true, the access security check will be carried out when calling, and the IllegalAccessException exception will be thrown
        //Then, the solution is to set the Accessible access flag bit of the Field object to Ture, so that the value of the private variable can be obtained through reflection, and the inspection of the access modifier will be ignored during access
        //Set to true to use a privatized constructor
        LazyMan instace2=declaredConstructor.newInstance();

    }

Bypassing constructor privatization by firing

The decision method adds judgment to the constructor

private LazyMan(){
        synchronized (LazyMan.class){
            if(lazyMan!=null){
                throw new RuntimeException("Do not break singleton mode");
            }
        }

        //System.out.println(Thread.currentThread().getName());
    }

The object pointer that does not come out through the new of getInstance will not be given to laztMan

private volatile static LazyMan lazyMan;

Because it is an object obtained directly from reflection

Both objects are taken from reflection:

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        /*for(int i=0;i<10;i++){
            new Thread(()->{
                LazyMan.getInstance();
            }).start();
        }*/

        Constructor<LazyMan> declaredConstructor=LazyMan.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);//In Java, the Field value in the entity class can be obtained through reflection. When the setAccessible method of Field is not set to true, the access security check will be carried out when calling, and the IllegalAccessException exception will be thrown
        //Then, the solution is to set the Accessible access flag bit of the Field object to Ture, so that the value of the private variable can be obtained through reflection, and the inspection of the access modifier will be ignored during access
        //
        LazyMan instance=declaredConstructor.newInstance();
        LazyMan instace2=declaredConstructor.newInstance();
        System.out.println(instance);
        System.out.println(instace2);
    }

Solution: add a discrimination bit.

private static boolean flag=false;
    private LazyMan(){
        synchronized (LazyMan.class){
            if(flag==false)
                flag=true;
            else {
                throw new RuntimeException("Do not break singleton mode");
            }
        }
        //System.out.println(Thread.currentThread().getName());
    }

The solution is to get the discrimination bit through reflection and change the discrimination bit.

None of them is safe.

1.6 use enumeration

public enum Singleton {

    INSTANCE;

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

}
Call method:

public class Main {

    public static void main(String[] args) {
        Singleton.INSTANCE.doSomething();
    }

}

Directly through Singleton.INSTANCE.doSomething()It can be called in the same way. Convenient, concise and safe.

1.7 enumeration classes cannot be reflected

package com.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public enum EnumSingle {
    INSTACNE;
    public EnumSingle getInstacne(){
        return INSTACNE;
    }

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        EnumSingle instance=EnumSingle.INSTACNE;
        Constructor<EnumSingle> declareConstructor=EnumSingle.class.getDeclaredConstructor();
        declareConstructor.setAccessible(true);
        EnumSingle instace2=declareConstructor.newInstance();
        System.out.println(instance);
        System.out.println(instace2);
    }
}

Operation results:

As a result, the compilation failed. Let's decompile this class and check the construction method:

Enumerate the final decompiled code

Then decompile through jad

jad -sjava 

Add the class file path after it

Code decompiled by jad

// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://kpdus.tripod.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   EnumSingle.java

package com.single;

import java.io.PrintStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public final class EnumSingle extends Enum
{

    public static EnumSingle[] values()
    {
        return (EnumSingle[])$VALUES.clone();
    }

    public static EnumSingle valueOf(String name)
    {
        return (EnumSingle)Enum.valueOf(com/single/EnumSingle, name);
    }

    private EnumSingle(String s, int i)
    {
        super(s, i);
    }

    public EnumSingle getInstacne()
    {
        return INSTACNE;
    }

    public static void main(String args[])
        throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException
    {
        EnumSingle instance = INSTACNE;
        Constructor declareConstructor = com/single/EnumSingle.getDeclaredConstructor(new Class[0]);
        declareConstructor.setAccessible(true);
        EnumSingle instace2 = (EnumSingle)declareConstructor.newInstance(new Object[0]);
        System.out.println(instance);
        System.out.println(instace2);
    }

    private static EnumSingle[] $values()
    {
        return (new EnumSingle[] {
            INSTACNE
        });
    }

    public static final EnumSingle INSTACNE = new EnumSingle("INSTACNE", 0);
    private static final EnumSingle $VALUES[] = $values();

}

You can see that the constructor needs to pass Stirng and int as parameters

package com.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public enum EnumSingle {
    INSTACNE;
    public EnumSingle getInstacne(){
        return INSTACNE;
    }

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        EnumSingle instance=EnumSingle.INSTACNE;
        Constructor<EnumSingle> declareConstructor=EnumSingle.class.getDeclaredConstructor(String.class,int.class);
        declareConstructor.setAccessible(true);
        EnumSingle instace2=declareConstructor.newInstance();
        System.out.println(instance);
        System.out.println(instace2);
    }
}

Even if we call the correct constructor, the enumeration cannot be reflected.

By looking at the source code of reflection, we can see that when creating objects through newInstance, reflection will check whether the class is decorated with ENUM. If so, an exception will be thrown and reflection fails.

2 factory mode

2.1 core idea

It realizes the separation of creator and caller.

Detailed classification: simple factory pattern, factory method pattern, abstract factory pattern

Core essence: the materialized object does not use new and is replaced by factory method.

The implementation class will be selected to create objects for unified management and control. This decouples the caller from our implementation class.

Three modes:

Simple factory mode: used to produce any product in the same hierarchical structure.

Factory method mode: used to produce fixed products in the same hierarchical structure.

Abstract factory pattern: create other factories around one super factory.

2.2 simple factory mode

Take buying a car as an example

package com.kuang.factory.simple;

public interface Car {
    public void name();
}

Create Car entity class

package com.kuang.factory.simple;

public class WuLing implements Car{
    @Override
    public void name() {
        System.out.println("Wuling Hongguang");
    }
}
package com.kuang.factory.simple;

public class Tesla implements Car{
    @Override
    public void name() {
        System.out.println("Tesla, I can't stop");
    }
}

Traditional car buying methods:

package com.kuang.factory.simple;

public class Consumer {
    public static void main(String[] args) {
        Car car=new WuLing();
        Car car1=new Tesla();
        car.name();
        car1.name();
        
    }
}

The new object must be manually.

Simple factory mode:

package com.kuang.factory.simple;
//Static factories are inconvenient to add new cars dynamically. For example, if we want to add Volkswagen cars, we must modify the code
public class CarFactory {
    public static Car getCar(String car){
        if(car.equals("WuLing")){
            return new WuLing();
        }else if (car.equals("Tesla")){
            return new Tesla();
        }
        return null;
    }

}
package com.kuang.factory.simple;

public class Consumer {
    public static void main(String[] args) {
        
        Car car2=CarFactory.getCar("WuLing");
        car2.name();
    }
}
The static factory is inconvenient to add new cars dynamically. For example, if we want to add Volkswagen cars, we must modify the code, which is against the law OOP The principle of opening and closing in.

If you need to add Volkswagen, you must modify the code in the car factory.

2.3 factory method mode

Add an abstract class of factory:

package com.kuang.factory.method;

public interface Factory {
    public Car getCar();
}

Build corresponding factories:

package com.kuang.factory.method;

public class WuLingFactory implements Factory{
    @Override
    public Car getCar() {
        return new WuLing();
    }
}
package com.kuang.factory.method;

public class TeslaFactory implements Factory{
    @Override
    public Car getCar() {
        return new Tesla();
    }
}

Implementation class:

package com.kuang.factory.method;

import com.kuang.factory.simple.CarFactory;

public class Consumer {
    public static void main(String[] args) {
        Factory wulingFactory=new WuLingFactory();
        Factory teslaFactory=new TeslaFactory();
        Car car=wulingFactory.getCar();
        Car car1=teslaFactory.getCar();
        car1.name();
        car.name();
    }
}

Summary:

Simple factory (static): although it does not conform to the design principles to some extent, it is actually used most.

Factory method mode: expand by adding new factory classes without modifying existing classes.

Abstract factory mode: products cannot be added, but product families can be added.

2.4 abstract factory mode

No matter how the factory splits and abstracts the above two modes, they are only for one kind of product Car (AbstractProduct). How should we express it if we want to generate another product PC?

This is the simplest production method in factory 2. But at the same time, it also means that we should completely copy and modify all the codes of Phone production management. Obviously, this is a stupid method and is not conducive to expansion and maintenance.

Abstract factory mode adds an interface to create products in AbstarctFactory, and realizes the creation of new products in specific sub factories. Of course, the premise is that the sub factories support the production of the products. Otherwise, the inherited interface can do nothing. Factory factory

Abstract Factory Pattern is to create other factories around a super factory. The super factory is also known as the factory of other factories. This type of design pattern is a creation pattern, which provides the best way to create objects.

The code is as follows:

PC interface

package com.kuang.factory.abstract1;

public interface PC {
    void start();
    void close();
    void internet();
    void email();
}

Phone interface

package com.kuang.factory.abstract1;

public interface Phone {
    void start();
    void close();
    void call();
    void song();
}

Factory interface

package com.kuang.factory.abstract1;

public interface abstractFactory {
    PC pcProduct();
    Phone phoneProduct();
}

Millet factory

package com.kuang.factory.abstract1;

public class XiaoMiFactory implements abstractFactory{
    @Override
    public PC pcProduct() {
        return new XiaoMiPC();
    }

    @Override
    public Phone phoneProduct() {
        return new XiaoMiPhone();
    }
}

Huawei factory

package com.kuang.factory.abstract1;

public class HuaWeiFactory implements abstractFactory{
    @Override
    public PC pcProduct() {
        return new HuaWeiPC();
    }

    @Override
    public Phone phoneProduct() {
        return new HuaWeiPhone();
    }
}

Related products

package com.kuang.factory.abstract1;

public class HuaWeiPC implements PC{
    @Override
    public void start() {
        System.out.println("Huawei PC Power on");
    }

    @Override
    public void close() {
        System.out.println("Huawei PC Shut down");
    }

    @Override
    public void internet() {
        System.out.println("Huawei PC surf the internet");
    }

    @Override
    public void email() {
        System.out.println("Huawei PC send emails");
    }
}

Test code

package com.kuang.factory.abstract1;

public class Test {
    public static void main(String[] args) {
        abstractFactory xiaoMiFactory=new XiaoMiFactory();
        PC xiaomiPC=xiaoMiFactory.pcProduct();
        Phone xiaomiPhone=xiaoMiFactory.phoneProduct();
        xiaomiPC.close();
        xiaomiPC.email();
        xiaomiPC.internet();
        xiaomiPhone.call();
        xiaomiPhone.close();
        xiaomiPhone.song();
        xiaomiPhone.start();
        abstractFactory huaWeiFactory=new HuaWeiFactory();
        PC huaweiPC=huaWeiFactory.pcProduct();
        Phone huaweiPhone=huaWeiFactory.phoneProduct();
        huaweiPC.internet();
        huaweiPC.email();
        huaweiPC.start();
        huaweiPhone.start();
        huaweiPhone.song();
        huaweiPhone.call();

    }
}

The abstract factory mode gives full play to the principle of opening and closing and dependency inversion, but the disadvantage is that the abstract factory mode is too heavy. If the IFactory interface needs new functions, it will affect all specific factory classes. Using the abstract factory pattern, you only need to change one line of code when replacing a specific factory, but to add an abstract method, you need to modify all the specific factory classes. Therefore, the abstract factory mode is suitable for the horizontal expansion of similar factories, not for the vertical expansion of new functions.

3. Builder mode

The builder pattern is also a creation pattern, which provides the best way to create objects.

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

Main function
When users do not know the construction process and details of objects, they can create complex objects. (to put it bluntly, it is to hide the internal construction process and details)

For example:

Factory (builder mode): responsible for manufacturing cars (assembly process and details are in the factory)
Car buyer (user): you just need to say the model you need (that is, the type and content of the object), and then pay to buy it directly (you don't need to know how the car is assembled)

Practical scope
1. When the algorithm for creating a complex object should be independent of the components of the object and how they are assembled.
2. When the construction process must allow different representations of the constructed object.

role
In such a design pattern, there are the following roles:
1. Builder: specify the abstract interface for each part of creating a product object.
2. ConcreteBuilder: implement the interface of Builder to construct and assemble various parts of the product, define and clarify the representation it creates, and provide an interface to retrieve the product.
3. Director: construct an object using the Builder interface to guide the construction process. It is also used to isolate the association between users and the construction process
4. Product: represents the complex object being constructed. ConcreteBuilder creates the internal representation of the product and defines its assembly process, including the classes that define the components, including the interfaces that assemble these components into the final product.

For building a house, the construction process is fixed. The difference lies in the details of the house, such as one room and one living room, two rooms and one living room. The specific size of the house is different.

When to use builder mode?

It is mainly used to create some complex objects. The construction order between the internal constructions of these objects is usually stable, but the internal constructions of objects are often faced with complex changes.

Difference between builder mode and factory mode: factory mode does not care about the construction process, but only about what products are produced by what factory.

The builder mode will care about the process and pay attention to the assembly, combination and packaging of parts.

Builder class,

package com.kuang.Builder;

public interface Builder {
    void buildlivingroom();
    void buildbedroom();
    void buildkitchen();
    Product getProduct();
}

Implementation class of Builder:

package com.kuang.Builder;

public class BigHouse implements Builder{
    private Product product=new Product();
    @Override
    public void buildlivingroom() {
        product.setLivingroom("Large living room");
        System.out.println("Build a large living room");
    }

    @Override
    public void buildbedroom() {
        product.setBedroom("Big bedroom");
        System.out.println("Build a large bedroom");
    }

    @Override
    public void buildkitchen() {
        product.setKitchen("Big kitchen");
        System.out.println("Build a large kitchen");
    }

    @Override
    public Product getProduct() {
        return product;
    }
}
package com.kuang.Builder;

public class SmallHouse implements Builder{
    private Product product=new Product();
    @Override
    public void buildlivingroom() {
        product.setLivingroom("Small living room");
        System.out.println("Build a small living room");
    }

    @Override
    public void buildbedroom() {
        product.setBedroom("Small bedroom");
        System.out.println("Build a small bedroom");
    }

    @Override
    public void buildkitchen() {
        product.setKitchen("Kitchenette");
        System.out.println("Build kitchenette");
    }

    @Override
    public Product getProduct() {
        return product;
    }
}

The Director class is used to direct the construction process

package com.kuang.Builder;

public class Director {
    public Product build(Builder builder){
        builder.buildlivingroom();
        builder.buildbedroom();
        builder.buildkitchen();
        return builder.getProduct();
    }
}

The above is the conventional writing method of the Builder mode. The Director class plays a very important role in the Builder mode. It is used to guide the specific Builder how to build the product, control the call order, and return the complete product class to the caller. However, in some cases, it is necessary to simplify the system structure and combine the Director with the abstract Builder

The construction of disorderly assembly of parts is realized by static internal classes, which is more flexible and more in line with the definition. There is a default implementation of complex objects inside. When using, you can freely define and change the content according to user needs, and there is no need to change the specific construction method. Can produce different complex products

For example, for McDonald's package, the waiter (specific builder) can mix any number of products (parts) to form a package (product) and then sell it to customers. There are fewer commanders than the first method, mainly because the second method gives the commander to the user to operate, making the creation of products more simple and flexible.
 

//builder
public abstract class Builder {

    abstract Builder builderA(String msg); //hamburger
    abstract Builder builderB(String msg); //cola
    abstract Builder builderC(String msg); //French fries
    abstract Builder builderD(String msg);//Dessert


    abstract Product getProduct();
}
package builder.demo2;

//Product: package
public class Product {

    private  String BuilderA="hamburger";
    private  String BuilderB="cola";
    private  String BuilderC="French fries";
    private  String BuilderD="Dessert";

    public String getBuilderA() {
        return BuilderA;
    }

    public void setBuilderA(String builderA) {
        BuilderA = builderA;
    }

    public String getBuilderB() {
        return BuilderB;
    }

    public void setBuilderB(String builderB) {
        BuilderB = builderB;
    }

    public String getBuilderC() {
        return BuilderC;
    }

    public void setBuilderC(String builderC) {
        BuilderC = builderC;
    }

    public String getBuilderD() {
        return BuilderD;
    }

    public void setBuilderD(String builderD) {
        BuilderD = builderD;
    }

    @Override
    public String toString() {
        return "Product{" +
                "BuilderA='" + BuilderA + '\'' +
                ", BuilderB='" + BuilderB + '\'' +
                ", BuilderC='" + BuilderC + '\'' +
                ", BuilderD='" + BuilderD + '\'' +
                '}';
    }
}

package builder.demo2;

//Specific builder
public class Worker extends Builder {

    private Product product;

    public Worker( ) {
        product = new Product();
    }


    @Override
    Builder builderA(String msg) {
        product.setBuilderA(msg);
        return this;
    }

    @Override
    Builder builderB(String msg) {
        product.setBuilderB(msg);
        return this;
    }

    @Override
    Builder builderC(String msg) {
        product.setBuilderC(msg);
        return this;
    }

    @Override
    Builder builderD(String msg) {
        product.setBuilderD(msg);
        return this;
    }

    @Override
    Product getProduct() {
      return product;
    }
}

package builder.demo2;

import javax.jws.WebService;


public class Test {

    public static void main(String[] args) {

        //waiter
        Worker worker=new Worker();

        //Chain programming
        Product product=worker.builderA("drumsticks").getProduct();
        System.out.println(product.toString());
    }
}

advantage
The construction and representation of products are separated to realize decoupling. Using the builder mode can make the client do not have to know the details of the internal composition of the product.
The creation steps of complex products are decomposed into different methods to make the creation process clearer
The specific builder classes are independent of each other, which is conducive to the expansion of the system. Adding a new specific builder does not need to modify the code of the original class library, which is in line with the opening and closing principle.
shortcoming
The products created by the builder model generally have more in common, and their components are similar; If there are great differences between products, it is not suitable to use the builder mode, so its scope of use is limited.
If the internal changes of the product are complex, it may lead to the need to define many specific constructors to realize this change, resulting in the system becoming very large.
Application scenario
The product objects to be produced have complex internal structure, and these product objects have commonalities;
Isolate the creation and use of complex objects, and make the use of the creation process to create different products.
It is suitable for the creation process of a product (object) with more parts (attributes).
 

Comparison between builder and abstract factory mode:

Compared with the abstract factory pattern, the builder pattern returns an assembled complete product, while the abstract factory pattern returns a series of related products, which are located in different product hierarchy and form a product family.

In the abstract factory mode, the client is an instance of the chemical plant class, and then the factory method is used to get the desired product object. In the builder mode, the client can not directly call the builder's related methods, but through the command class to guide how to generate objects, including the assembly process and the construction steps of the object, and he focuses on constructing a complex object step by step. Returns a complete object.

If the abstract factory model can be called an auto parts production factory to produce the products of a product family, the builder model is an auto assembly factory, which can return a complete car through the assembly of parts.

4 prototype design mode

Prototype pattern, which uses prototype instances to specify the type of objects to be created, and creates new objects by copying these prototypes.

Prototype pattern is actually to create another customizable object from one object without knowing any creation details.

Why do you need a prototype pattern?

If we need a large number of the same objects, if we generate these objects through new every time, it will consume a lot of time. Every new time, the constructor needs to be executed. If the execution time of the constructor is very long, multiple initialization operations will be very inefficient. Generally, when the initialization information does not change, cloning is the best method, which hides the details of object creation and greatly improves the performance.

To implement the prototype pattern in JAVA, you only need to implement the clonable interface and rewrite the clone method.

Let me give an example:

If I am a fresh graduate and want to send a large number of resumes to the company, it is obviously inconvenient to generate resumes one by one. We can use the prototype pattern to solve this problem.

package com.kuang.Prototype;

import java.util.Date;

public class Resume implements Cloneable{
    private String name;
    private String sex;
    private String age;
    private WorkExperience work;

    @Override
    protected Object clone() throws CloneNotSupportedException {

        return super.clone();
    }

    public String getName() {
        return name;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
    public void SetWorkExperience(Date date,String company){
        work.workDate=date;
        work.company=company;
    }

    public Resume(String name, String sex, String age, WorkExperience work) {
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.work = work;
    }

    public Resume() {
        work=new WorkExperience();
    }

    @Override
    public String toString() {
        return "Resume{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", age='" + age + '\'' +
                ", work=" + work +
                '}';
    }
    public void Display(){
        System.out.println(name+" "+sex+" "+age);
        System.out.println(work);
    }
}

package com.kuang.Prototype;

import java.util.Date;

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Resume a=new Resume();
        a.setName("Big bird");
        a.setAge("18");
        a.setSex("male");
        a.SetWorkExperience(new Date(1221),"xx-company");
        Resume b=(Resume) a.clone();
        b.SetWorkExperience(new Date(222222),"YY enterprise");
        Resume c=(Resume) a.clone();
        c.SetWorkExperience(new Date(),"ZZ enterprise");
        a.Display();
        b.Display();
        c.Display();
    }
}

This quickly generated three resumes.

If we add a work experience class in the resume class, which contains attributes such as time and company name, the resume class directly calls this object.

package com.kuang.Prototype;

import java.util.Date;

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Resume a=new Resume();
        a.setName("Big bird");
        a.setAge("18");
        a.setSex("male");
        a.SetWorkExperience(new Date(1221),"xx-company");
        Resume b=(Resume) a.clone();
        b.SetWorkExperience(new Date(222222),"YY enterprise");
        Resume c=(Resume) a.clone();
        c.SetWorkExperience(new Date(),"ZZ enterprise");
        a.Display();
        b.Display();
        c.Display();
    }
}

Operation results

This result is not what we want, which is related to the shallow replication and deep replication in the prototype mode.

Shallow copy: all variables of the copied object contain the same value as the original object, while all references to other objects still point to the original object.

Three assignments are equivalent to three assignments to the same object.

Deep copy points the variables of the referenced object to the copied new object instead of the original referenced object.

We will implement the clonable interface of the work experience class and rewrite the clone method.

package com.kuang.Prototype;

import java.util.Date;

public class WorkExperience implements Cloneable{
    public Date workDate;
    public String company;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public WorkExperience() {
    }

    public WorkExperience(Date workDate, String company) {
        this.workDate = workDate;
        this.company = company;
    }

    @Override
    public String toString() {
        return "WorkExperience{" +
                "workDate=" + workDate +
                ", company='" + company + '\'' +
                '}';
    }

    public Date getWorkDate() {
        return workDate;
    }

    public void setWorkDate(Date workDate) {
        this.workDate = workDate;
    }

    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        this.company = company;
    }
}

Modify the clone method in the resume class to turn work into a class that can clone.

package com.kuang.Prototype;

import java.util.Date;

public class Resume implements Cloneable{
    private String name;
    private String sex;
    private String age;
    private WorkExperience work;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object o=super.clone();
        Resume resume=(Resume) o;
        resume.work= (WorkExperience) this.work.clone();
        return resume;
    }

    public String getName() {
        return name;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
    public void SetWorkExperience(Date date,String company){
        work.workDate=date;
        work.company=company;
    }

    public Resume(String name, String sex, String age, WorkExperience work) {
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.work = work;
    }

    public Resume() {
        work=new WorkExperience();
    }

    @Override
    public String toString() {
        return "Resume{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", age='" + age + '\'' +
                ", work=" + work +
                '}';
    }
    public void Display(){
        System.out.println(name+" "+sex+" "+age);
        System.out.println(work);
    }
}

This enables deep replication.

In some specific situations, deep replication or shallow replication is often involved, such as DataSet object DataSet, which has clone and copy methods. Clone method is used to replicate DataSet structure, but does not replicate data. It is equivalent to shallow replication, and the overhead is relatively small. Copy method copies all of them to realize class deep copy.

5 adapter mode

Adapter mode: convert the interface of a class into another interface desired by the customer. Adapter mode enables those classes that cannot work together due to incompatible interfaces to work together.

Adapters are divided into two types: one is class adapter and object adapter.

Let's illustrate with an example of notebook Internet access. Today's thin and light books have no traditional Internet port. When you need to surf the Internet, you can only link through one adapter.

Class adapter mode

Network cable:

package com.kuang.Adapter;

public class Adaptee {
    public void request(){
        System.out.println("Connect the network cable to the Internet");
    }

}

converter:

package com.kuang.Adapter;

public interface NetToUSB {
    public void handleRequest();
}

Realize through the interface and enhance reuse

Adapter

package com.kuang.Adapter;

public class Adapter extends Adaptee implements NetToUSB{

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

computer:

package com.kuang.Adapter;

public class Computer {
    //Our computer needs to be connected to a 1: transponder to access the 1: Internet
    public void net(NetToUSB adapter) {
        //For the specific realization of Internet access, find "a transfer"
        adapter.handleRequest();
    }

    public static void main(String[] args) {
        //Computer, adapter, network cable~
        Computer computer = new Computer(); //computer
        Adaptee adaptee = new Adaptee(); //Network cable
        Adapter adapter = new Adapter(); //Rotary press
        computer.net(adapter);
    }


}

Object adapter, through combination:

Just change the adapter:

package com.kuang.Adapter;

public class Adapter2 implements NetToUSB{
    private Adaptee adaptee;
    public Adapter2(Adaptee adaptee){
        this.adaptee=adaptee;
    }

    @Override
    public void handleRequest() {
        adaptee.request();
    }
}

package com.kuang.Adapter;

public class Computer {
    //Our computer needs to be connected to a 1: transponder to access the 1: Internet
    public void net(NetToUSB adapter) {
        //For the specific realization of Internet access, find "a transfer"
        adapter.handleRequest();
    }

    public static void main(String[] args) {
      
        Computer computer = new Computer(); //computer
        Adaptee adaptee = new Adaptee(); //Network cable
        Adapter2 adapter = new Adapter2(adaptee); //Rotary press

        computer.net(adapter);
    }


}

Application of adapter mode:

DataAdapter is used as an adapter between DataSet and data source to retrieve and save data. The DataAdapter provides this adapter by mapping Fill and Update. Because the data source may come from multiple data sources, for example, SQL Server {may come from Oracle. These data sources are different, but we want to get a unified DataSet. At this time, using DataAdapter is a very good means. We can use data flexibly without paying attention to the data details of the database.

6 bridge mode

Bridging mode is to separate the abstract part from its implementation part, so that they can change independently. It is an object structure pattern, also known as Bing body pattern or interface pattern.

Brand class

package com.kuang.bridge;

//brand
public interface Brand {

    void info();
}

Lenovo

package com.kuang.bridge;

//Lenovo brand
public class Lenovo implements Brand {
    @Override
    public void info() {
        System.out.print("association");
    }
}

Apple

package com.kuang.bridge;

//Lenovo brand
public class Apple implements Brand {
    @Override
    public void info() {
        System.out.print("Apple");
    }
}

Computer

package com.kuang.bridge;

//Abstract computer type class
public abstract class Computer {
    //Combination, brand~
    protected Brand brand;

    public Computer(Brand brand) {
        this.brand = brand;
    }

    public void info() {
        brand.info();//Own brand
    }


}

class Desktop extends Computer {
    public Desktop(Brand brand) {
        super(brand);
    }

    @Override
    public void info() {
        super.info();
        System.out.print("Desktop");
    }
}

class Laptop extends Computer {
    public Laptop(Brand brand) {
        super(brand);
    }

    @Override
    public void info() {
        super.info();
        System.out.print("notebook");
    }
}

Test

package com.kuang.bridge;

public class Test {
    public static void main(String[] args) {
        //Apple tube notebook
        Computer computer = new Laptop(new Apple());
        computer.info();

        System.out.println();
        //Lenovo desktop
        Computer computer2 = new Desktop(new Lenovo());
        computer2.info();
    }
}

Operation results

Apple notebook
 Lenovo desktop


SWOT Analysis


Principle re understanding

Differences between bridge mode and adapter mode:

common ground

Both bridges and adapters work together

The starting point is different.
1) adapter: change the two existing interfaces to make them compatible.
2) bridging mode: separate abstraction and implementation, so that their interfaces can be different. The purpose is to separate them.

So, if you get two existing modules and want them to work at the same time, you use the adapter.
If you don't have anything yet, but want to implement it separately, bridging is an option.

Bridging is a bridge before there are things at both ends
Adaptation is to have things on both sides before there is an adapter

After the bridge is completed, the things on both sides can be changed.

7 static proxy mode

The proxy pattern is the bottom layer of spring AOP

Role analysis: Abstract role: it is generally solved by using interfaces or abstract classes

Real role: the role represented

Proxy role: proxy real role, proxy real role monkey, we usually do some ancillary operations

Client: the person who accesses the proxy object

Benefits of agent mode:

It can make the real role more pure without paying attention to some public business

The public business can be handed over to the agent role to realize the division of business

When the public business is expanded, it is convenient for centralized management

Disadvantages:

A real role will produce a proxy role, the amount of code will increase, and the development efficiency will become lower.

Code steps:

1 interface:

package com.kuang.staticproxy;

public interface Rent {
    void rent();
}

2 real role

package com.kuang.staticproxy;

public class Host implements Rent{
    @Override
    public void rent() {
        System.out.println("The landlord rented the house");
    }
}

3 agent role

package com.kuang.staticproxy;

public class Client {
    public static void main(String[] args) {
        Host host=new Host();
        Proxy proxy=new Proxy(host);//The agent helps the landlord rent a house, but the agent role will have some ancillary operations! This is aspect oriented programming
        proxy.rent();
    }
}

4 client access role

package com.kuang.staticproxy;

public class Proxy implements Rent{
    private Host host;
    public Proxy(){

    }
    public Proxy(Host host){
        this.host=host;
    }

    @Override
    public void rent() {
        seeHouse();
        host.rent();
        hetong();
        fare();
    }
    public void seeHouse(){
        System.out.println("The agent will show you the house");
    }
    public void fare(){
        System.out.println("Intermediary fee");
    }
    public void hetong(){
        System.out.println("sign a contract");
    }
}

AOP

package com.kuang.staticproxy;

public interface UserService {
    void add();
    void delete();
    void update();
    void selete();
}
package com.kuang.staticproxy;

public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("Added an object");
    }

    @Override
    public void delete() {
        System.out.println("An object was deleted");
    }

    @Override
    public void update() {
        System.out.println("Modified an object");
    }

    @Override
    public void selete() {
        System.out.println("Find an object");
    }
}
package com.kuang.staticproxy;

public class UserServiceProxy implements UserService{
    private UserServiceImpl userService;

    public UserServiceProxy(UserServiceImpl userService) {
        this.userService = userService;
    }

    public UserServiceProxy() {
    }

    @Override
    public void add() {
        userService.add();
        log("add");
    }

    @Override
    public void delete() {
        userService.delete();
        log("delete");
    }

    @Override
    public void update() {
        userService.update();
        log("update");
    }

    @Override
    public void selete() {
        userService.selete();
        log("select");
    }
    public void log(String name){
        System.out.println("Called"+name+"method");
    }
}
package com.kuang.staticproxy;

public class Client2 {
    public static void main(String[] args) {
        UserServiceImpl userService=new UserServiceImpl();
        UserServiceProxy proxy=new UserServiceProxy(userService);
        proxy.add();
        proxy.delete();
        proxy.selete();
        proxy.update();
    }
}

8 dynamic agent

See the previous blog < < CRAZY God says spring 5 learning notes > > section 10

9 decorator mode

As crazy God didn't update the relevant content, the rest of the content came from Han Shunping, a teacher from Shang Silicon Valley.

Starbucks Coffee order project Starbucks Coffee order project (CAFE):

1) Coffee type / single product: Espresso, ShortBlack, longblack, decaf

2) Seasoning: Milk, soy Milk, Chocolate

3) When expanding new coffee varieties, it is required to have good expansibility, convenient modification and convenient maintenance

4) Use OO to calculate the cost of different kinds of coffee: customers can order single coffee or single coffee + seasoning combination

Solution 1 - Analysis of Starbucks Coffee order

1) Drink is an abstract class that represents a drink

2) des is a description of coffee, such as the name of coffee

3) The cost () method is to calculate the cost and make an abstract method in the sink class

4) Decaf is a single product of coffee. It inherits Drink and realizes cost

5) Espress & & milk is a single coffee + seasoning. There are many combinations

6) Problem: with this design, there will be many classes. When we add a single coffee or a new seasoning, the number of classes will double and there will be class explosion

Problem analysis of scheme 2 -

1) Scheme 2 can control the number of classes without causing many classes

2) When adding or deleting seasoning types, the maintenance of codes is very heavy

3) Considering that the user can add multiple condiments, you can return hasMilk to a corresponding int

4) Consider using decorator mode

9.1 definition of decorator mode

Decorator pattern definition

1) Decorator mode: dynamically attach new functions to objects. In terms of object function expansion, it is more flexible than inheritance, and the decorator mode also embodies the open close principle (ocp)

2) The dynamic addition of new functions to objects and ocp principles mentioned here will be reflected in the form of code in the later application examples. Please pay attention to the experience.

Decorator mode

Decorator mode principle

1) Decorator mode is like packing a courier

 main body: for example: ceramics, clothes (Component) / / decorated person

Packaging: for example: newspaper stuffing, plastic foam, cardboard, board (Decorator)

2) Component body: for example, similar to the previous Drink

3) ConcreteComponent and Decorator ConcreteComponent: specific subjects, such as the previous single coffee Decorator: decorators, such as spices

4) Between the Component and ConcreteComponent as shown in the figure, if there are many ConcreteComponent classes, you can also design a buffer layer to extract the common parts, and the abstraction layer is a class.

Order in decorator mode: 2 chocolate + 1 milk LongBlack

explain

1) Milk contains LongBlack

2) A Chocolate contains (milk + long black)

3) A Chocolate contains (Chocolate + milk + long black)

4) In this way, no matter what form of single coffee + seasoning combination, it can be easily combined and maintained through recursion.

package com.kuang.decorator;

public abstract class Drink {
    public String des;
    private float price=0.0f;

    public String getDes() {
        return des;
    }

    public void setDes(String des) {
        this.des = des;
    }

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }
    public abstract float cost();
}
package com.kuang.decorator;

public class Coffee extends Drink{
    @Override
    public float cost() {
        return super.getPrice();
    }
}

package com.kuang.decorator;

public class Espresso extends Coffee{
    public Espresso() {
        setDes(" Italian Coffee ");
        setPrice(6.0f);
    }
}
package com.kuang.decorator;

public class ShortBlack extends Coffee{
    public ShortBlack() {
        setDes(" shortblack ");
        setPrice(4.0f);
    }
}
package com.kuang.decorator;

public class LongBlack extends Coffee{
    public LongBlack() {
        setDes(" longblack ");
        setPrice(5.0f);
    }
}
package com.kuang.decorator;

public class DeCaf extends Coffee{
    public DeCaf() {
        setDes(" Coffee without cause ");
        setPrice(1.0f);
    }
}
package com.kuang.decorator;

public class Decorator extends Drink{
    private Drink obj;

    public Decorator(Drink obj) { //combination
        // TODO Auto-generated constructor stub
        this.obj = obj;
    }

    @Override
    public float cost() {
        // TODO Auto-generated method stub
        // getPrice own price
        return getPrice() + obj.cost();
    }

    @Override
    public String getDes() {
        // TODO Auto-generated method stub
        // obj.getDes() outputs the information of the decorated person
        return des + " " + getPrice() + " && " + obj.getDes();
    }
}
package com.kuang.decorator;

public class Chocolate extends Decorator{
    public Chocolate(Drink obj) {
        super(obj);
        setDes(" Chocolates ");
        setPrice(3.0f); // Price of condiment
    }
}
package com.kuang.decorator;

public class Soy extends Decorator{
    public Soy(Drink obj) {
        super(obj);
        // TODO Auto-generated constructor stub
        setDes(" Soybean Milk  ");
        setPrice(1.5f);
    }
}
package com.kuang.decorator;

public class Milk extends Decorator{
    public Milk(Drink obj) {
        super(obj);
        // TODO Auto-generated constructor stub
        setDes(" milk ");
        setPrice(2.0f);
    }
}
package com.kuang.decorator;

public class Bar {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        // Order in decorator mode: 2 chocolate + 1 milk LongBlack

        // 1. Order a LongBlack
        Drink order = new LongBlack();
        System.out.println("Cost 1=" + order.cost());
        System.out.println("describe=" + order.getDes());

        // 2. order add a portion of milk
        order = new Milk(order);

        System.out.println("order Add a milk charge =" + order.cost());
        System.out.println("order Add a description of the milk = " + order.getDes());

        // 3. order add a chocolate

        order = new Chocolate(order);

        System.out.println("order Add one milk and one chocolate =" + order.cost());
        System.out.println("order Add a portion of milk and a portion of chocolate = " + order.getDes());

        // 3. order add a chocolate

        order = new Chocolate(order);

        System.out.println("order Add one milk and two chocolate =" + order.cost());
        System.out.println("order Add one portion of milk and two portions of chocolate = " + order.getDes());

        System.out.println("===========================");

        Drink order2 = new DeCaf();

        System.out.println("order2 No coffee fee =" + order2.cost());
        System.out.println("order2 Coffee description = " + order2.getDes());

        order2 = new Milk(order2);

        System.out.println("order2 No charge for adding a portion of milk to coffee =" + order2.cost());
        System.out.println("order2 Add a milk description to the coffee = " + order2.getDes());

    }
}

Keywords: Java Singleton pattern

Added by ChemicalBliss on Sun, 20 Feb 2022 17:31:00 +0200