A simple and comprehensive understanding of the use of the strategy mode [Spend a few minutes to easily master a knowledge point]

Hello, I’m Coder Fei Ge, thank you for reading this article, and welcome one-click three-connection .

This article focuses on: introducing the concept and practical application of the strategy pattern.
It is full of dry goods, it is recommended to collect it, and you need to check it often. If you have any questions or needs, please feel free to leave a message~~~.
Insert picture description here


Article Directory


Insert picture description here

Problem requirements (the choice of payment system channel providers)

The aggregate payment project has docked three channel providers, channel A, channel B, and channel C. Each channel has a set of independent docking documents (from merchant entry, to payment, to liquidation). So how to realize this function?

problem solved

V1.0 version

The V1.0 version is direct stud, all business logic is written on the client, and the client can call the merchant entry method of each channel when calling.

public class Client {
    public static void main(String[] args) {
        Client client = new Client();
        client.addCustomerA("商户1");
        client.addCustomerB("商户2");
        client.addCustomerC("商户3");
    }

    public String addCustomerA(String customer) {
        System.out.println("渠道商A入驻"+customer+"成功");
        return "success";
    }


    public String addCustomerB(String customer) {
        System.out.println("渠道商B入驻"+customer+"成功");
        return "success";
    }

    public String addCustomerC(String customer) {
        System.out.println("渠道商C入驻"+customer+"成功");
        return "success";
    }
}

The above code puts the strategy and call in the client. It is not difficult to see that if you want to add a channel merchant, you must modify the client to add a new channel merchant's merchant entry method. This will cause the client to become very bloated and complex, which is very troublesome to maintain. At the same time, it does not reflect the object-oriented design idea. Writing such code in the generation is afraid that it will be optimized by the boss. So how can we optimize it?

v1.1 version

From an object-oriented perspective, each channel business is abstracted into a specific object, and its attributes and behaviors are stored in a separate class. At the same time, the public behavior (the behavior of merchants) is defined by an interface. The classes implement this interface respectively.

  1. Define the public interface Income, which mainly regulates the behavior of various channel merchants. Here, the behavior of merchants is defined through the addCustomer method.
public interface Income {
    /**
     * 商户入驻
     * @return
     */
    String addCustomer(String customer);
}
  1. Define the classes of each channel merchant. Here, the classes of MerchantAIncome, MerchantBIncome, and MerchantCIncome are defined respectively, and these three classes all implement the addCustomer method of the Income interface to realize merchant entry.
public class MerchantAIncome implements Income {
    @Override
    public String addCustomer(String customer) {
        System.out.println("渠道商A入驻"+customer+"成功");
        return "success";
    }
}
  1. Define the environment class Context. The main function of the environment class here is to hold a reference to an Income object, and various behaviors (that is, various strategies) that the Income object can operate.
public class Context {
    private Income income;

    public Context(Income income) {
        this.income = income;
    }

    public String addCustomer(String customer) {
        return income.addCustomer(customer);
    }
}
  1. Define calling client
public class Client {
      public static void main(String[] args) {
        new Context(new MerchantAIncome()).addCustomer("商户1");
        new Context(new MerchantBIncome()).addCustomer("商户2");
        new Context(new MerchantCIncome()).addCustomer("商户3");
    }
}

UML diagram

The UML diagram of the above code is shown below: There are three main roles:

  1. Environment Context
  2. Abstract strategy Income interface
  3. The specific strategies are MerchantAIncome, MerchantBIncome and MerchantCIncome.
Insert picture description here


As shown in the figure above: each channel vendor's class implements a public interface, which defines various behaviors in the channel vendor, including merchants entering and so on. The client determines which channel vendor is currently being used. Then, an instance of the distributor is generated, and then the relevant method of the distributor is called. Each channel's own changes will not affect the client. Compared with the V1.0 version, the code logic is much more concise. If you add a channel provider, you only need to add another channel provider category, and add a judgment in the client, which conforms to the opening and closing principle. Here is the pattern that this article will introduce-the strategy pattern.

Strategy mode

definition

The strategy mode is to define a series of algorithms (corresponding to a series of channel vendors in this example), and encapsulate each algorithm into a series of strategy classes with a public interface (corresponding to this example is to encapsulate each channel vendor into one Separate classes, and these classes implement a common interface), so that they can replace each other and allow the algorithm to change without affecting the client.

main effect

From the definition of the strategy pattern, we can see that the main function of the strategy pattern is to decouple the responsibility of the algorithm from the object itself, so that:

  1. The algorithm can be changed independently of the client (the internal changes of each channel will not affect the client)
  2. The client can choose different strategies to solve different problems according to external conditions.

advantage

  1. Freely switch between strategy classes
  2. Easy to extend, adding a new strategy only needs to add a specific strategy class, basically no need to change the original code, in line with the "open and close principle"
  3. Eliminate some if else conditional statements

Disadvantage

  1. The client must know all the strategy classes and decide which strategy class to use.
  2. The strategy mode will result in a lot of strategy classes, and the number of objects can be reduced to a certain extent by using the flyweight mode.

Comparison of Strategy Mode and Other Modes

Comparison with state mode

The conditional selection of the strategy mode is executed only once, while the state mode is to constantly change the execution mode as the instance parameters (the state of the object instance) change.

Comparison with simple factory model

The factory mode is a creational mode, which focuses on the creation of objects and provides an interface for creating objects. The
strategy mode is an object behavior mode, which focuses on the encapsulation of behaviors and algorithms. For example: In the travel plan, the strategic mode allows you to choose a travel mode, while the factory mode is to build a specific plan for you.

Extension

v1.2 version

After talking about the strategy mode, the previous version V1.1 did use the strategy mode very well. It is still not enough, or a bit cumbersome. The client still needs to determine which strategy to use through conditional judgments when calling, so that it still does not kill the if And else, is there any way to kill the conditional judgment of the client? The answer is yes. We can define various channel business instances through enumeration classes.
Here only need to add an enumeration class

public enum MerchantIncomeEnum {

    MERCHANT_A("a", new MerchantAIncome()),
    MERCHANT_B("b", new MerchantBIncome()),
    MERCHANT_C("c", new MerchantCIncome()),;
    private String key;
    private Income income;

    MerchantIncomeEnum(String key, Income income) {
        this.key = key;
        this.income = income;
    }
    
    public static Income getIncome(String key) {
        Map<String, Income> map = Arrays.stream(MerchantIncomeEnum.values()).collect(Collectors.toMap(p -> p.key, p -> p.income));
        return map.get(key);
    }
}

Modify the environment class Context

public class Context {

    public String addCustomer(String key, String customer) {
        Income income = MerchantIncomeEnum.getIncome(key);
        return income.addCustomer(customer);
    }
}

In this way, the client only needs to pass in the Key of the designated channel provider when calling, and then the method of the designated channel provider can be called.

to sum up

Starting from a practical application scenario, this article leads to the strategy mode: The main problem that the strategy mode solves is how to separate the object and the algorithm so that the algorithm can change independently of the client using it. Its advantage is that it is easy to expand and can switch freely between strategy classes. The disadvantage is that the client must know all the strategy classes when calling, and decide which strategy class to use. At the same time, the strategy mode will produce many strategy classes. The strategy mode is suitable for scenarios with a series of algorithms or strategies, such as various discount algorithms in shopping malls, various travel plans, and so on.