Five common design patterns

https://www.runoob.com/design-pattern/factory-pattern.html

Singleton pattern

**Intent:** Ensure that a class has only one instance and provide a global access point to it.
**Main solution:** A globally used class is frequently created and destroyed.
**When to use:** When you want to control the number of instances and save system resources.
**How ​​to solve:** Determine whether the system already has this singleton, if so, return it, if not, create it.
**Key code:** The constructor is private.
Application examples:
1. There is only one class teacher in a class.
2. Windows is multi-process and multi-threaded. When operating a file, it is inevitable that multiple processes or threads will operate a file at the same time. Therefore, all files must be processed through The only instance to carry out.
Usage scenarios:

  • 1. Require the production of a unique serial number.
  • 2. The counter in the WEB does not need to be added to the database every time it is refreshed. It is cached first with a single instance.
  • 3. Creating an object consumes too many resources, such as I/O and database connections, etc.

Advantages:
1. There is only one instance in the memory, which reduces memory overhead, especially when instances are frequently created and destroyed (such as the School of Management homepage page cache).
2. Avoid multiple occupation of resources (such as file writing operations).
Disadvantages: No interface, no inheritance, conflicts with the single responsibility principle. A class should only care about internal logic, not how to instantiate it outside.
Note:
1. A singleton class can only have one instance.
2. The singleton class must create its own unique instance.
3. The singleton class must provide this instance to all other objects.

accomplish

Hungry mode

When the class is loaded, an instance is created.

public class SingletonHungry {
    
    
    //static 修饰成员变量,全局只有一个
    private static SingletonHungry instance = new SingletonHungry();
    //构造方法私有化,使类对象只有一个
     private SingletonHungry() {
    
    }
//    对外提供一个获取获取实例对象的方法
//    用static修饰方法
    public static SingletonHungry getInstance(){
    
    
        return instance;
    }
}

Lazy mode-single-threaded version

An instance is not created when a class is loaded. An instance is created the first time it is used.

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

Lazy mode-multi-threaded version

The implementation of the lazy mode above is thread-unsafe.
The thread-safety problem occurs when the instance is first created. If the getInstance method is called simultaneously in multiple threads, it may cause Create multiple instances.
Once the instance has been created, there will no longer be thread safety issues when calling getInstance in a multi-threaded environment (the instance will no longer be modified)
Adding synchronized can improve the thread safety issue here.

public class SingletonLazy2 {
    
    
    private static SingletonLazy2 instance = null;
    private SingletonLazy2() {
    
    }

//    以下两种方法都可以
//    在获取成员变量时,先判断锁是否被占用
//
//    其实synchronized代码块只需要执行一次就够了,以现在的写法,只要调用了getInstance方法,都要竞争锁,锁竞争是非常耗费系统资源的
//    使用了synchronized就从用户态转到了内核态
    public static synchronized SingletonLazy2 getInstance() {
    
    
        if (instance == null) {
    
    
//            初始化过程只执行一次
                instance = new SingletonLazy2();
        }
        return instance;
    }

    public static  SingletonLazy2 getInstance1() {
    
    
        synchronized(SingletonLazy2.class) {
    
    
            if (instance == null) {
    
    
                instance = new SingletonLazy2();
            }
            return instance;
        }
    }

//    错误的!!!!!!!!!!!!
//    public static SingletonLazy2 getInstance() {
    
    
//        if (instance == null) {
    
    
//    此时已经判断instance为空,争抢锁之后就会创建一个新的实例对象
//            synchronized (SingletonLazy2.class){
    
    
//                instance = new SingletonLazy2();
//            }
//        }
//        return instance;
//    }

}

Lazy mode-multi-threaded version (improved)

The following code has been further modified based on locking:

  • Use double if judgments to reduce the frequency of lock competition.
  • Added volatile to instance.
/**
 * 使用双重 if 判定, 降低锁竞争的频率.
 * 给 instance 加上了 volatile.
 *
 * 加锁 / 解锁是一件开销比较高的事情. 而懒汉模式的线程不安全只是发生在首次创建实例的时候.
 * 因此后续使用的时候, 不必再进行加锁了.
 * 外层的 if 就是判定下看当前是否已经把 instance 实例创建出来了.
 * 同时为了避免 "内存可见性" 导致读取的 instance 出现偏差, 于是补充上 volatile .
 * 当多线程首次调用 getInstance, 大家可能都发现 instance 为 null, 于是又继续往下执行来竞争锁,
 * 其中竞争成功的线程, 再完成创建实例的操作.
 * 当这个实例创建完了之后, 其他竞争到锁的线程就被里层 if 挡住了. 也就不会继续创建其他实例.
 */
//双重检查锁 DCL
public class SingletonDCL {
    
    

    //synchronized只能保证原子性和可见性,不能保证有序性(其他线程可能得到一个创建了对象(instance != null),但没有得到某些数据初始化的对象)
    //加上volatile保证有序性(可见性与有序性)
    private volatile static SingletonDCL instance = null;
    private SingletonDCL() {
    
    }
    public static  SingletonDCL getInstance() {
    
    
        //为了让后面的线程不再获取锁,避免锁竞争
        if (instance == null) {
    
    
            synchronized (SingletonDCL.class) {
    
    
                //完成初始化操作,只执行一次
                if (instance == null) {
    
    
                    instance = new SingletonDCL();
                }
            }
        }
        return instance;
    }
}

About the Hungry Man and Lazy Man Pattern of Singleton Pattern

  1. You can use Hungry Man mode at work because writing is simple and less prone to errors.
  2. The hungry mode is initialized when the program is loaded. However, due to limited computer resources, in order to save resources, the lazy mode can be used.
  3. The lazy mode is to complete the initialization operation when using the object.
  4. Lazy mode may cause thread safety issues in multi-threaded mode
  5. Then you need to use synchronized to wrap the initialization code block
  6. The initialization code is only executed once. When subsequent threads call getInstance(), they will still generate competing locks and frequently switch between user mode and kernel mode, which is a waste of resources.
  7. At this time, you can use double check lock (DCL) to add a non-null check in the outer layer to avoid useless lock competition.
  8. synchronized can only guarantee atomicity and visibility, but not orderliness (other threads may get an object that has been created (instance != null), but has not gotten some data initialized), and then use volatile to solve the ordering problem
  9. Describe possible problems that may arise with instruction reordering

Factory pattern

**Intent:** Define an interface for creating objects, allowing its subclasses to decide which factory class to instantiate. The factory pattern delays the creation process until the subclass.
**Mainly solve: **Mainly solve the problem of interface selection.
**When to use:** When we explicitly plan to create different instances under different conditions.
**How ​​to solve:** Let its subclass implement the factory interface and return an abstract product.
**Key code:** The creation process is executed in its subclass.
Application examples: 1. You need a car that can be picked up directly from the factory without worrying about how the car is made, and The specific implementation inside this car.
Advantages:
1. If a caller wants to create an object, he only needs to know its name.
2. High scalability. If you want to add a product, you only need to extend a factory class.
3. Shield the specific implementation of the product. The caller only cares about the product interface.
**Disadvantages:** Every time you add a product, you need to add a specific class and object implementation factory, which doubles the number of classes in the system and increases it to a certain extent. The complexity of the system also increases the dependence on specific classes of the system. This is not a good thing.
Usage scenarios:
1. Logger: Records may be recorded to local hard disk, system events, remote servers, etc. Users can choose where to record logs.
2. Database access, when the user does not know which type of database will be used by the final system, and when the database may change.
3. Designing a framework for connecting to the server requires three protocols, "POP3", "IMAP", and "HTTP". These three can be regarded as product categories and jointly implement an interface.
**Note:** As a class creation pattern, the factory method pattern can be used wherever complex objects need to be generated. One thing to note is that complex objects are suitable for using the factory pattern, while simple objects, especially those that can be created only through new, do not need to use the factory pattern. If you use the factory pattern, you need to introduce a factory class, which will increase the complexity of the system.
The factory model includes the following core roles:

  • Abstract Product: defines a common interface or abstract class of products. It can be the parent class or interface of a specific product class, which specifies the common methods of product objects.
  • Concrete Product: implements the abstract product interface and defines the specific behavior and attributes of the specific product.
  • Abstract Factory: declares an abstract method to create a product, which can be an interface or abstract class. It can have multiple methods for creating different types of products.
  • Concrete Factory: Implements the abstract factory interface and is responsible for actually creating objects of specific products.

accomplish

public static void main(String[] args) {
    
    
      ShapeFactory shapeFactory = new ShapeFactory();
 
      //获取 Circle 的对象,并调用它的 draw 方法
      Shape shape1 = shapeFactory.getShape("CIRCLE");
 
      //调用 Circle 的 draw 方法
      shape1.draw();
 
      //获取 Rectangle 的对象,并调用它的 draw 方法
      Shape shape2 = shapeFactory.getShape("RECTANGLE");
 
      //调用 Rectangle 的 draw 方法
      shape2.draw();
 
      //获取 Square 的对象,并调用它的 draw 方法
      Shape shape3 = shapeFactory.getShape("SQUARE");
 
      //调用 Square 的 draw 方法
      shape3.draw();
   }

Template mode

In the Template Pattern, an abstract class publicly defines the means/templates for executing its methods. Its subclasses can override the method implementation as needed, but the calls will be made in the manner defined in the abstract class. This type of design pattern is a behavioral pattern.

Intent: Define the skeleton of an algorithm in one operation, while deferring some steps to subclasses. Template methods allow subclasses to redefine specific steps of an algorithm without changing the structure of the algorithm.

Main solution: Some methods are common, but this method is rewritten in every subclass.
When to use: There are some general methods.
How to solve: Abstract these general algorithms.
Key code: implemented in the abstract class, and other steps implemented in the subclass.
Application examples:
1. When building a house, the foundation, wiring, and water pipes are all the same. Only in the later stages of construction are closets and fences added, etc. difference.
2. The 81 difficulties set by the Bodhisattva in Journey to the West are a top-level logical framework.
3. The support for Hibernate in spring encapsulates some predetermined methods, such as opening transactions, obtaining Sessions, closing Sessions, etc., so programmers do not need to repeatedly write those already standardized codes. Just drop an entity and save it.

Advantages:
1. Encapsulate the constant parts and extend the variable parts.
2. Extract public code to facilitate maintenance.
3. Behavior is controlled by the parent class and implemented by the subclass.

Disadvantages: Each different implementation requires a subclass to implement, resulting in an increase in the number of classes and making the system larger.

Usage scenarios:
1. There are methods common to multiple subclasses with the same logic.
2. Important and complex methods can be considered as template methods.

Note: To prevent malicious operations, the final keyword is generally added to template methods.

accomplish

We will create a Game abstract class that defines the action, with the template method set to final so that it will not be overridden. Cricket and Football are entity classes that extend Game and override the methods of the abstract class.
TemplatePatternDemo, our demo class uses Game to demonstrate the use of template pattern.

public abstract class Game {
    
    
   abstract void initialize();
   abstract void startPlay();
   abstract void endPlay();
 
   //模板
   public final void play(){
    
    
      //初始化游戏
      initialize();
      //开始游戏
      startPlay();
      //结束游戏
      endPlay();
   }
}
public class Cricket extends Game {
    
    
 
   @Override
   void endPlay() {
    
    
      System.out.println("Cricket Game Finished!");
   }
 
   @Override
   void initialize() {
    
    
      System.out.println("Cricket Game Initialized! Start playing.");
   }
 
   @Override
   void startPlay() {
    
    
      System.out.println("Cricket Game Started. Enjoy the game!");
   }
}
public class Football extends Game {
    
    
 
   @Override
   void endPlay() {
    
    
      System.out.println("Football Game Finished!");
   }
 
   @Override
   void initialize() {
    
    
      System.out.println("Football Game Initialized! Start playing.");
   }
 
   @Override
   void startPlay() {
    
    
      System.out.println("Football Game Started. Enjoy the game!");
   }
}
public class TemplatePatternDemo {
    
    
   public static void main(String[] args) {
    
    
 //直接调用抽象类中的模板方法,模板方法中执行子类实现的方法
      Game game = new Cricket();
      game.play();
      System.out.println();
      game = new Football();
      game.play();      
   }
}

strategy pattern

Intent: Define a series of algorithms, encapsulate them one by one, and make them interchangeable.
Main solution: When there are multiple similar algorithms, the complexity and difficulty of maintenance caused by using if...else.
When to use: A system has many, many classes, and what distinguishes them is their direct behavior.
How to solve: Encapsulate these algorithms into classes one by one and replace them arbitrarily.
Key code: implement the same interface.
Application examples:
1. Zhuge Liang’s tips, each tip is a strategy.
2. The way to travel, choose riding a bicycle or taking a car, each way of traveling is a strategy.
3. LayoutManager in JAVA AWT.

Advantages: 1. The algorithm can be switched freely. 2. Avoid using multiple conditional judgments. 3. Good scalability.
Disadvantages: 1. The number of strategy categories will increase. 2. All strategy classes need to be exposed to the outside world.

Usage scenarios:
1. If there are many classes in a system and the only difference between them is their behavior, then the strategy pattern can be used to dynamically make an object Choose one behavior among many.
2. A system needs to dynamically choose one of several algorithms.
3. If an object has many behaviors, if appropriate patterns are not used, these behaviors have to be implemented using multiple conditional selection statements.
Strategy mode includes the following core roles:

  • Context: Maintains a reference to the policy object and is responsible for delegating client requests to specific policy objects for execution. Environment classes can obtain specific policy objects through dependency injection, simple factories, etc.
  • Abstract Strategy: defines the public interface or abstract class of the strategy object, and specifies the methods that the specific strategy class must implement.
  • Concrete Strategy: implements an interface or abstract class defined by an abstract strategy, and contains specific algorithm implementations.

The Strategy pattern provides a way to dynamically select different algorithms by decoupling the algorithm from the code that uses the algorithm. The client code does not need to know the specific algorithm details, but uses the selected strategy by calling the environment class.

accomplish

We will create a Strategy interface that defines the activity and an entity strategy class that implements the Strategy interface. Context is a class that uses a certain strategy.
StrategyPatternDemo, our demo class uses Context and strategy objects to demonstrate the behavior changes of a Context when the strategies it configures or uses change.

public interface Strategy {
    
    
   public int doOperation(int num1, int num2);
}
public class OperationAdd implements Strategy{
    
    
   @Override
   public int doOperation(int num1, int num2) {
    
    
      return num1 + num2;
   }
}
public class OperationSubtract implements Strategy{
    
    
   @Override
   public int doOperation(int num1, int num2) {
    
    
      return num1 - num2;
   }
}
public class OperationMultiply implements Strategy{
    
    
   @Override
   public int doOperation(int num1, int num2) {
    
    
      return num1 * num2;
   }
}
public class Context {
    
    
   private Strategy strategy;
 
   public Context(Strategy strategy){
    
    
      this.strategy = strategy;
   }
 
   public int executeStrategy(int num1, int num2){
    
    
      return strategy.doOperation(num1, num2);
   }
}
public class StrategyPatternDemo {
    
    
   public static void main(String[] args) {
    
    
      Context context = new Context(new OperationAdd());    
      System.out.println("10 + 5 = " + context.executeStrategy(10, 5));
 
      context = new Context(new OperationSubtract());      
      System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
 
      context = new Context(new OperationMultiply());    
      System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
   }
}

proxy mode

Intent: Provide a proxy for other objects to control access to this object.
Mainly solves the problems caused when directly accessing objects.
When to use: Want to have some control when accessing a class.
How to solve: Add an intermediate layer.
Key code: combination of implementation and proxied class.
Application examples:
1. Shortcuts in Windows.
2. You don’t have to buy train tickets at the train station, you can also go to an agency.
3. spring aop.

Advantages: 1. Clear responsibilities. 2. High scalability. 3. Intelligent.

Disadvantages: 1. Due to the addition of a proxy object between the client and the real topic, some types of proxy modes may cause the request processing speed to slow down. 2. Implementing the proxy mode requires additional work, and the implementation of some proxy modes is very complex.

Usage scenarios: Divided according to responsibilities, there are usually the following usage scenarios: 1. Remote agent. 2. Virtual agent. 3. Copy-on-Write agent. 4. Protect (Protect or Access) agent. 5. Cache agent. 6. Firewall proxy. 7. Synchronization agent. 8. Smart Reference agent.

Notes: 1. The difference between the adapter mode and the adapter mode: the adapter mode mainly changes the interface of the object under consideration, while the proxy mode cannot change the interface of the proxy class. 2. The difference between the decorator mode and the decorator mode: the decorator mode is to enhance the function, while the proxy mode is to control it.

accomplish

public interface Image {
    
    
   void display();
}
public class RealImage implements Image {
    
    
 
   private String fileName;
 
   public RealImage(String fileName){
    
    
      this.fileName = fileName;
      loadFromDisk(fileName);
   }
 
   @Override
   public void display() {
    
    
      System.out.println("Displaying " + fileName);
   }
 
   private void loadFromDisk(String fileName){
    
    
      System.out.println("Loading " + fileName);
   }
}
public class ProxyImage implements Image{
    
    
 
   private RealImage realImage;
   private String fileName;
 
   public ProxyImage(String fileName){
    
    
      this.fileName = fileName;
   }
 
   @Override
   public void display() {
    
    
      if(realImage == null){
    
    
         realImage = new RealImage(fileName);
      }
      realImage.display();
   }
}
public class ProxyPatternDemo {
    
    
   
   public static void main(String[] args) {
    
    
      Image image = new ProxyImage("test_10mb.jpg");
 
      // 图像将从磁盘加载
      image.display(); 
      System.out.println("");
      // 图像不需要从磁盘加载
      image.display();  
   }
}

Compared

Factory pattern: Define an interface for creating objects and let its subclasses decide which factory class to instantiate. The factory pattern delays the creation process until the subclass.

ShapeFactory shapeFactory = new ShapeFactory();
//获取 Circle 的对象,并调用它的 draw 方法
Shape shape1 = shapeFactory.getShape("CIRCLE");

Template pattern: Define the skeleton of an algorithm in an operation, while deferring some steps to subclasses. Template methods allow subclasses to redefine specific steps of an algorithm without changing the structure of the algorithm.

Game game = new Cricket();
 game = new Football();
 game.play();

Strategy pattern: Define a series of algorithms, encapsulate them one by one, and make them interchangeable

 Context context = new Context(new OperationAdd());

In the template pattern, we specify the processing process in the parent class and implement the specific processing in the subclass. If we use this pattern for generating instances, it evolves into the factory pattern.

A "strategy" is an overall (complete) algorithm, and the algorithm can be replaced as a whole. The template method can only be replaced at specific points, and the algorithm flow is fixed and immutable.

Guess you like

Origin blog.csdn.net/qq_53869058/article/details/132789551