The strategy pattern

Policy mode: several algorithms and policy modes are abstracted to provide a unified interface. Different algorithms and policies have different implementation classes. In this way, the client can inject different implementation objects to achieve dynamic replacement of algorithms or policies, which has higher scalability and maintainability.

  • define

    Define a set of methods that encapsulate these algorithms and provide a unified interface that can be interchangeable with each other. The policy pattern lets the algorithm change independently of the customers that use it

  • Usage scenarios

    1. There are many ways to deal with the same type of problem, but the specific behavior is different
    2. When multiple operations of the same type need to be securely encapsulated
    3. When there are multiple subclasses of the same abstract class and you need to select a specific subclass using if-else, etc
  • UML

ConcreteStragetyA, ConcreteStargetyB: ConcreteStragetyA, ConcreteStargetyB: ConcretePolicy implementation classesCopy the code

A unified interface for abstracting algorithms or policies in Stragety; The specific implementation algorithm is encapsulated to achieve a unified interface. In the context of the environment, specific algorithms are instantiated and implementation objects are injected to accomplish specific functions, depending on the situation.

Example is given to illustrate

Taking the amount of traffic mileage as the background, the bus is 1 yuan within 10km, and the bus can travel 5km for every 1 yuan. 3 yuan within 6km, 4 yuan within 6-12, 5 yuan within 12-22, and 6 yuan for the rest; The specific logic is realized by code: the amount required by bus 24km and subway 18km.

Example Code 1
Public class PriceCalculator {private final static int BUS = 1; private final static int SUBWAY = 2; public static void main(String args[]) { PriceCalculator priceCalculator = new PriceCalculator(); / / traffic mileage calculation amount, the incoming traffic types, mileage int busPrice = priceCalculator. CalculatePrice (BUS, 24); int subwayPrice = priceCalculator.calculatePrice(SUBWAY, 18); Println ("BUS(24km): "+ busPrice + "\n" + "SUBWAY(18km):" + subwayPrice); } /** * @param km * @return */ private int busPrice(int km) {if (km <= 10) return 1; else { return ((km - 10) / 5 * 1 + 2); }} /** * 3 yuan within 6km, 4 yuan within 6-12, 5 yuan within 12-22, * * @param km * @return */ private int subwayPrice(int km) {if (km < 6) {return 3; } else if (km >= 6 && km < 12) { return 4; } else if (km >= 12 && km < 22) { return 5; } else { return 6; }} private int calculatePrice(int type, int km) {switch (type) {case BUS: return busPrice(km); case SUBWAY: return subwayPrice(km); } return 0; }}Copy the code
  1. The problem you can see above: Not a single responsibility. It undertakes the calculation logic of bus and subway mileage, and determines which algorithm is used to solve the problem through switch. If we need to add taxis at this time, we need to add specific algorithms for taxis, and at the same time, we need to add judgment conditions in the solution of choosing traffic types, which increases the difficulty of modification.

  2. Secondly, if the policy changes and the transportation pricing changes, we must make unified modification in this class, which will definitely affect other classes and increase the difficulty and controllability of modification.

  3. In the case of too much coupling above, a policy pattern can be used to handle it. Firstly, the common algorithm interface is abstracted and the concrete implementation class of the algorithm is completed. The environment class gives you a specific instance of how to do it according to the plan and the algorithm is injected to get the results printed.

Improve the code

  1. Abstract Common algorithm interface CalculateStategy

    Public interface CalculateStategy {/** * Public interface, return price according to mileage * @param km * @return */ int calculatePrice(int km); }Copy the code
  2. Concrete algorithm implementation class

    Public class BusStragety implements CalculateStategy {** * public class BusStragety implements CalculateStategy {** * * * @param km * @return */ @override public int calculatePrice(int km) {int extraTotal = km-10; int extraFactor = extraTotal / 5; int fraction = extraTotal % 5; int price = 1 + extraFactor * 1; return fraction > 0 ? ++price : price; } // Public class SubwayStagety implements CalculateStategy {** * Subway 3 yuan for 6km, 4 yuan for 6-12, 5 yuan for 12-22, * * @param km * @return */ @override public int calculatePrice(int km) {if (km < 6) {return 3; } else if (km >= 6 && km < 12) { return 4; } else if (km >= 12 && km < 22) { return 5; } else { return 6; }}}Copy the code
  3. The environment class instantiates the algorithm on a case-by-case basis and injects a printout to get the amount

    Public class TranficCalculator {public static void main(String args[]) {// TranficCalculator TranficCalculator =  new TranficCalculator(); / / into the corresponding algorithm instance (traffic) tranficCalculator. SetStagety (new BusStragety ()); / / incoming mileage for amount int busPrice = tranficCalculator. CalculatePrice (24). / / injection algorithm instance (subway) and solving tranficCalculator. SetStagety (new SubwayStagety ()); int subwayPrice = tranficCalculator.calculatePrice(18); System.out.println("BUS(24km): " + busPrice + "\n" + "SUBWAY(18): " + subwayPrice); } // Common interface member variables private CalculateStategy CalculateStategy; Private void setStagety(CalculateStategy CalculateStategy) {this. CalculateStategy = CalculateStategy; } / / through the interface, the realization of algorithm dynamic invocation private int calculatePrice (int km) {return calculateStategy. CalculatePrice (km); }}Copy the code
  • As can be seen from the above, specific algorithms are encapsulated inside their respective concrete implementation classes through common interfaces to decouple the logic between algorithms. At the same time, in the environment class, the algorithm instance corresponding to the specific case instance is solved directly, eliminating if-else and other conditioning judgment. Clearer logic and better maintainability

  • At the same time, if the policy changes and the price fluctuates, we only need to change the corresponding algorithm implementation class. Even if the taxi mode is added, it is very simple as follows:

    Public class TaxiStargety implements CalculateStategy {/** * 3 kM 5 yuan; More than 3 kilometers, 1 yuan per kilometer; * * @param km * @return */ @Override public int calculatePrice(int km) { if (km <= 3) { return 5; } else { int extarTotal = km - 3; return (5 + extarTotal * 1); }}}Copy the code
  • Create a new taxi instance in the environment class and inject and solve the amount of 14 km

    // TranficCalculator TranficCalculator = new TranficCalculator(); / / into the object instance algorithm tranficCalculator. SetStagety (new TaxiStargety ()); / / print System. Out. Println (" Taxi (14) : "+ tranficCalculator. CalculatePrice (14));Copy the code
  • A printout

      BUS(24km): 4
      SUBWAY(18): 5
      Taxi(14): 16
    Copy the code

conclusion

We can see the difference between the two examples:

  1. The former solves the problem of traffic algorithm selection through if-else, which is simple to implement and has a single type level. However, the exposed problems are very obvious, that is, the code is bloated, the logic is complex, it is difficult to upgrade and maintain, and there is no structure at all

  2. In the latter, different policies are constructed into a specific strategy through abstraction, and algorithm replacement is realized through different strategies. This simplifies logic and structure, and enhances readability, stability and scalability of the system, which is more intuitive for complex business logic and more convenient for expansion.

The difference and relation between policy pattern and state pattern

The difference between

  • Strategy mode:

    1. The policy pattern is generally used for the replacement of a single algorithm
    2. The client must know all the alternative policies in advance, and it is up to the client to specify which policy is required for the environment class, noting that usually only one of the most appropriate policies (algorithm) is selected
    3. Policies are peer and can be dynamically replaced during operation
  • State mode:

    1. Each state subclass of the state pattern needs to contain concrete implementations of all methods in the Context class — conditional statements
    2. Eliminate a lot of logical judgment in the environment class by wrapping the behavior and its logic in the state class
    3. The State subclasses of State are used to switch between different states. If the State of the current object is not a parameter corresponding to the State, each State subclass switches the State to the Context class.
    4. And the client does not interact directly with the state class, the client does not need to know about the state! The policy mode is directly dependent on parameters injected into the Context class to select policies. There is no state switching operation. The client needs to know the policy

contact

  1. UML class diagrams, like UML class diagrams, have abstract interfaces
  2. Both the state mode and the policy mode are designed for multiple possible situations. They abstract different processing situations into the same interface (abstract class), which conforms to the open and closed principle, and the policy mode is more general
  3. In practice, policy patterns can be used to encapsulate almost any type of rule. Whenever you hear in the analysis process that different business rules need to be applied in different practices, you can consider using policy patterns. In this regard, policy patterns include the capabilities of state patterns

depth

Reference: Android source code design pattern analysis and actual combat