How to solve IF-ELSE in strategy mode

Keywords: Programming

How to use policy pattern to solve excessive if else problems

We divide the policy pattern into three parts: the definition of policy, the creation of policy, and the use of policy

Definition of policy

The definition of policy class is relatively simple, including a policy interface and a group of policy classes that implement the interface. Because all policy classes implement the same interface, the client code is based on the interface rather than the implementation programming, and can flexibly replace different policies. The sample code is as follows:


public interface Strategy {
  void algorithmInterface();
}

public class ConcreteStrategyA implements Strategy {
  @Override
  public void  algorithmInterface() {
    //Specific algorithm
  }
}

public class ConcreteStrategyB implements Strategy {
  @Override
  public void  algorithmInterface() {
    //Specific algorithm
  }
}

Policy creation

Because the policy pattern contains a set of policies, when using them, the type is generally used to determine which policy is created to use. In order to encapsulate the creation logic, we need to create details for the client code mask. We can separate the logic of creating policy according to type and put it into factory class. The sample code is as follows:

// If the policy can be shared, the static map can be used to cache the class bytecode directly. If the policy cannot be shared, the static map can be used to cache the class bytecode and then reflect.
public class StrategyFactory {
  private static final Map<String, Strategy> strategies = new HashMap<>();

  static {
    strategies.put("A", new ConcreteStrategyA());
    strategies.put("B", new ConcreteStrategyB());
  }

  public static Strategy getStrategy(String type) {
    if (type == null || type.isEmpty()) {
      throw new IllegalArgumentException("type should not be empty.");
    }
    return strategies.get(type);
  }
}

Use of policies

I just talked about the definition and creation of policies. Now let's take a look at the use of policies. We know that the policy pattern contains a set of optional policies. How does the client code generally determine which policy to use? The most common is to dynamically determine which policy to use at runtime, which is also the most typical application scenario of policy pattern. The "runtime dynamic" here refers to that we do not know which policy will be used in advance, but dynamically decide which policy to use during the program running according to the uncertain factors such as configuration, user input, calculation results, etc. Next, let's explain it with an example.


// Policy interface: evaluation strategy
// Policy classes: lruevicionstrategy, fifoevicionstrategy, lfuevicionstrategy
// Policy factory: EvictionStrategyFactory

public class UserCache {
  private Map<String, User> cacheData = new HashMap<>();
  private EvictionStrategy eviction;

  public UserCache(EvictionStrategy eviction) {
    this.eviction = eviction;
  }

  //...
}

// The runtime dynamically determines which policy to use based on the configuration of the configuration file
public class Application {
  public static void main(String[] args) throws Exception {
    EvictionStrategy evictionStrategy = null;
    Properties props = new Properties();
    props.load(new FileInputStream("./config.properties"));
    String type = props.getProperty("eviction_type");
    evictionStrategy = EvictionStrategyFactory.getEvictionStrategy(type);
    UserCache userCache = new UserCache(evictionStrategy);
    //...
  }
}

// Non runtime dynamically determines which policy is specified in the code
public class Application {
  public static void main(String[] args) {
    //...
    EvictionStrategy evictionStrategy = new LruEvictionStrategy();
    UserCache userCache = new UserCache(evictionStrategy);
    //...
  }
}

How to use policy pattern to eliminate if else

The following code uses if else


public class OrderService {
  public double discount(Order order) {
    double discount = 0.0;
    OrderType type = order.getType();
    if (type.equals(OrderType.NORMAL)) { // General order
      //... omit discount calculation algorithm code
    } else if (type.equals(OrderType.GROUPON)) { // Group purchase order
      //... omit discount calculation algorithm code
    } else if (type.equals(OrderType.PROMOTION)) { // Promotional orders
      //... omit discount calculation algorithm code
    }
    return discount;
  }
}

How to remove the branch judgment logic? That's where the strategic model comes in. We use the policy pattern to refactor the above code, design the discount policy of different types of orders into a policy class, and the factory class is responsible for creating the policy object. The specific code is as follows:


// Definition of policy
public interface DiscountStrategy {
  double calDiscount(Order order);
}
// Omit the code of NormalDiscountStrategy, GrouponDiscountStrategy, PromotionDiscountStrategy class

// Policy creation
public class DiscountStrategyFactory {
  private static final Map<OrderType, DiscountStrategy> strategies = new HashMap<>();

  static {
    strategies.put(OrderType.NORMAL, new NormalDiscountStrategy());
    strategies.put(OrderType.GROUPON, new GrouponDiscountStrategy());
    strategies.put(OrderType.PROMOTION, new PromotionDiscountStrategy());
  }

  public static DiscountStrategy getDiscountStrategy(OrderType type) {
    return strategies.get(type);
  }
}

// Use of policies
public class OrderService {
  public double discount(Order order) {
    OrderType type = order.getType();
    DiscountStrategy discountStrategy = DiscountStrategyFactory.getDiscountStrategy(type);
    return discountStrategy.calDiscount(order);
  }
}

This article is a reading of the beauty of design patterns

Posted by delphi on Sat, 27 Jun 2020 22:10:26 -0700