Design pattern detailed strategy pattern

Author: Liu Wenhui

The strategy pattern is a widely used behavioral pattern. The core idea is to encapsulate the algorithm and delegate it to different objects for management. This article will focus on the strategy pattern to share.

I. Overview

If we want to achieve maintainability and scalability during software development, we need to reuse the code as much as possible and reduce the coupling of the code. The design pattern is a method that can improve code reusability, maintainability, and scalability. and solutions for readability.

The 23 well-known design patterns can be divided into three categories: creational patterns, structural patterns, and behavioral patterns. Among them, the behavioral pattern can be used to describe how multiple classes and multiple objects in the program cooperate to complete complex tasks, involving the assignment of responsibilities among different objects and the abstraction of algorithms. The strategy pattern is a widely used behavioral pattern. This article will focus on the strategy pattern for learning and sharing.

2. Basic concepts

The core idea of ​​the strategy pattern is to encapsulate the algorithm and delegate it to different objects for management. In this way, we can define a series of algorithms, encapsulate each algorithm into a series of concrete strategy classes with a common interface, so that they can be replaced flexibly, and the algorithm can be changed without affecting the client. At the same time, the strategy mode only encapsulates algorithms (including adding and deleting), but it does not decide when to use which algorithm, and the choice of algorithm is determined by the client.

For example, there are many travel strategies that we can choose when we travel: bicycles, cars, trains, and airplanes. Each travel strategy has its own method of use. As long as we can reach the destination, we can change various strategies at will. Another example is when we go to the mall, there will be many promotional activities in the mall: full discount, rebate, etc. These promotion methods are essentially algorithms, and the algorithm itself is also a strategy, which can be replaced at any time. For the same product, today Get 50 off when you spend 500, and get 100 coupons when you spend 300 tomorrow. These strategies are also interchangeable.

So, how should we use the strategy pattern? The following will give a conceptual introduction to the strategy pattern from two levels of structure and usage steps.

2.1 Structure

Strategy pattern contains three categories, which are abstract strategy category, concrete strategy category, and environment category. They are responsible for completing specific tasks and are closely related to each other.

2.2 use

With the above basic concepts, we summarize the steps to use the strategy pattern as follows:

  • Step1: Create an abstract strategy class and define a public interface for the specific strategy;

  • Step2: Create a specific strategy class, which implements the abstract strategy class through the interface, and encapsulates the specific algorithm at the same time;

  • Step3: Create an environment class, hold a reference to an abstract strategy class, and provide it to the client to call.

3. Example of use

In addition to the Double 11 shopping carnival, many other promotional activities are created every year. Just imagine, if each promotional activity uses a promotional model, it would be too boring and unfriendly to users, merchants, and platforms. Therefore, in order to improve the user's purchasing experience and highlight the marketing characteristics of merchants, it is necessary to use different strategies for different promotional activities. Here we take the promotion strategy as an example to briefly analyze how the strategy mode is used.

3.1 Code implementation

//step1:定义抽象策略角色(Strategy):所有促销活动的共同接口
public interface Strategy {  
    void show();
}
​
//step2:定义具体策略角色(Concrete Strategy):不同类型的具体促销策略
//618大促活动 A
public class ConcreteStrategyA implements Strategy {
    @Override
    public void show() {
        System.out.println("618大促");
    }
}
​
//99大促活动 B
public class ConcreteStrategyB implements Strategy {
    @Override
    public void show() {
        System.out.println("99大促");
    }
}
​
//双11大促活动 C
public class ConcreteStrategyC implements Strategy {
    @Override
    public void show() {
        System.out.println("双11大促");
    }
}
​
//step3:定义环境角色(Context):把促销活动推送给用户,这里可理解为淘宝平台
public class Context{
    //持有抽象策略的引用
    private Strategy myStrategy;
    //生成构造方法,让平台根据传入的参数(type)选择促销活动
    public Context(Strategy strategyType) {
        this.myStrategy = strategyType;
    }
    //向用户展示促销活动
    public void taoPlatformShow(String time) {
        System.out.println(time + "的促销策略是:");
        myStrategy.show();
    }
}
​
//step4:客户端调用,需事先明确所有每种策略类如何使用
public class StrategyPattern{
  public static void main(String[] args){
        Context_TaoPlatform context;
    
        String time1 = "9月";
        Strategy strategyB = new ConcreteStrategyB();
        context = new Context(strategyB);
        context.taoPlatformShow(time1);
    
        String time2 = "11月";
        Strategy strategyC = new ConcreteStrategyC();
        context = new Context(strategyC);
        context.taoPlatformShow(time2);
    
        String time3 = "6月";
        Strategy strategyA = new ConcreteStrategyA();
        context = new Context(strategyA);
        context.taoPlatformShow(time3);
  }   
}

3.2 Result output

9月的促销策略是:
99大促
11月的促销策略是:
双11大促
6月的促销策略是:
618大促

3.3 UML diagrams

3.4 Differences from Simple Factory Pattern

As can be seen from the above code example and class diagram, the strategy pattern is very similar to the simple factory pattern introduced in the previous article. The main difference between the two is the Context class and the factory class. For the convenience of comparison, let's take a look at the codes of these two classes separately:

public class Context_TaoPlatform{
    //持有抽象策略的引用
    private Strategy myStrategy;
    //生成构造方法,让平台根据传入的参数(type)选择促销活动
    public TaoPlatform(Strategy strategyType) {
        this.myStrategy = strategyType;
    }
    //向用户展示促销活动
    public void taoPlatformShow(String time) {
        System.out.println(time + "的促销策略是:");
        myStrategy.show();
    }
}

public class Factory{
    public static Shirt exhibit(String ShirtName){
        switch(ShirtName){
            case "女款衬衫":
                return new WomenShirt();
            case "男款衬衫":
                return new MenShirt();
            default:
                return null;
        }
    }
}

First look at the receiving parameters: the exhibit() method in the factory class Factory receives a string and returns a Shirt object; the environment class Context_TaoPlatform needs to receive a Strategy object when it is initialized. That is to say: the factory class creates a corresponding object according to the receiving conditions, and the Context class receives an object, and methods can be called to execute the methods of this object.

For example: There are many kinds of pens. Suppose there is a factory responsible for producing pens for different purposes.

Factory mode: Produce pens for different purposes according to the purpose given by the user, such as: if you want to write brush characters, you can produce brushes, and if you want to write pen characters, you can produce pens. That is, according to a certain attribute given by the user, an object that can perform corresponding behaviors is produced and returned to the user, and the focus is on what kind of object to create.

Strategy mode: use the pen produced by the factory to do the corresponding behavior, such as: write brush characters with a brush, and write pen characters with a pen. That is, according to a certain object given by the user, the corresponding method is executed, and the focus is on which behavior to choose.

Four, JDK source code appreciation

Here we take the Comparator comparator as an example, and understand the strategy mode in depth by analyzing its source code implementation.

In JDK, when we call a sorting method sort() of the array tool class Arrays, we can use the default sorting rule (ascending order), or customize a sorting rule, that is, customize the sorting in ascending or descending order. The source code is as follows:

public class Arrays{
    public static <T> void sort(T[] a, Comparator<? super T> c) {
        if (c == null) {
            //若没有传入Comparator接口的实现类对象,调用默认的升序排序方法
            sort(a);
        } else {
            if (LegacyMergeSort.userRequested)
                //jdk5及之前的传统归并排序,新版本中LegacyMergeSort.userRequested默认false
                legacyMergeSort(a, c);
            else
                //改进后的归并排序
                TimSort.sort(a, 0, a.length, c, null, 0, 0);
        }
    }
}

At this point we need to pass in two parameters: one is the array to be sorted, and the other is the implementation class object of the Comparator interface. Among them, the Comparator interface is a functional interface, which defines an abstract method int compare(T o1, T o2), which is used to define a specific sorting rule. Here the Comparator interface is the abstract strategy interface in the strategy pattern, which defines a sorting algorithm, and the specific strategy (the specific sorting algorithm) will be defined by the user, then Arrays is an environment class, and the sort() method can pass in a strategy c , let Arrays perform sorting tasks according to this strategy.

public class demo {
    public static void main(String[] args) {
​
        Integer[] data = {12, 2, 3, 2, 4, 5, 1};
        // 实现降序排序
        Arrays.sort(data, new Comparator<Integer>() {
             // 排序策略 降序
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
        System.out.println(Arrays.toString(data)); //[12, 5, 4, 3, 2, 2, 1]
    }
}

In the above test class, when we call the Arrays.sort() method, the second parameter is the sub-implementation class object of the Comparator interface. It can be seen that the Comparator acts as an abstract strategy role, while the specific sub-implementation class acts as a concrete strategy role, and the environment role class Arrays should hold a reference to the abstract strategy to call. So, does the Arrays.sort() method use the compare() method in the Comparator sub-implementation class? Let's take a look at the TimSort.sort() method, the source code is as follows:

class TimSort<T> {
    static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c,
                         T[] work, int workBase, int workLen) {
        assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length;
​
        int nRemaining  = hi - lo;
        if (nRemaining < 2)
            return;  // Arrays of size 0 and 1 are always sorted
​
        // If array is small, do a "mini-TimSort" with no merges
        if (nRemaining < MIN_MERGE) {
            int initRunLen = countRunAndMakeAscending(a, lo, hi, c);
            binarySort(a, lo, hi, lo + initRunLen, c);
            return;
        }
        ...
    }   
        
    private static <T> int countRunAndMakeAscending(T[] a, int lo, int hi,Comparator<? super T> c) {
        assert lo < hi;
        int runHi = lo + 1;
        if (runHi == hi)
            return 1;
​
        // Find end of run, and reverse range if descending
        if (c.compare(a[runHi++], a[lo]) < 0) { // Descending
            while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)
                runHi++;
            reverseRange(a, lo, runHi);
        } else {                              // Ascending
            while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0)
                runHi++;
        }
        return runHi - lo;
    }
}

The above code will finally be executed in the countRunAndMakeAscending() method, and the compare() method is called when the judgment statement is executed. Then if you only use the compare() method, you only need to pass the class object of the specific compare() override method when calling the Arrays.sort() method.

5. Advantages and disadvantages and applicable scenarios

5.1 Advantages

  • The specific strategy classes can be switched freely. Since the specific strategy classes all implement the same abstract strategy interface, they can be switched freely.

  • It supports the "open-close principle". When adding a new strategy, you only need to add a specific strategy class, and basically do not need to change the original code.

  • Avoid using multiple conditional selection statements (if else), fully embody the object-oriented design idea.

5.2 Disadvantages

  • The client must know all concrete strategy classes, understand the difference between different concrete strategies, and decide which strategy class to use.

  • The strategy pattern will generate many specific strategy classes, which increases the number of classes in the system to a certain extent (the number of objects can be reduced to a certain extent by using the flyweight pattern).

5.3 Applicable scenarios

  • When a system needs to dynamically choose one of several algorithms, each algorithm can be encapsulated into a specific strategy class.

  • A class defines multiple behaviors, and these behaviors appear in the form of multiple conditional statements in the operation of this class. You can move each conditional branch into their respective strategy classes to replace these conditional statements, and you can avoid using difficult Maintain multiple conditional selection statements, and embody the concepts involved in object-oriented.

  • Each algorithm in the system is completely independent of each other, and the implementation details of the specific algorithm are required to be hidden from customers to improve the confidentiality and security of the algorithm.

  • The only difference between multiple classes is that they behave differently. You can use the strategy mode to dynamically select the specific behavior to be executed at runtime.

6. Summary

The strategy pattern is a design pattern that is relatively easy to understand and use. It only encapsulates algorithms to facilitate the insertion of new algorithms into the system and the retirement of old algorithms from the system. This article mentioned in the analysis of the shortcomings of the strategy pattern that the strategy pattern does not determine when to use which algorithm, and the algorithm selection is determined by the client. Although this improves the flexibility of the system to a certain extent, the client needs to understand The distinction between all concrete strategy classes, in order to choose the appropriate algorithm, increases the difficulty of use by the client.

The strategy pattern and the factory pattern have some similarities in their pattern structure, so it is sometimes confusing. In fact, there are many differences between the two: the factory mode is a creational mode, its function is to create objects, it focuses on how to create objects, and mainly solves the unified distribution of resources, completely independent of the creation of objects, allowing The creation has nothing to do with the specific customer; the strategy pattern is a behavioral pattern, the function is to let an object choose a behavior among many behaviors, it pays attention to how the behavior is encapsulated, and realizes the flexible switching and expansion of the strategy by defining the strategy family, and Make policy changes independent of the client using the policy.

In addition, in many scenarios, the strategy mode and the factory mode can be used in combination to play a complementary role. For example, one of the disadvantages of the strategy pattern is that the user must know all the specific strategy algorithms, so that the specific strategy will inevitably be exposed and initialized by the upper module, which is contrary to Dimit's law (the principle of least knowledge), while the upper module and the lower layer The decoupling between modules can be done by factory mode. After the combination of the two, the upper module does not need to know each specific strategy, as long as the strategy mode can be realized through the Context.

Guess you like

Origin blog.csdn.net/AlibabaTech1024/article/details/130984749