Policy pattern of design pattern (Java implementation)

Policy pattern of design pattern (Java implementation)

Everyone must have used electronic maps. Enter the departure and destination in the map, and then select your travel mode to calculate the optimal route and estimated duration. Travel modes include driving, bus, walking, cycling, etc. The calculated route and time are different with different travel modes.
In fact, in other words, travel mode is travel strategy. The strategy pattern is the design pattern for such problems. There are too many examples in life, such as shopping promotion and discount strategy, tax calculation strategy and so on. The corresponding strategy pattern is also a common design pattern. In this section, we will take the electronic map as an example to compare the factory mode and the strategy mode, and explain the principle of the strategy mode. Finally, combined with the code implementation of factory mode transformation strategy mode, in order to achieve a higher design goal.

1. Implementation strategy mode

Next, we will take the electronic map as an example to explain how to use the strategy mode. But don't worry. In the last section, we learned the factory mode. It seems that the electronic map can also be realized by the factory mode. So let's first look at how to implement it in factory mode. In the following example, only the travel mode is involved in the interface for convenience, and the departure and destination are omitted. The calculation result is the estimated duration.

1.1 electronic map of factory mode

First, we need a policy interface, which is implemented by different policies. With a strategic factory. The client code only needs to let the factory return to the specific implementation according to the user's travel mode, and the specific implementation provides algorithm calculation. The electronic map code implemented in factory mode is as follows.
TravelStrategy interface code:

public interface TravelStrategy {
    int calculateMinCost();
}

Implementation code of TravelStrategy interface:

public class SelfDrivingStrategy implements TravelStrategy {
    @Override
    public int calculateMinCost() {
        return 30;
    }
}

TravelStrategyFactory Code:

public class TravelStrategyFactory {
    public TravelStrategy createTravelStrategy(String travelWay) {
        if ("selfDriving".equals(travelWay)) {
            return new SelfDrivingStrategy();
        } if ("bicycle".equals(travelWay)) {
            return new BicycleStrategy();
        } else {
            return new PublicTransportStrategy();
        }
    }
}

TravelService provides external calculation methods and generates the required strategy through the factory. The code is as follows:

public class TravelService {
    private TravelStrategyFactory travelStrategyFactory = new TravelStrategyFactory();

    public int calculateMinCost(String travelWay) {
        TravelStrategy travelStrategy = travelStrategyFactory.createTravelStrategy(travelWay);
        return travelStrategy.calculateMinCost();
    }
}

The code structure is as like as two peas of music recommended in the previous section. It also seems to solve our design problems well. Next, let's look at how to solve this problem with the strategy model, and then we compare the two models.

1.2 implementation of electronic map in strategic mode

To use the policy mode, you need to add a policy context class. The context class holds a reference to the policy implementation and provides external calculation methods. Context class implements different calculation logic according to different holding strategies. The client code only needs to call the calculation method of the context class. If you want to switch the policy implementation, you only need to change the policy implementation held by the context class.
The code of TravelStrategy interface and implementation remains unchanged. Please refer to the code given in the factory pattern above. Other codes are as follows:
StrategyContext class:

public class StrategyContext {
    private TravelStrategy strategy;

    public StrategyContext(TravelStrategy strategy) {
        this.strategy = strategy;
    }

    public int calculateMinCost(){
        return strategy.calculateMinCost();
    }

The StrategyContext holds the implementation of a TravelStrategy. The calculateMinCost method provided externally is actually a layer of proxy for TravelStrategy. When you want to switch between different algorithms, you only need to change the TravelStrategy implementation held by the StrategyContext.

TravelService provides external calculation methods with the following codes:

public class TravelService {
    private StrategyContext strategyContext;

    public int calculateMinCost(String travelWay){

        if ("selfDriving".equals(travelWay)) {
            strategyContext = new StrategyContext(new SelfDrivingStrategy());
        } if ("bicycle".equals(travelWay)) {
            strategyContext = new StrategyContext(new BicycleStrategy());
        } else {
            strategyContext = new StrategyContext(new PublicTransportStrategy());
        }

        return strategyContext.calculateMinCost();
    }
}

It can be seen that TravelService only deals with Context. When initializing Context, different policies are set according to different travel modes. Seeing this, you may wonder if using factory mode eliminates the conditional statements of client code. How to use the policy mode, and the conditional statements come back? Don't worry, let's keep looking down.

Finally, let's take a look at the class diagram of the policy pattern:

2. Advantages and disadvantages of strategy mode

2.1 advantages

  1. Using the policy mode, you can define a series of reusable algorithms or behaviors according to the policy interface;
  2. The caller only needs to hold the reference of Context. Without knowing the specific strategy implementation. Satisfy Dimitri's law;
  3. Context can do some general aspect logic outside the policy method.

In GOF's book design patterns, I doubt that strategic patterns can eliminate some conditional statements. As in the above example, although the policy implementation has been specified during Context initialization, it is not necessary to select logical branches according to conditions in the calculation logic. However, when the client code initializes the Context, how to determine which policy implementation should be passed in? In fact, there is no lack of conditional judgment in the client code or elsewhere. Therefore, the elimination of conditional statements here is only for the conditional judgment of algorithm logic.

The first advantage is the core problem solved by the strategy model. But in fact, the factory model can also be achieved. Second, I think it is very important that the client code only needs to deal with Context, avoiding contact with different policy classes and factory classes. In factory mode, the client code needs to know factory class and product class. Just to review the Demeter's law, if two classes don't need to communicate directly, then two classes don't need to interact. It can be called indirectly through a third party.

2.2 disadvantages

  1. The client code needs to know the different policies and how to select them. Therefore, you can see that the above client code has ugly conditional judgment;
  2. Since the policy class implements the same interface, the parameter list should be consistent, but not all policies need all parameters

3. The strategy mode is combined with the factory mode

For the first disadvantage. We can improve it through the combination of policy mode and factory mode. By further encapsulation, the conditional selection of client code is eliminated.

Let's modify the StrategyContext class. The code is as follows:

public class StrategyContext {
    private TravelStrategy strategy;

    public StrategyContext(String travelWay) {
        if ("selfDriving".equals(travelWay)) {
            strategy = new SelfDrivingStrategy();
        } if ("bicycle".equals(travelWay)) {
            strategy = new BicycleStrategy();
        } else {
            strategy = new PublicTransportStrategy();
        }
    }

    public int calculateMinCost(){
        return strategy.calculateMinCost();
    }
}

You can see that our initialization logic is very similar to that of the factory. In this way, the condition judgment is refined into the Context class. The client code will be much simpler. You only need to pass in the corresponding travel mode when initializing the StrategyContext. The code is as follows:

public class TravelService {
    private StrategyContext strategyContext;

    public int calculateMinCost(String travelWay){

        strategyContext = new StrategyContext(travelWay);

        return strategyContext.calculateMinCost();
    }
}

After improvement, the client code is now completely unaware of the existence of the policy object. Conditional judgment is also eliminated. In fact, many times we achieve our design goals by matching different design patterns.

The class diagram of strategy + factory mode is as follows:

4. Applicable scenarios of strategy mode

When there are many behaviors or algorithms with different logic but belonging to the same type, you can consider using the policy pattern. In order to eliminate the conditional judgment in your algorithm code. At the same time, let your code meet a variety of design principles.

Many times, factory mode and strategy mode can solve similar problems for you. But you should think clearly whether you want an object or just a calculation result. If you need an object and want to do a lot of things with it. Then use factory mode. If you only want the calculation result of a specific algorithm, please use the policy mode.

The policy pattern belongs to the object behavior pattern, while the factory belongs to the creation pattern. The comparison between strategy mode and factory mode is as follows:

Tags: Java Design Pattern

Posted on Sun, 24 Oct 2021 01:05:06 -0400 by wrong_move18