Common design patterns

Posted by ben2005 on Tue, 21 Dec 2021 23:25:27 +0100


Learning address: Dark horse design pattern

1. Six design principles of design mode

1.1 principle of single responsibility

  1. The responsibility of a class should be single, and a method does only one thing. Try to change for only one reason.

1.2 Richter's replacement principle

  1. All references to the base class must be able to be replaced directly with its subclasses. (closely related to object-oriented inheritance).

1.3. Dependency Inversion Principle

  1. Interface oriented programming, which is one of the essence of object-oriented design, can reduce the coupling between classes, improve the stability of the system and reduce the risk caused by parallel development.
  2. Specific performance:
    1. Dependencies between modules occur through interfaces, and there is no direct dependency between implementation classes.
    2. The interface or abstract class does not depend on the concrete implementation class.
    3. Implementation classes depend on interfaces or implementation classes.

1.4 interface isolation principle

  1. The client should not rely on interfaces that it does not need (in this case, interfaces refer to classes and interfaces).
  2. Establish a single interface, not a bulky interface.

1.5. Demeter's law

  1. One object should have the least understanding of other objects. It puts forward clear requirements for low coupling of classes.
  2. If two software entities do not need to communicate directly, they should not call each other directly, and the call can be forwarded through a third party. The purpose is to reduce the coupling between classes and improve the relative independence of modules.
  3. Friend: the current object itself, the member object of the current object, the object created by the current object, the method parameters of the current object, etc.
  4. For example: the teacher asks the monitor to call the roll. At this time, the teacher and the monitor are coupled, and should not be coupled with the students.

1.6 opening and closing principle

  1. It is open to extensions and closed to modifications. Objective: to make the program scalable, easy to maintain and upgrade.

2. Singleton mode

  1. This class is responsible for creating its own objects while ensuring that only a single object is created.

2.1 hungry Han style

  1. Classic hungry Chinese style
public class Singleton {

    //1. Private construction method
    private Singleton() {}

    //2. Create this class object in this class
    private static Singleton instance = new Singleton();

    //3. Provide a public access method for the outside world to obtain the object
    public static Singleton getInstance() {
        return instance;
    }
}
  1. Static code block
public class Singleton {

    //Private construction method
    private Singleton() {}

    //Declare a variable of type Singleton
    private static Singleton instance; //null

    //Assignment in static code block
    static {
        instance = new Singleton();
    }

    //Provide external methods to obtain this kind of object
    public static Singleton getInstance() {
        return instance;
    }
}

2.2. Lazy type

  1. Synchronization method
public class Singleton {

    //Private construction method
    private Singleton() {}

    //Declare variable instance of type Singleton
    private static Singleton instance; //It just declares a variable of this type without assignment

    //Provide external access
    public static synchronized Singleton getInstance() {
        //Judge whether instance is null. If it is null, it indicates that the Singleton class object has not been created
        //If not, create one and return. If yes, return directly
        if(instance == null) {
            //Thread 1 waits, and thread 2 obtains the execution right of the cpu and enters the judgment
            instance = new Singleton();
        }
        return instance;
    }
}
  1. DCL using volatile
public class Singleton {

    //Private construction method
    private Singleton() {}

    //Declare a variable of type Singleton
    private static volatile Singleton instance;

    //Provide public access
    public static Singleton getInstance() {
        //In the first judgment, if the value of instance is not null, there is no need to preempt the lock, and the object is returned directly
        if(instance == null) {
            synchronized (Singleton.class) {//Because it is static, the current lock is a CLass object
                //Second judgment
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }

        return instance;
    }
}

2.3 enumeration

public enum Singleton {
    INSTANCE;
}
public class Client {
    public static void main(String[] args) {
        Singleton instance = Singleton.INSTANCE;
        Singleton instance1 = Singleton.INSTANCE;

        System.out.println(instance == instance1);
    }
}

2.4 advantages

  1. Ensure that all objects access an instance.
  2. It has certain scalability.
  3. Avoid multiple occupation of shared resources.

2.5 disadvantages

  1. The singleton pattern has no abstraction layer, so it is very difficult to extend.
  2. The singleton mode violates the principle of single responsibility to a certain extent.
  3. Not applicable to changing objects.

2.6 application

  1. Database connection pool
  2. Thread pool

3. Factory mode

  1. Cause: violation of the opening and closing principle. If you create a new object directly, the object will be seriously coupled. If you want to replace the object, all new objects need to be modified.

3.1. Simple factory mode

  1. It is not a design pattern, but more like a programming habit.
  2. Method: separate the method of the original new object into a class, so that when you really need to create an object, you can call the method directly, reducing the coupling degree.
  3. Structure:
    1. Abstract product: it defines the product specification and describes the main characteristics and functions of the product.
    2. Concrete products: subclasses that implement or inherit Abstract products.
    3. Specific factory: provides a method to create a product, through which the caller obtains the product.
  4. Class diagram:

3.1. 1. Advantages

  1. It reduces the possibility of code modification and is easier to expand.

3.1. 2. Shortcomings

  1. Violation of opening and closing principle

3.2 factory method mode

  1. Define an interface for creating objects, and let subclasses decide which product class object to instantiate.
  2. It is a good application of polymorphism.
  3. Structure:
    1. Abstract Factory: it provides an interface for creating products, through which callers access factory methods of specific factories to create products.
    2. Concrete factory: it mainly implements the abstract methods in the abstract factory and completes the creation of specific products.
    3. Abstract Product: it defines the specification of the Product and describes the main characteristics and functions of the Product.
    4. Concrete product: it implements the interface defined by the abstract product role, which is created by the specific factory and corresponds to the specific factory one by one.
  4. Class diagram:
  • Abstract factory
public interface CoffeeFactory {

    //Method of creating coffee object
    Coffee createCoffee();
}
  • Specific factories create specific objects
public class AmericanCoffeeFactory implements CoffeeFactory {

    public Coffee createCoffee() {
        return new AmericanCoffee();
    }
}
public class LatteCoffeeFactory implements CoffeeFactory {

    public Coffee createCoffee() {
        return new LatteCoffee();
    }
}
  • abstract class
public abstract class Coffee {

    public abstract String getName();

    //Add sugar
    public void addsugar() {
        System.out.println("Add sugar");
    }

    //Add milk
    public void addMilk() {
        System.out.println("Add milk");
    }
}
  • Concrete object (through inheritance)
public class AmericanCoffee extends Coffee {

    public String getName() {
        return "Cafe Americano";
    }
}
public class LatteCoffee extends Coffee {

    public String getName() {
        return "Cafe Latte";
    }
}

  • Coffee shops produce coffee
public class CoffeeStore {

    //Private factory object
    private CoffeeFactory factory;

    //Get objects according to specific factories
    public void setFactory(CoffeeFactory factory) {
        this.factory = factory;
    }

    //Order coffee function
    public Coffee orderCoffee() {
        Coffee coffee = factory.createCoffee();
        //Add ingredients
        coffee.addMilk();
        coffee.addsugar();
        return coffee;
    }
}

  • test
public class Client {
    public static void main(String[] args) {
        //Create coffee shop object
        CoffeeStore store = new CoffeeStore();
        //create object
        //CoffeeFactory factory = new AmericanCoffeeFactory();
        CoffeeFactory factory = new LatteCoffeeFactory();
        store.setFactory(factory);

        //Order coffee
        Coffee coffee = store.orderCoffee();

        System.out.println(coffee.getName());
    }
}

3.2. 1. Advantages

  1. Users only need to know the name of the specific factory to get the desired product, without knowing the specific creation process of the product;
  2. When adding new products to the system, only the specific product category and the corresponding specific factory category need to be added without any modification to the original factory, which meets the opening and closing principle;

3.2. 2. Shortcomings

  1. Every time a product is added, a specific product class and a corresponding specific factory class are added, which increases the complexity of the system.

3.3 abstract factory mode

  1. The factory method model only considers the production of similar products, but in reality, many factories are comprehensive factories.
  2. It is a pattern structure that provides an interface for the access class to create a group of related or interdependent objects, and the access class can obtain different levels of products of the same family without specifying the specific class of the product.
  3. Abstract factory pattern is an upgraded version of factory method pattern. Factory method pattern only produces one level of products, while abstract factory pattern can produce multiple levels of products.
  4. Structure:
    1. Abstract Factory: it provides an interface for creating products. It contains multiple methods for creating products, and can create multiple products of different levels.
    2. Concrete Factory: it mainly implements multiple abstract methods in the abstract factory to complete the creation of specific products.
    3. Abstract Product: it defines the Product specification and describes the main features and functions of the Product. The abstract factory pattern has multiple Abstract products.
    4. Concrete product: it implements the interface defined by the abstract product role and is created by the concrete factory. It has a many-to-one relationship with the concrete factory.
  5. At present, the business of coffee shops has changed, not only to produce coffee, but also to produce desserts, such as tiramisu and Matcha mousse. If you need to define tiramisu, Matcha mousse, tiramisu factory, Matcha mousse factory and dessert factory according to the factory method mode, it is easy to explode. Latte and American coffee are one product grade, both of which are coffee; Tiramisu and Matcha mousse are also a product grade; Latte and tiramisu belong to the same product family (i.e. both belong to Italian flavor), American coffee and Matcha mousse belong to the same product family (i.e. both belong to American flavor). Then it conforms to the abstract factory mode. Class diagram:

  • Products of the same level (coffee and dessert)
public abstract class Coffee {

    public abstract String getName();

    //Add sugar
    public void addsugar() {
        System.out.println("Add sugar");
    }

    //Add milk
    public void addMilk() {
        System.out.println("Add milk");
    }
}
public abstract class Dessert {

    public abstract void show();
}
  • Specific object
public class AmericanCoffee extends Coffee {

    public String getName() {
        return "Cafe Americano";
    }
}

public class LatteCoffee extends Coffee {

    public String getName() {
        return "Cafe Latte";
    }
}

public class MatchaMousse extends Dessert {
    public void show() {
        System.out.println("Matcha Mousse");
    }
}

public class Trimisu extends Dessert {
    public void show() {
        System.out.println("Tiramisu");
    }
}
  • Same product family (Italian flavor, American flavor)
public class ItalyDessertFactory implements DessertFactory {

    public Coffee createCoffee() {
        return new LatteCoffee();
    }

    public Dessert createDessert() {
        return new Trimisu();
    }
}
public class AmericanDessertFactory implements DessertFactory {

    public Coffee createCoffee() {
        return new AmericanCoffee();
    }

    public Dessert createDessert() {
        return new MatchaMousse();
    }
}

  • Test class
public class Client {
    public static void main(String[] args) {
        //Created is an Italian dessert factory object
        ItalyDessertFactory factory = new ItalyDessertFactory();
//        AmericanDessertFactory factory = new AmericanDessertFactory();
        //Get latte and tiramisu desserts
        Coffee coffee = factory.createCoffee();
        Dessert dessert = factory.createDessert();

        System.out.println(coffee.getName());
        dessert.show();
    }
}

3.3. 1. Advantages

  1. When multiple objects in a product family are designed to work together, it can ensure that the client always uses only the objects in the same product family.

3.3. 2. Shortcomings

  1. When a new product needs to be added to the product family, all factory classes need to be modified.

3.4 difference between factory method pattern and abstract factory pattern

  1. The key of abstract factory is the abstract relationship between products, which requires at least two products; The factory method is to generate products without paying attention to the relationship between products. You can only generate one product.

4. Agent mode

  1. For some reason, an object needs to be provided with a proxy to control access to the object. At this time, the access object is not suitable or can not directly reference the target object. The proxy object acts as an intermediary between the access object and the target object.
  2. Agents in Java can be divided into static agents and dynamic agents according to the generation time of agent classes. Static proxy classes are generated at compile time, while dynamic proxy classes are generated dynamically at Java runtime. Dynamic agents include JDK agent and CGLib agent.
  3. Structure:
    1. Abstract Subject class: business methods implemented by declaring real subjects and proxy objects through interfaces or abstract classes.
    2. Real Subject class: it implements the specific business in the abstract subject. It is the real object represented by the proxy object and the object to be referenced finally.
    3. Proxy class: it provides the same interface as the real topic. It contains references to the real topic. It can access, control or extend the functions of the real topic.

4.1 static agent

Train station ticket selling

If you want to buy a train ticket, you need to go to the railway station to buy a ticket, take a bus to the railway station, queue up and a series of operations, which is obviously more troublesome. The railway station has consignment points in many places. It's much more convenient for us to buy tickets at the consignment point. This example is actually a typical agency model. The railway station is the target object and the consignment point is the agent object. Class diagram is as follows:

Using composition, take the real class as its own attribute in the proxy class. Method is also executed by calling the real class. During the test, the proxy object is directly accessed, that is, the proxy class acts as the intermediary between the access object and the target object, and the method is enhanced.

//Ticket selling interface
public interface SellTickets {
    void sell();
}

//The railway station has the function of selling tickets, so the SellTickets interface needs to be implemented
public class TrainStation implements SellTickets {

    public void sell() {
        System.out.println("Train station ticket");
    }
}

//Consignment point
public class ProxyPoint implements SellTickets {

    private TrainStation station = new TrainStation();

    public void sell() {
        System.out.println("The agency charges some service fees");
        station.sell();
    }
}

//Test class
public class Client {
    public static void main(String[] args) {
        ProxyPoint pp = new ProxyPoint();
        pp.sell();
    }
}

4.2 dynamic agent

  1. Dynamic proxy does not care about the proxy class in the implementation stage, but specifies which object in the run stage.
  2. Interface:
public interface UserDao {
    int add(int a,int b);
}
  1. Interface implementation class: (class to be enhanced)
public class UserDaoImpl implements UserDao {
    @Override
    public int add(int a, int b) {
        System.out.println("add Method is executed.....");
        return a+b;
    }
}
  1. Create proxy object
//Create proxy object code
class UserDaoProxy implements InvocationHandler {

    //1. Who is the proxy object created and who is passed over
    //Parametric construction transfer
    private Object obj;
    public UserDaoProxy(Object obj) {
        this.obj = obj;
    }

    //Enhanced logic
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //Before method
        System.out.println("Method...."+method.getName()+" :Parameters passed..."+ Arrays.toString(args));

        //Enhanced method execution
        Object res = method.invoke(obj, args);

        //After method
        System.out.println("Method...."+obj);
        return res;
    }
}
  1. test
public class JDKProxy {

    public static void main(String[] args) {
        //Create interface implementation class proxy object
        Class[] interfaces = {UserDao.class};
        UserDaoImpl userDao = new UserDaoImpl();
        UserDao dao = (UserDao)Proxy.newProxyInstance(UserDao.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
        int result = dao.add(1, 2);
        System.out.println("result:"+result);
    }
}

4.2. 1. Summary

  1. First, you need an interface and the implementation class of the interface (the class that needs to be enhanced).
  2. Then create a proxy object (write a class). The proxy object should implement the InvocationHandler interface and the invoke method (enhanced specific logic). In the class, transfer the proxy object of which interface is created and set it by injection.
  3. Finally, when testing, proxy must be called Newproxyinstance (classloader, loader, class <? > [] interfaces, invocationhandler h). The first parameter value refers to the implementation class (or write interface) loader of which interface; the second parameter is the list of interfaces to be implemented by the proxy class (Note: interface. Class is an interface list); the third parameter is the proxy object.

5. Decorator mode

Five minute learning design mode - decorator mode

  1. It refers to the mode of dynamically adding some responsibilities (i.e. adding additional functions) to the object without changing the existing object structure.
  2. Structure:
    1. Abstract Component role: define an abstract interface to standardize objects ready to receive additional responsibilities.
    2. Concrete Component role: implement abstract components and add some responsibilities to them by decorating roles.
    3. Abstract Decorator role: inherits or implements abstract components and contains instances of specific components. The functions of specific components can be extended through their subclasses.
    4. Concrete decorator role: implement relevant methods of abstract decoration and add additional responsibilities to specific component objects.

Class diagram:

3. For example: there is a product with few functions. If the manufacturer mentions the second generation, the second generation will inherit the functions of the first generation. At this time, you add a shell to the first generation and use the shell to complete the desired functions. The shell is a decorator and keeps the functions of the first generation products unchanged.
4. In the JDK source code, the decorator mode is widely used in IO streams.
5. Code example:

  • Interface:
public interface Robot {
    void doSomething();
}
  • Early generation robot
public class FirstRobot implements Robot{
    public void doSomething() {
        System.out.println("sing");
        System.out.println("move");
    }
}
  • Second generation robot
public class RobotDecorator implements Robot{
    private Robot robot;

    public RobotDecorator(Robot robot) {
        this.robot = robot;
    }

    public void doSomething() {
        robot.doSomething();
    }
    public void doMoreThing(){
        robot.doSomething();
        System.out.println("dance");
    }
}
  • test
public class DecoratorPattern {
    public static void main(String[] args) {
        RobotDecorator robotDecorator = new RobotDecorator(new FirstRobot());
        robotDecorator.doMoreThing();
    }
}

5.1 advantages

  1. Decorative classes and decorated classes can develop independently and will not be coupled with each other. Decorative pattern is an alternative pattern of inheritance. Decorative pattern can dynamically expand the functions of an implementation class.

5.2 disadvantages

  1. It needs to write more code and generate more classes, which increases the complexity of the program.

5.3 difference between static agent and decorator

5.3. 1. Same point

  1. Must implement the same business interface as the target class
  2. Declare the target object in both classes
  3. Can enhance the target method without modifying the target class

5.3. 2. Different points

  1. Different purposes: the decorator is to enhance the target object; Static proxy is to protect and hide the target object
  2. The construction of the target object is different: the decorator is passed in from the outside and can be passed through the construction method; Static proxy is created inside the proxy class to hide the target object

6. Strategy mode

  1. A series of algorithms are defined, each algorithm is encapsulated, and they can be converted to each other, and the change of the algorithm will not affect the users using the algorithm. The policy pattern belongs to the object behavior pattern. It separates the responsibility of using the algorithm from the implementation of the algorithm by encapsulating the algorithm, and delegates it to different objects to manage these algorithms
  2. The policy pattern allows the algorithm to change independently of the customers using it, also known as the policy pattern.
  3. Structure:
    1. Abstract Strategy class: This is an abstract role, usually implemented by an interface or abstract class. This role gives the interfaces required by all specific policy classes.
    2. Concrete Strategy class: implements the interface of abstract policy definition and provides specific algorithm implementation or behavior.
    3. Context class: holds a reference to a policy class, which is finally called to the client.
  4. For example: the IDE we use when writing code can choose to use idea, eclipse, etc. These ides are a specific algorithm.
  5. Case: [shopping mall promotion]

Common interface for promotional activities (Abstract policy class)

public interface Strategy {

    void show();
}

Specific algorithm

public class StrategyA implements Strategy {

    public void show() {
        System.out.println("Buy 1 Get 1 FREE");
    }
}
public class StrategyB implements Strategy {

    public void show() {
        System.out.println("50 yuan less than 200 yuan");
    }
}
public class StrategyC implements Strategy {

    public void show() {
        System.out.println("Over 1000 yuan plus 1 yuan for any commodity below 200 yuan");
    }
}

Promoter (environment class): set the policy object as one of its own properties

public class SalesMan {

    //Aggregate policy class object
    private Strategy strategy;

    public SalesMan(Strategy strategy) {
        this.strategy = strategy;
    }

    public Strategy getStrategy() {
        return strategy;
    }

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

    //Promoters show promotional activities to users
    public void salesManShow() {
        strategy.show();
    }
}

Test class

public class Client {
    public static void main(String[] args) {
        //Spring Festival is coming. Use Spring Festival promotions
        SalesMan salesMan = new SalesMan(new StrategyA());
        //Display promotional activities
        salesMan.salesManShow();

        System.out.println("==============");
        //The Mid Autumn Festival is coming. Use the promotional activities of the Mid Autumn Festival
        salesMan.setStrategy(new StrategyB());
        //Display promotional activities
        salesMan.salesManShow();

        System.out.println("==============");
        //Christmas is coming. Use Christmas promotions
        salesMan.setStrategy(new StrategyC());
        //Display promotional activities
        salesMan.salesManShow();
    }
}

6.1 advantages

  1. Policy classes can be switched freely. (since all policy classes implement the same interface, they can switch freely.)
  2. Easy to expand. (to add a new policy, you only need to add a specific policy class, basically do not need to change the original code, and comply with the "opening and closing principle")
  3. Avoid using multiple conditional selection statements (if else) and fully reflect the idea of object-oriented design.

6.2 disadvantages

  1. The client must know all policy classes and decide which policy class to use.
  2. The policy pattern will result in many policy classes. You can reduce the number of objects to a certain extent by using the meta pattern.

7. Observer mode

  1. Define a one to many dependency between objects, so that whenever an object state changes, its related dependent objects are notified and automatically updated.
  2. Also known as publish subscribe mode.
  3. Example 1: official account of WeChat.

Abstract theme role classes (official account interface)

public interface Subject {
    //Add subscriber (add observer object)
    void attach(Observer observer);

    //Delete subscriber
    void detach(Observer observer);

    //Notify subscribers of update messages
    void notify(String message);
}

Abstract Observer class

public interface Observer {

    void update(String message);
}

Specific theme role classes (subscriber's official account)

public class SubscriptionSubject implements Subject {

    //Define a collection to store multiple observer objects
    private List<Observer> weiXinUserList = new ArrayList<Observer>();

    public void attach(Observer observer) {
        weiXinUserList.add(observer);
    }

    public void detach(Observer observer) {
        weiXinUserList.remove(observer);
    }

    public void notify(String message) {
        //Traversal set
        for (Observer observer : weiXinUserList) {
            //Call the update method in the observer object
            observer.update(message);
        }
    }
}

Specific observer role classes (per user)

public class WeiXinUser implements Observer {

    private String name;

    public WeiXinUser(String name) {
        this.name = name;
    }

    public void update(String message) {
        System.out.println(name + "-" + message);
    }
}

Test class

public class Client {
    public static void main(String[] args) {
        //1, create official account number.
        SubscriptionSubject subject = new SubscriptionSubject();

        //2, subscribe to official account.
        subject.attach(new WeiXinUser("Sun WuKong"));
        subject.attach(new WeiXinUser("Pig Wuneng"));
        subject.attach(new WeiXinUser("Sha Wujing"));

        //3, the official account is updated to send messages to subscribers (observer objects).
        subject.notify("The column of Chuanzhi dark horse has been updated!");
    }
}

Example 2: [repayment of debts]
Interface for borrowing money from others:

public interface Debit {
    void borrow(Credit credit);//borrow money
    void notifyCredits();//Notice to pay back
}

Interface for lending money

public interface Credit {
    void takeMoney();
}

Who specifically lends money to

public class Wangwu implements Credit{
    public void takeMoney() {
        System.out.println("Wang Wu wants money");
    }
}
public class Zhaosi implements Credit{
    public void takeMoney() {
        System.out.println("Zhao Si wants money");
    }
}

Specific object of borrowing money

public class Zhangsan implements Debit{
    private List<Credit> allCredits = new ArrayList<Credit>();
    private Integer state = 0;//The change position of the state, 1 indicates money
    public void borrow(Credit credit) {
        allCredits.add(credit);
    }

    public void notifyCredits(Integer state) {
        this.state = state;
        allCredits.forEach(credit -> credit.takeMoney());

    }

}

Test class

public class ObserverPattern {
    public static void main(String[] args) {
        Debit zhangsan = new Zhangsan();
        zhangsan.borrow(new Wangwu());
        zhangsan.borrow(new Zhaosi());
        //State change
        zhangsan.notifyCredits(8);
    }
}

Topics: Java Design Pattern Singleton pattern