如何去使用策略模式去避免 if/else呢?
模板模式,为了起到一个代码复用和扩展的能力,何其类似的回调亦是如此
那么,策略模式,就是为了避免冗长的if-else或者switch判断.以及提供扩展点
策略模式在定义中是指,定义一组算法类,然后将不同的算法彼此隔离,彼此可以进行互相替换,策略模式可以让算法的变化独立于使用的客户端
工厂模式是解耦对象的创建和使用,观察者模式是解耦观察者和被观察者,策略模式和两者相似,起到一个解耦的作用,不过,其解耦出了三个部分 策略的定义 创建和使用
1.策略模式的定义
定义比较简单,一个策略接口和一个实现这个接口的策略类
利用面向对象的多态的特性,方便用户去灵活的替换不同的策略实现类
public interface Strategy {
void algorithmInterface(); } public class ConcreteStrategyA implements Strategy { @Override public void algorithmInterface() { //具体的算法… } } public class ConcreteStrategyB implements Strategy { @Override public void algorithmInterface() { //具体的算法… } } |
2.策略的创建,必然不能每次都去new一个新的策略类,而是可以选择去使用工厂模式或者使用一个框架去方便注入
那么,使用的方式可以如下
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); } } |
一般来说,策略类都是一个无状态的工具,这种策略类是可以被共享使用的,不需要每次调用get的时候,去创建一个新的,那么上面的方式就很合适,事先创建好每一个策略对象,然后放在一个集合存储起来,使用的时候直接调用
如果每次都需要创建新的策略类,还是必须去进行判断,利用多次If-else,比如如下
public class StrategyFactory {
public static Strategy getStrategy(String type) { if (type == null || type.isEmpty()) { throw new IllegalArgumentException(“type should not be empty.”); } if (type.equals(“A”)) { return new ConcreteStrategyA(); } else if (type.equals(“B”)) { return new ConcreteStrategyB(); } return null; } } |
3.策略的使用
策略模式是去包含一组可选择的策略,那么如何确定使用哪个策略呢?
可以分为两种方式,分别是运行时指定和非运行时指定
运行时动态分配是指的策略,也就是在程序运行的期间,根据配置 用户输入,计算结果这些不确定因素,然后确定使用的类型
// 策略接口:EvictionStrategy
// 策略类:LruEvictionStrategy、FifoEvictionStrategy、LfuEvictionStrategy… // 策略工厂:EvictionStrategyFactory public class UserCache { private Map<String, User> cacheData = new HashMap<>(); private EvictionStrategy eviction; public UserCache(EvictionStrategy eviction) { this.eviction = eviction; } //… } // 运行时动态确定,根据配置文件的配置决定使用哪种策略 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); //… } } // 非运行时动态确定,在代码中指定使用哪种策略 public class Application { public static void main(String[] args) { //… EvictionStrategy evictionStrategy = new LruEvictionStrategy(); UserCache userCache = new UserCache(evictionStrategy); //… } } |
上面代码中,说明了非运行时确定,也就是第二个Application中的使用方式,并不能发挥策略模式的优势,直接先在代码中指定了使用的类型,让策略模式毫无用处
其实说到底,策略模式的主要目的,是为了避免过多的代码判断逻辑,但也并非是完全消除了,很多情况下只是放在了策略工厂类中进行判断罢了
就跟如下的场景一样
public class OrderService {
public double discount(Order order) { double discount = 0.0; OrderType type = order.getType(); if (type.equals(OrderType.NORMAL)) { // 普通订单 //…省略折扣计算算法代码 } else if (type.equals(OrderType.GROUPON)) { // 团购订单 //…省略折扣计算算法代码 } else if (type.equals(OrderType.PROMOTION)) { // 促销订单 //…省略折扣计算算法代码 } return discount; } } |
如何移除这段代码的分支判断逻辑呢?
可以将这个订单类型改为多个策略类,然后在一个工厂策略类中进行这些策略类,并且由ordertype来进行判断选择哪个策略
// 策略的定义
public interface DiscountStrategy { double calDiscount(Order order); } // 省略NormalDiscountStrategy、GrouponDiscountStrategy、PromotionDiscountStrategy类代码… // 策略的创建 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); } } // 策略的使用 public class OrderService { public double discount(Order order) { OrderType type = order.getType(); DiscountStrategy discountStrategy = DiscountStrategyFactory.getDiscountStrategy(type); return discountStrategy.calDiscount(order); } } |
这样我们利用了Map,来进行查找,并且利用type,从Map中获取到对应的策略,从而避免了if-else的判断
同样,如果需要创建不停的策略对象,使用其他的工厂类实现方式就行了,具体的代码如下
那么这一章的重点就很简单了
public class DiscountStrategyFactory {
public static DiscountStrategy getDiscountStrategy(OrderType type) { if (type == null) { throw new IllegalArgumentException(“Type should not be null.”); } if (type.equals(OrderType.NORMAL)) { return new NormalDiscountStrategy(); } else if (type.equals(OrderType.GROUPON)) { return new GrouponDiscountStrategy(); } else if (type.equals(OrderType.PROMOTION)) { return new PromotionDiscountStrategy(); } return null; } } |
策略模式定义一族算法类,每个算法分别封装起来,可以彼此替换,策略模式让其算法变化独立于客户端
策略模式由三部分组成,定义策略,创建策略,使用策略类
策略的定义不必说,很简单,只需要包含一个策略接口和一组实现策略类
策略的创建交给工厂类完成,封装具体实现
策略模式使用时候有编译时候确定和运行时确定,一般是运行时确定