设计模式-策略模式
策略模式,英文全称是 Strategy Design Pattern。它是这样定义的:Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. 翻译成中文就是:定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的客户端(这里的客户端代指使用算法的代码)。
策略模式解耦了策略的定义、创建、使用这三部分。
案例分析
我之前做过一个审核系统,需要审核很多东西,例如注册资本超过100w得2分,否则不得分,用房面积每20平得0.2分等等
通过一些系统设计,审核操作需要传递的参数定义如下:
1@Data 2public class Input { 3 4 private String type; 5 6 private Map<String, String> data; 7 8} 9
type 字段标识当前审核的是哪一个类型的数据(注册资本、用房面积等),data 存储的是审核的值,这里省略了一些细节,例如审核状态、这条记录在审核表对应的审核ID等等
定义策略和对应的实现类
1// 策略接口 2public interface Strategy { 3 4 BigDecimal compute(Input input); 5 6} 7 8// 用房面积策略类 9public class AccommodationStrategy implements Strategy { 10 11 @Override 12 public BigDecimal compute(Input input) { 13 Map<String, String> inputData = input.getData(); 14 String coveredArea = inputData.get("coveredArea"); 15 if (coveredArea != null) { 16 BigDecimal capitalValue = new BigDecimal(coveredArea); 17 return capitalValue.divideToIntegralValue(new BigDecimal("20")) 18 .multiply(new BigDecimal("0.2")); 19 } 20 throw new IllegalArgumentException("coveredArea is required"); 21 } 22 23} 24 25// 注册资本策略类 26public class RegisteredCapitalStrategy implements Strategy { 27 28 @Override 29 public BigDecimal compute(Input input) { 30 Map<String, String> inputData = input.getData(); 31 String capital = inputData.get("Registered capital"); 32 if (capital != null) { 33 BigDecimal capitalValue = new BigDecimal(capital); 34 if (capitalValue.compareTo(new BigDecimal("100")) >= 0) { 35 return new BigDecimal("2"); 36 } 37 return new BigDecimal("0"); 38 } 39 throw new IllegalArgumentException("Registered capital is required"); 40 } 41 42} 43
在使用的时候,可以利用 switch case 或 if else 判断选择对应的策略类,由于它们实现了相同的接口因此可以互相替换
1public class Main { 2 3 public static void main(String[] args) { 4 Input input = new Input(); 5 input.setData(Map.of("coveredArea", "100")); 6 input.setType("accommodation"); 7 test(input); 8 } 9 10 private static void test(Input input) { 11 if (input.getData() == null) { 12 throw new IllegalArgumentException("invalid data"); 13 } 14 Strategy strategy = null; 15 String type = input.getType(); 16 if ("accommodation".equals(type)) { 17 strategy = new AccommodationStrategy(); 18 } else if ("registeredCapital".equals(type)) { 19 strategy = new RegisteredCapitalStrategy(); 20 } 21 if (strategy == null) { 22 throw new IllegalArgumentException("invalid type"); 23 } 24 System.out.println(strategy.compute(input)); 25 } 26 27} 28
但是这样的 if 判断在多的时候会非常繁琐,而且策略的算分逻辑是一直不变的,没有必要每次都重新 new 一个对象出来,可以借助之前的工厂模式简化 if else 判断以及每次返回同一个对象
1public interface Constants { 2 3 Map<String, Strategy> STRATEGY_MAP = Map.of( 4 "accommodation", new AccommodationStrategy(), 5 "registeredCapital", new RegisteredCapitalStrategy() 6 ); 7 8} 9
1public class Main { 2 3 public static void main(String[] args) { 4 Input input = new Input(); 5 input.setData(Map.of("coveredArea", "100")); 6 input.setType("accommodation"); 7 test(input); 8 } 9 10 private static void test(Input input) { 11 if (input.getData() == null) { 12 throw new IllegalArgumentException("invalid data"); 13 } 14 String type = input.getType(); 15 Strategy strategy = Constants.STRATEGY_MAP.get(type); 16 if (strategy == null) { 17 throw new IllegalArgumentException("invalid type"); 18 } 19 System.out.println(strategy.compute(input)); 20 } 21 22} 23
“运行时动态确定”是策略模式最典型的应用场景,根据配置文件或参数动态选择策略类,不过按照上述的代码(静态 Map 存储对应策略关系),每次返回的都是之前的对象,不能做到每次返回新的对象,有没有什么办法呢?
我们可以不存储真正的策略类实例对象,而是存储类路径,每次通过反射创建,这样每次拿到的就都是全新的对象了。
