[Interview Special Topic] Design Pattern ①

1.Factory design pattern

The factory design pattern is a creational pattern that provides an interface for creating objects, but the specific type of objects created can be determined at runtime. The factory design pattern mainly solves the problem of flexibility in creating objects.

Factory design patterns mainly include three types: simple factory pattern, factory method pattern and abstract factory pattern.

  1. Simple factory pattern: By specifically defining a class to be responsible for creating instances of other classes, the created instances usually have a common parent class. This model belongs to the innovative model of classes, also called the static factory method model. The simple factory model seriously violates the "open-close principle" and is difficult to expand.
  2. Factory method pattern: Define a factory interface that creates product objects, deferring the actual creation work to subclasses. The core factory class is no longer responsible for the creation of products. In this way, the core class becomes an abstract factory role and is only responsible for the interfaces that specific factory subclasses must implement. The advantage of this further abstraction is that the factory method pattern allows the system to introduce new products without modifying the specific factory role.
  3. Abstract Factory Pattern: Creates other factories around a super factory. The Gigafactory is also known as the factory of other factories.

Simple factory pattern:

//抽象产品
interface Product {
    void doSomething();
}
 
//具体产品1
class ConcreteProduct1 implements Product {
    @Override
    public void doSomething() {
        System.out.println("具体产品1");
    }
}
 
//具体产品2
class ConcreteProduct2 implements Product {
    @Override
    public void doSomething() {
        System.out.println("具体产品2");
    }
}
 
//工厂类
class Factory{
    public static Product createProduct(int type) {
        switch (type) {
            case 1:
                return new ConcreteProduct1();
            case 2:
                return new ConcreteProduct2();
            default:
                return null;
        }
    }
}
 
//测试类
public class Test {
    public static void main(String[] args) {
        Factory.createProduct(1).doSomething();//输出具体产品1
        Factory.createProduct(2).doSomething();//输出具体产品2
    }
}

Factory method pattern:

//抽象产品
interface Product{
    void doSomething();
}
 
//具体产品1
class ConcreteProduct1 implements Product{
    @Override
    public void doSomething() {
        System.out.println("具体产品1");
    }
}
 
//具体产品2
class ConcreteProduct2 implements Product{
    @Override
    public void doSomething() {
        System.out.println("具体产品2");
    }
}
 
//抽象工厂
interface Factory {
    Product createProduct();
}
 
//具体工厂1
class ConcreteFactory1 implements Factory{
    @Override
    public Product createProduct() {
        return new ConcreteProduct1();
    }
}
 
//具体工厂2
class ConcreteFactory2 implements Factory{
    @Override
    public Product createProduct() {
        return new ConcreteProduct2();
    }
}
 
//测试类
public class Test {
    public static void main(String[] args) {
        Factory factory1 = new ConcreteFactory1();
        factory1.createProduct().doSomething(); //输出具体产品1
        Factory factory2 = new ConcreteFactory2();
        factory2.createProduct().doSomething(); //输出具体产品2
    }
}

Abstract factory pattern:

//抽象产品A
interface ProductA{
    void doSomething();
}
 
//具体产品A1
class ConcreteProductA1 implements ProductA{
    @Override
    public void doSomething() {
        System.out.println("具体产品A1");
    }
}
 
//具体产品A2
class ConcreteProductA2 implements ProductA{
    @Override
    public void doSomething() {
        System.out.println("具体产品A2");
    }
}
 
//抽象产品B
interface ProductB{
    void doSomething();
}
 
//具体产品B1
class ConcreteProductB1 implements ProductB{
    @Override
    public void doSomething() {
        System.out.println("具体产品B1");
    }
}
 
//具体产品B2
class ConcreteProductB2 implements ProductB{
    @Override
    public void doSomething() {
        System.out.println("具体产品B2");
    }
}
 
//抽象工厂
interface AbstractFactory{
    ProductA createProductA();
    ProductB createProductB();
}
 
//具体工厂1
class ConcreteFactory1 implements AbstractFactory{
    @Override
    public ProductA createProductA() {
        return new ConcreteProductA1();
    }
    @Override
    public ProductB createProductB() {
        return new ConcreteProductB1();
    }
}
 
//具体工厂2
class ConcreteFactory2 implements AbstractFactory{
    @Override
    public ProductA createProductA() {
        return new ConcreteProductA2();
    }
    @Override
    public ProductB createProductB() {
        return new ConcreteProductB2();
    }
}
 
//测试类
public class Test {
    public static void main(String[] args) {
        AbstractFactory factory1 = new ConcreteFactory1();
        factory1.createProductA().doSomething(); //输出具体产品A1
        factory1.createProductB().doSomething(); //输出具体产品B1
        AbstractFactory factory2 = new ConcreteFactory2();
        factory2.createProductA().doSomething(); //输出具体产品A2
        factory2.createProductB().doSomething(); //输出具体产品B2
    }
}

2. Strategy mode

Strategy pattern is a behavioral design pattern that allows the behavior of an algorithm to be selected at runtime. In Java, the strategy pattern can be implemented through interfaces and abstract classes. The following is a simple example showing how the Strategy pattern should be written in Java.

First, define an interface that will define the methods of the policy algorithm.

public interface Strategy {
    int execute(int num1, int num2);
}

Next, create different policy classes that implement this interface.

public class Add implements Strategy {
    public int execute(int num1, int num2) {
        return num1 + num2;
    }
}

public class Subtract implements Strategy {
    public int execute(int num1, int num2) {
        return num1 - num2;
    }
}

public class Multiply implements Strategy {
    public int execute(int num1, int num2) {
        return num1 * num2;
    }
}

Then, in the main program, create a Context class that uses the specified strategy to execute the algorithm.

public class Context {

    private Strategy strategy;

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

    public int executeStrategy(int num1, int num2) {
        return strategy.execute(num1, num2);
    }
}

Finally, instantiate the different strategies and pass them to the Context class.

public class StrategyPatternExample {

    public static void main(String[] args) {
        Context context = new Context(new Add());
        System.out.println("10 + 5 = " + context.executeStrategy(10, 5));

        context = new Context(new Subtract());
        System.out.println("10 - 5 = " + context.executeStrategy(10, 5));

        context = new Context(new Multiply());
        System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
    }
}

Output:

10 + 5 = 15
10 - 5 = 5
10 * 5 = 50

This example demonstrates how to implement the Strategy pattern, which allows the behavior of an algorithm to be chosen at runtime.

3. Strategy mode + factory mode to achieve login

The factory method pattern is a creational pattern that delegates object creation to a factory class, which is responsible for creating specific object instances. The strategy pattern is a behavioral pattern that defines a series of algorithms and encapsulates each algorithm so that they are interchangeable.

Through the factory method pattern, we can create the corresponding policy object based on the input parameters, and then implement the login function through the policy object. The specific implementation is as follows:

1. Create a policy interface and define the login method.

public interface LoginStrategy {
    boolean login(String username, String password);
}

2. Create a specific policy implementation class and implement the login method.

public class EmailLoginStrategy implements LoginStrategy {
    @Override
    public boolean login(String username, String password) {
        // 基于邮箱的登录逻辑
        return true;
    }
}

public class PhoneLoginStrategy implements LoginStrategy {
    @Override
    public boolean login(String username, String password) {
        // 基于手机号的登录逻辑
        return true;
    }
}

public class UsernameLoginStrategy implements LoginStrategy {
    @Override
    public boolean login(String username, String password) {
        // 基于用户名的登录逻辑
        return true;
    }
}

3. Create a factory interface and define methods for creating policy objects.

public interface LoginStrategyFactory {
    LoginStrategy createLoginStrategy(String type);
}

4. Create a specific factory implementation class and create the corresponding strategy object based on the input parameters.

public class LoginStrategyFactoryImpl implements LoginStrategyFactory {
    @Override
    public LoginStrategy createLoginStrategy(String type) {
        switch (type) {
            case "email":
                return new EmailLoginStrategy();
            case "phone":
                return new PhoneLoginStrategy();
            case "username":
                return new UsernameLoginStrategy();
            default:
                return null;
        }
    }
}

5. In the final login class, call the factory method to create the corresponding policy object and call the login method.

public class Login {
    public boolean login(String type, String username, String password) {
        LoginStrategyFactory factory = new LoginStrategyFactoryImpl();
        LoginStrategy strategy = factory.createLoginStrategy(type);
        return strategy.login(username, password);
    }
}

In this way, we can dynamically create the corresponding policy object based on the input parameters to implement the login function. And when you need to add a new login method, you only need to add a new policy class and factory method.

4. Chain of Responsibility Model

The chain of responsibility pattern can be used to connect multiple objects that handle requests to form a processing chain, and pass the request along this chain until an object is able to handle the request, thereby achieving the purpose of request processing and decoupling. The following is a simple example to illustrate how to implement the chain of responsibility design pattern in Java.

abstract class Handler {
     protected Handler handler;

     void setNext(Handler handler){
         this.handler = handler;

     }

    public abstract void process(OrderInfo order);


}

import java.math.BigDecimal;

public class OrderInfo {
    private String  productId;
    private String userId;
    private BigDecimal amount;


    public String getProductId() {
        return productId;
    }

    public void setProductId(String productId) {
        this.productId = productId;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public BigDecimal getAmount() {
        return amount;
    }

    public void setAmount(BigDecimal amount) {
        this.amount = amount;
    }
}
public class OrderValidition extends Handler {

    @Override
    public void process(OrderInfo order) {
        System.out.println("OrderValidition--------");
        handler.process(order);
    }
}
public class OrderFill extends Handler{
    @Override
    public void process(OrderInfo order) {
        System.out.println("OrderFill----");
        handler.process(order);
    }
}
public class OderAmountCalcuate extends Handler {
    @Override
    public void process(OrderInfo order) {
        System.out.println("OderAmountCalcuate----");
        handler.process(order);
    }
}
public class OderCreate extends Handler {

    @Override
    public void process(OrderInfo order) {
        System.out.println("OderCreate ----");

    }
}
public class Client {
    public static void main(String[] args) {

        OrderValidition orderValidition = new OrderValidition();
        OrderFill orderFill = new OrderFill();
        OderAmountCalcuate oderAmountCalcuate = new OderAmountCalcuate();
        OderCreate oderCreate = new OderCreate();
        
        orderValidition.setNext(orderFill);
        orderFill.setNext(oderAmountCalcuate);
        oderAmountCalcuate.setNext(oderCreate);

        orderValidition.process(new OrderInfo());


    }
}
OrderValidition--------
OrderFill----
OderAmountCalcuate----
OderCreate ----

5. Singleton mode

Recommended video:[Single-case Mode] The macho man was mercilessly ridiculed by the interviewer because he did not understand the single-case mode_bilibili_bilibili

The singleton pattern is a creational design pattern that ensures that only one instance of a class exists and provides a global access point. The main idea is that a class only allows the creation of one object (or instance) and provides a global access point to that object.

Application scenarios of the singleton pattern include:

  1. Globally unique configuration manager.

  2. Globally unique state manager.

  3. Database connection pool.

  4. Multithread pool.

  5. Globally unique logger.

  6. Resource managers with special restrictions or uniqueness requirements.

There are many ways to implement the singleton pattern, including hungry singletons, lazy singletons, double check lock singletons, static inner class singletons, etc. Among them, the hungry man style and the lazy man style are the two most basic implementation methods.

1. Hungry-style singleton mode

The Hungry-style singleton mode creates an instance when the class is loaded. There is no thread safety issue, but it will affect performance because the instance will always occupy memory even if it is not needed.

public class Singleton {
    // 静态实例,类加载时即创建
    private static Singleton instance = new Singleton();
    // 私有构造方法,防止外部创建实例
    private Singleton() {}
    // 全局访问方法
    public static Singleton getInstance() {
        return instance;
    }
}

2. Lazy Singleton Pattern

The lazy singleton mode is created when the instance is accessed for the first time, but it has thread safety issues and needs to be locked.

public class Singleton {
    // 私有静态实例,延迟加载
    private static Singleton instance = null;
    // 私有构造方法,防止外部创建实例
    private Singleton() {}
    // 全局访问方法,加锁保证线程安全
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
3. Double verification lock singleton

Double check lock singleton is a commonly used singleton mode implementation method, which not only ensures thread safety, but also improves efficiency. The following is the code to implement double check lock singleton in Java:

public class Singleton {
    // volatile修饰的变量在多线程环境下保证可见性和有序性
    private volatile static Singleton instance;
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                // 双重校验锁,第一个if判断为了避免不必要的同步,第二个if保证同步情况下只有一个instance被创建
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

In the above code, the instance variable is modified with the volatile keyword to ensure that the instance The read and write operations are all visible to avoid the situation where thread A modifies the instance variable value but thread B is not visible.

In thegetInstance() method, the firstif judgment is made to prevent multiple threads from entering the synchronized code block at the same time. This in turn causes a waste of system resources. The second if ensures that only one instance is created in the synchronized code block to avoid thread safety issues.

In short, using double check lock singleton can ensure thread safety and efficiency, and is a commonly used singleton mode implementation method.

Guess you like

Origin blog.csdn.net/Javascript_tsj/article/details/134156101