Common design patterns & design principles

Posted by westair on Mon, 27 Dec 2021 08:36:58 +0100

1 design mode

1 strategy mode

Policy mode:
Design an interface for a function that can be changed
A series of implementation classes are extended for the interface, and the unique characteristics of the object are given when the object is generated
Separate the implementation of functions from objects and separate objects and behaviors to reduce coupling and improve code reuse

If a duck yard is simulated, there are green headed ducks and red headed ducks
 At first, each duck had its own members and methods, but with the increase of the number of ducks
 There are too many classes to maintain the design, so consider creating a duck parent class using inheritance
 All duck subclasses inherit it and optionally override the methods in the parent class

But as the system becomes bloated, when you need to change a method in the parent class
 Methods in thousands of subclasses also need to be overridden, and inheritance affects the whole body

Therefore, the flight, chirp and other behaviors can be made into interfaces, and various implementation classes can be made for these interfaces
 The parent class takes these interfaces as members, and the child class selectively instantiates the implementation classes of these interfaces to complete different functions
 And can pass set Method to replace the function

2 observer mode

Observer mode:
The roles in the system are divided into topics and observers. Observers work with topics. Topics can publish data so that all observers can get the same data to get a cleaner design. Swing and many GUI frameworks use observer mode a lot

Referring to the working mode of the newspaper, the observer mode is equal to the publishing house+subscriber
 The publishing house manages all subscribers and publishes information to all its subscribers whenever it needs to publish information

Two interfaces can be designed to complete observer mode
ISpeaker and IListener,ISpeaker As a theme, IListener As an observer
ISpeaker The abstract methods are addOneListener,removeOneListener,speakMessage
IListener The abstract methods are readMessage
 The class as the subject implements ISpeaker Maintain one after the interface List<IListener>
Whenever information is published speakMessage Polling when List Call all observers readMessage that will do

The subject does not know any information about the observer. It does not lie in what the observer is or what he is doing
 The observer does not know the specific implementation of the topic. It only lies in the messages from the topic, which is loose coupling

The above behavior of publishing messages from the topic to the observer can be regarded as the push of the topic to the observer
 At the same time, the subject object can also be directly sent to the observer, which can be used by the observer himself get The data in the topic to actively pull information

3 decorator mode

Decorator mode:
The decorated person and the decorator belong to the same type. The decorated person is regarded as a member of the decorator. The process of decoration is to take the decorated person as a parameter of the decorator, generate a new decorator, and take the object as a member to expand the function through combination, Java IO is full of decorator mode

When designing a coffee shop menu, you can initially design a category for each drink
 However, with the increase of drinks, more and more kinds are needed, and there are also large cups, small cups, sugar, milk and other needs
 If you design a class for each demand, such as large coffee with sugar, small latte with sugar and milk
 Then it will appear in the system"Explosion like",Too many classes need to be maintained

Therefore, the decorator mode can be adopted, and the basic drinks can be used as"Decorated person",Take a large cup, add sugar, etc"Decorator"
They have a common parent"Material Science",Make the decorator a member of the decorator
 When you need a small cup of sugar and milk latte, create a latte object first
 Then use the small cup as a parameter"decorate",The obtained object is decorated with sugar...

Example:

public class Tea extends Beverage {

    public Tea() { super("Tea"); }

    @Override
    public double cost() { return 10; }
}

public class Milk extends Decorator {
    private Beverage beverage;

    public Milk(Beverage beverage) {
        super("milk");
        this.beverage = beverage;
    }

    @Override
    public double cost() {
        return 3 + beverage.cost();
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + " + Milk";
    }
}

eg:
Tea tea = new Tea();
tea = new Milk(tea); // Decorated with milk
tea = new Mocha(tea); // Decorated with mocha
tea = new BigCup(tea); // Decorated with large cups
 Got a big cup of tea with milk and mocha

java. Decorator mode in io:

FileInputStream inputStream = new FileInputStream("");
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);

4 factory mode

Factory mode:
When a large number of objects are created in a class, the more objects, the more fragile the class is. If a dependency is modified slightly, the whole class cannot work normally. Therefore, it is necessary to separate the function implementation from the object creation process, encapsulate the object creation process, decouple the program, and separate the function implementation from the object creation to reduce the dependency

Factory mode can be divided into simple factory and factory method

The simple factory provides a method or static method for producing the corresponding object, and the return value is this kind of object
 Put the object creation process into the method of another class
public static Pizza createPizza(String type) {
    Pizza pizza = null;

    if("cheese".equals(type)) {
        pizza = new CheesePizza();
    } else if("clam".equals(type)) {
        pizza = new ClamPizza();
    } else {
        return null;
    }
    
    return pizza;
}


The factory method defines an abstract class to create an object, delaying the process of creating an object to a subclass
 When a subclass tries to get an object from the factory, the concrete production object is implemented by the abstract method completed in the subclass
public abstract class PizzaStore {

    public Pizza getPizza(String type) {
        Pizza pizza;

        pizza = createPizza(type);
        return pizza;
    }

    protected abstract Pizza createPizza(String type);
}

5 singleton mode

Singleton mode:
Sometimes only one object is needed in the whole application, such as registry, log and thread pool. If these objects are created arbitrarily without constraints, it will lead to abnormal program behavior and unexpected results. The singleton mode ensures that there is only one instance of a class and provides a global access point

Singleton mode is often used to manage shared unique resources

Normal singleton mode:
public class Registry {
    private static Registry instance;

    private Registry() {}
    
    public static Registry getInstance() {
        if(instance == null) {
            instance = new Registry();
        }
        return instance;
    }     
}


Synchronous singleton mode under multithreading:
public class Registry {
    private volatile static Registry registry;

    private Registry() {}

    public static Registry getInstance() {
        if(registry == null) {
            synchronized (Registry.class) {
                if(registry == null) {
                    return new Registry();
                }
            }
        }
        return registry;
    }
}

The above two singletons are created in lazy mode
 That is, reproduction when acquisition is needed, the hungry man mode can also be used
 However, if there are a large number of objects created by hungry man mode, it will give users a bad experience when the application is started

6 command mode

Command mode:
Through the command mode, methods can be encapsulated and actions can be encapsulated into command objects, so that they can be stored, transmitted and called at will. The command mode decouples the object issuing the request from the object executing the request

In the process of designing a remote controller, the remote controller can control the switch of TV and table lamp
 When the design mode is not adopted, objects such as TV and table lamp are encapsulated into the remote control object
 The switching method of remote controller is packaged with the switching method of TV and table lamp
 Such a design first creates dependencies, and functions are coupled with objects

If you use command mode, design a Command Interface, which contains an abstract method execute()
For this Command The interface implements different command objects, such as light on object and light off object
 Specific objects are encapsulated in the light on object and light off object Light,The specific function implementation is completed by the command object
 Use the combination of the interface as the caller(Remote control)Members of, through set To replace the command object

When the caller makes this call, the method in the command object is called
 The command object will complete specific operations in the future, so as to decouple the object issuing the request from the object executing the request

7 adapter mode

Adapter mode:
Convert the interface of a class into another interface expected
The adapter pattern makes it easy for incompatible classes to cooperate

When one interface is needed to realize multiple functions, the adapter mode is adopted
 stay JDK of swing In technology, listeners are usually added to various components, such as focus acquisition/lose
	/*The numbered text box gets the focus, and all the numbered text boxes are selected*/
        this.jtxtId.addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent e) {
                jtxtId.selectAll();
            }
        });

        /*The name text box gets the focus, and all the name text boxes are selected*/
        this.jtxtName.addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent e) {
                jtxtName.selectAll();
            }
        });

FocusAdapter Is an adapter that implements FocusListener Interface
 Here, the specific implementation method is given to the user to rewrite the method to use according to the scenario

The function can be decoupled from the implementation through the adapter pattern, and different adapters can be replaced through combination
 And the adapter can be empty and implemented by the user

8 template method mode

Template method mode:
The skeleton of an algorithm is defined in a method, and some steps are delayed to subclasses. The template method can enable subclasses to redefine some steps in the algorithm without changing the structure of the algorithm framework

That is, there is a template method containing several abstract methods in the parent class, and different subclasses inherit the parent class and implement different abstract methods
 The template method has different functions

In the process of making coffee and tea
 tea: 1,Bring the water to a boil -> 2,Brew tea with boiling water -> 3,Pour the tea into the cup -> 4,Add lemon
 Coffee: 1,Bring the water to a boil -> 2,Brew coffee with boiling water -> 3,Pour the coffee into the cup -> 4,Add sugar and milk
 We can simply make each step into a method and put it in a specific place Tea and Coffee In class
 However, this leads to the coupling between function and implementation, and it is found that the first step and the third step are the same. Therefore, the template method pattern can be adopted

public abstract class Drink {

    public Drink() {}

    public final void prepare() {
        boilWater();
        brew();
        pourInCup();
        add();
    }

    public abstract void brew();

    public abstract void add();

    public void boilWater() {
        System.out.println("Bring the water to a boil");
    }

    public void pourInCup() {
        System.out.println("Pour it into the cup");
    }
}

public class Tea extends Drink {

    public Tea() {}

    @Override
    public void brew() { System.out.println("Brew tea with boiling water"); }

    @Override
    public void add() { System.out.println("Add lemon"); }
}
When called Tea Medium prepare()Method is actually called Drink Class prepare()method
 here prepare()Method is a template method, which will be executed in turn boilWater() brew() pourInCup() add()
Because it is Tea Invoked prepare() Therefore, the abstract method in the template method is selected Tea Medium brew()and add()
Some steps are delayed to subclasses to decouple the function from the implementation

At the same time, it can also be added in the template method hook() To control the process of template methods, such as:
public abstract class Drink {

    public Drink() {}

    public final void prepare() {
        boilWater();
        brew();
        pourInCup();
        //Hook point
        if(wantAddSomething()) {
            add();
        }
    }

    public abstract void brew();

    public abstract void add();

    public void boilWater() { System.out.println("Bring the water to a boil"); }

    public void pourInCup() { System.out.println("Pour it into the cup"); }

    //hook can be overridden by subclasses to control template methods
    public boolean wantAddSomething() {
        return true;
    }
}
Subclasses can override hook(),That is, the above wantAddSomething()To control the flow of template methods
hook()It can affect the process of template methods and enable subclasses to control the trend of template methods


In Java Arrays. In util The sort () method can sort. For simple Integer type arrays, you can sort directly, but you can also let a class implement compareTo() in the Comparable interface to specify the sorting conditions you want. In this process, sort() can be regarded as a template method and compareTo() can be regarded as a link in the template method, The user implementation changes the result of the final template method execution

9 status mode

Status mode:
Allows an object to change its behavior when its internal state changes

When designing a candy machine, the candy machine has different states
 If no coin is inserted, coin has been inserted, shipment in progress, refund medium status
 If the design pattern is not adopted, it needs to be carried out continuously in multiple methods if-else Judge the current state, such as:
public void insertMoney() {
        if(this.state == NO_MONEY) {
            System.out.println("Coin paid");
            this.state = HAS_MONEY;
            System.out.println("Please click <purchase> or <refund> Button");
            return;
        } else if(this.state == HAS_MONEY) {
            this.state = BACK_MONEY;
            System.out.println("Please don't put in coins continuously. Your coins will be withdrawn immediately");
            backMoney();
            return;
        }
    }

Although it can be easily completed, once new requirements are added, such as winning requirements
 Most of the code of the whole machine has to be rewritten. The previous practice violates the opening and closing principle, and the system is not flexible and logical confusion

Now use the state pattern to design and define a State Interface, each state of the candy machine implements this State Interface
 Delegate the action of the candy machine to the state class
public class Machine {
    private State soldOutState;
    private State noMoneyState;
    private State hasMoneyState;
    private State soldState;

    private State state;
    private int count = 0;

    public Machine(int count) {
        this.soldOutState = new SoldOutState(this);
        this.noMoneyState = new NoMoneyState(this);
        this.hasMoneyState = new HasMoneyState(this);
        this.soldState = new SoldState(this);

        this.count = count;
        if(count > 0) {
            this.state = noMoneyState;
        }
}
   
// Status interface
public interface State {
    //coin-operated
    void insertMoney();

    //refund
    void ejectMoney();

    //Turn the crank
    void turnCrank();

    //Distribute candy
    void dispense();
}

// The state of the candy machine when there is no money to put into the machine
public class NoMoneyState implements State {
    private Machine machine;

    public NoMoneyState(Machine machine) {
        this.machine = machine;
    }

    @Override
    public void insertMoney() {
        System.out.println("You put in a coin");
        machine.setState(machine.getHasMoneyState());
    }

    @Override
    public void ejectMoney() {
        System.out.println("No coin has been inserted and cannot be refunded");
    }

    @Override
    public void turnCrank() {
        System.out.println("No coin has been inserted, unable to turn the crank");
    }

    @Override
    public void dispense() {
        System.out.println("No coin has been inserted, unable to get candy");
    }

}

This is a two-way combination process. Various state classes are combined into the candy machine, and the candy machine is combined into the state class
 Various behaviors of the candy machine are executed by the state class. After the state class works, it will call the candy machine to enter the next state

10 agent mode

Proxy mode:
Provide an "Avatar" for an object to control access to that object

The proxy pattern provides a substitute for a real object when a method in the proxy is called
 Proxy objects delegate processing to real objects, but before and after delegation
 More functions can be added, such as permission detection, security judgment and remote call...
Thus, the function of real objects is expanded non invasively and real objects are protected


There are many application scenarios for agents, such as
 Firewall agent: Control the access of network resources to avoid illegal operations
 Cache agent: It provides temporary storage for expensive calculation results, and allows multiple customers to share the results to reduce calculation
 Synchronization agent: Provide secure access to real objects in a multithreaded environment
...

2 design principles

2.1 distinguish between change and invariance

The change of one part of the system will not affect other parts, so as to reduce the change caused by code change and make the system more flexible

Find out where changes may be needed in the application and separate them
 Don't put it with code that doesn't need to change

2.2 multi use combination and less inheritance

Use inheritance as little as possible and treat interfaces as members to extend the functionality of classes

Inheritance can obtain the code of the parent class to complete code reuse
 But inheritance also compresses the space for subclass changes, and if the parent class wants to make changes
 Then all subclasses will be affected

2.3 loose coupling

The two objects are loosely coupled, and they can still interact, but they don't know each other's details

Loose coupling can make two objects unclear, not in each other's specific implementation
 It can also complete the data interaction and improve the flexibility of the system

2.4 opening and closing principle

Classes should be developed for extensions and closed for modifications

The program should be free from change and good at expansion
 On the basis of trying not to modify the source code, you can match the new behavior

2.5 Dependency Inversion Principle

When an object is instantiated directly, it depends on its specific implementation class. This dependence should be minimized

Do not let high-level components rely on low-level components, and both high-level and low-level components should rely on"abstract"
Parent classes and interfaces are abstractions

Like in a pizza shop, if you let PizzaStore Direct dependence on various CheesePizza,BeefPizza That is, high-level components depend on low-level components
 whole PizzaStore A large number of dependent implementation classes, so you can PizzaStore Dependent parent Pizza,Make all kinds of pizza Pizza Subclass of
 To follow the dependency inversion principle and separate the function from the creation object

2.6 principle of minimum knowledge

Don't couple too many classes together

Don't couple too many classes together, so as not to modify one part of the system and affect other parts
 If many classes depend on each other, the system is a fragile system and needs more energy to maintain

2.7 Hollywood principles

Don't call me, I'll call you

Hollywood principle can reduce the dependence between objects
 For example, in a bad system, high-level components rely on low-level components, and low-level components rely on side components
 The side components depend on low-level components, and the whole system is seriously coupled

The Hollywood principle allows low-level components to hook themselves to the system, and high-level components decide how and when to use these low-level components
 As in the template method Coffee and Drink In, execute coffee.prepare()Time
Drink It's a high-level component, Coffee It is a low-level component that executes coffee.prepare()When, yes Drink Called Coffee Methods in
 instead of Drink Depend on Coffee In order to reduce dependency

2.8 principle of single responsibility

Each class is only responsible for its own business, and a class is only responsible for one job

Following the principle of single responsibility can improve the readability of classes and the maintainability of the system
 When modifying a function, it can significantly reduce the impact on other functions. It is designed according to the function by module