This is the second day of my participation in the August More text Challenge. For details, see: August More Text Challenge

0 x0, introduction

😭 be abused finish continue liver!! In this paper, we refer to design patterns and paradigms: behavioral patterns (60-61) and Strategy patterns, which are often used to avoid lengthy if-else or switch branch judgments, but are more useful than that. They can also be used to define, create, and use decoupling policies.

Tips: Second-hand knowledge processing is hard to avoid mistakes, interested in time can be consulted by themselves, thank you.


0 x1, definition

The original definition

Define a set of algorithms, encapsulate each algorithm, and make them interchangeable. The policy pattern allows the algorithm to change independently of the client that uses it.

The algorithm here is the same as the template pattern in the previous section. It does not specifically refer to the algorithm in the data structure and algorithm. It can be understood as the business logic in the general sense.


0x2. Write a simple example

Talk is cheap, show you the code, an example of a simple calculator, without using policy mode:

public class Calculator {
    public static void main(String[] args) {
        System.out.println("4 + 2 =" + compute("+".4.2));
        System.out.println("Calculation: 4-2 =" + compute("-".4.2));
        System.out.println("4 * 2 =" + compute("*".4.2));
        System.out.println("Calculation: 4/2 =" + compute("/".4.2));
    }

    public static float compute(String operator, int first, int second) {
        float result = 0.0 f;
        if(operator.equals("+")) {
            result = first + second;
        } else if(operator.equals("-")){
            result = first - second;
        } else if(operator.equals("*")){
            result = first * second;
        } else if(operator.equals("/")){
            result = first / second;
        }
        returnresult; }}Copy the code

Write out the above code, the IDE still suggests that you can use the switch instead, but it is the same thing. By the way, you can also separate the compute logic from the compute() function into four separate compute functions to avoid the problem of a single function being too long.

Next, refactor the calculator using the policy pattern, in three steps. First, define the policy:

// Calculate the policy interface
public interface ICompute {
    String compute(int first, int second);
}

// Specific strategy
public class AddCompute implements ICompute {
    @Override public String compute(int first, int second) {
        return "Calculate:" + first + "+" + second + "="+ (first + second); }}public class SubCompute implements ICompute {
    @Override public String compute(int first, int second) {
        return "Calculate:" + first + "-" + second + "="+ (first - second); }}public class MulCompute implements ICompute {
    @Override public String compute(int first, int second) {
        return "Calculate:" + first + "*" + second + "="+ (first * second); }}public class DivCompute implements ICompute {
    @Override public String compute(int first, int second) {
        return "Calculate:" + first + "/" + second + "="+ (first / second); }}Copy the code

Then there is the creation and use of the policy:

public class Context {
    private ICompute compute;
    public Context(a) { this.compute = new AddCompute(); }
    public void setCompute(ICompute compute) { this.compute = compute; }
    
    // Use the policy
    public void calc(int first, int second) { System.out.println(compute.compute(first, second)); }}// Test case
public class TestCompute {
    public static void main(String[] args) {
        Context context = new Context();

        context.setCompute(new AddCompute());
        context.calc(4.2);

        context.setCompute(new SubCompute());
        context.calc(4.2);

        context.setCompute(new MulCompute());
        context.calc(4.2);

        context.setCompute(new DivCompute());
        context.calc(4.2); }}Copy the code

The code runs as follows:

It runs the same as the original code, and the if-else is gone, right? Instead of taking advantage of the policy pattern, however, it degenerated into object-oriented polymorphism or interface-based programming rather than implementation, non-dynamic, specifying directly in the code which policy to use.

In the actual development scenario, the policy is not known in advance, but is decided dynamically during the run of the program based on uncertainties such as configuration, user input, calculated demerits, and so on. For situations like the one above where the policy objects can be shared (no member variables, just pure algorithm implementations), we can rewrite the Context as a factory class implementation:

public class Context {
    private static final Map<String, ICompute> computes = new HashMap<>();

    static {
        computes.put("+".new AddCompute());
        computes.put("-".new SubCompute());
        computes.put("*".new MulCompute());
        computes.put("/".new DivCompute());
    }

    public void calc(String operator, int first, int second) { System.out.println(computes.get(operator).compute(first, second)); }}// Modified test case
public class TestCompute {
    public static void main(String[] args) {
        Context context = new Context();
        context.calc("+".4.2);
        context.calc("-".4.2);
        context.calc("*".4.2);
        context.calc("/".4.2); }}Copy the code

The refactored code still has no if-else statements except for reuse of policy objects. Did we really remove the branch judgment? In fact, it is transferred, with the help of table lookup, to hashMap.get (). A similar approach, which returns the appropriate policy by traversing the list, is also to shift ~

For stateful, non-shareable policy objects (new every time) scenarios, in Java can use reflection + annotation technology to hide if-else, limited by space, not to expand, many open source libraries are useful.

Finally, there are UML class diagrams, component roles, and the pros and cons

  • Strategy (abstract policy class) → define common methods of the policy, usually interfaces;
  • ConcreteStrategy → Implement common methods defined by classes in the abstract policy;
  • → Store and execute the specific policy class that needs to be used, the logic that the client calls;

Usage scenario:

  • The system needs to switch several algorithms dynamically.
  • For multiple conditional selection statements, to hide the branching judgment, the behavior can be transferred to the specific policy class by the policy pattern;
  • We only want the client to use the encapsulated algorithm directly, and do not care about the specific implementation details of the algorithm;

advantages

  • Define a series of algorithm implementation, so that algorithms can be replaced with each other, improve the code scalability and flexibility;
  • Ease of understanding nested statements with multiple conditions (moved to specific policy classes);

disadvantages

  • The caller can choose which policy to use, but they need to know how different each policy is and when the policy changes.
  • When the number of policies is large, the number of specific policies will increase, which will increase the maintenance cost.
  • Small strategies, less concise than functional programming (anonymous functions to implement different versions of the algorithm);

That’s all for this section. Thank you