借助强大的Spring,优雅地使用策略模式
啥是佩奇 策略模式
维基百科:Strategy pattern
菜鸟教程:策略模式
定义:指对象有某个行为,但是在不同的 场景 中,该行为有不同的 实现算法。
主要解决:在有多种算法相似的情况下,使用if...else...
所带来的复杂和难以维护。
关键代码:实现同一个接口。
应用场景
public Integer getResult(Integer num1, Integer num2, OperationTypeEnum typeEnum) {
// 判断操作类型
if (OperationTypeEnum.ADD == typeEnum) {
// 相加
return num1 + num2;
} else if (OperationTypeEnum.SUBTRACT == typeEnum) {
// 相减
return num1 - num2;
} else if (OperationTypeEnum.MULTIPLY == typeEnum) {
// 相乘
return num1 * num2;
}
throw new RuntimeException("没有对应的操作类型");
}
场景:有两个Integer类型的数num1和num2,根据入参的操作类型不同(场景不同),执行相应的相加、减、乘处理逻辑(实现算法)。这样我们的应用场景就跟上面说的策略模式定义就对应上了。
缺点:随着实现逻辑的 复杂 化,以及操作类型的 扩展,if...else...
逻辑块会越来越臃肿,也越来越 不易维护。
优雅的策略模式
菜鸟教程(戳我)中针对上述场景实现了一套经典的策略模式解决方式。
不过本文意在借助 Spring的@Autowired
注解可以直接注入类的集合类型 这一特点,优雅地实现策略模式。
- 相加、相减、相乘三种操作方式实现同一个接口
OperationService
的同一方法doOperation()
。
public interface OperationService {
/**
* 执行操作
*
* @param num1
* @param num2
* @return
*/
Integer doOperation(Integer num1, Integer num2);
}
@Service("addOperationServiceImpl")
public class AddOperationServiceImpl implements OperationService {
/**
* 执行相加操作
*
* @param num1
* @param num2
* @return
*/
@Override
public Integer doOperation(Integer num1, Integer num2) {
return num1 + num2;
}
}
@Service("subtractOperationServiceImpl")
public class SubtractOperationServiceImpl implements OperationService {
/**
* 执行相减操作
*
* @param num1
* @param num2
* @return
*/
@Override
public Integer doOperation(Integer num1, Integer num2) {
return num1 - num2;
}
}
@Service("multiplyOperationServiceImpl")
public class MultiplyOperationServiceImpl implements OperationService {
/**
* 执行相乘操作
*
* @param num1
* @param num2
* @return
*/
@Override
public Integer doOperation(Integer num1, Integer num2) {
return num1 * num2;
}
}
- 三种操作的 类型 和操作 实现类Bean的name,维护到枚举
OperationTypeEnum
中作为字典使用。
public enum OperationTypeEnum {
/**
* 相加
*/
ADD(1, "相加", "addOperationServiceImpl"),
/**
* 相减
*/
SUBTRACT(2, "相减", "subtractOperationServiceImpl"),
/**
* 相乘
*/
MULTIPLY(3, "相乘", "multiplyOperationServiceImpl");
/**
* 唯一编码
*/
private Integer code;
/**
* 描述
*/
private String desc;
/**
* 对应策略模式实现类Bean的name
*/
private String strategyBeanName;
/* 构造方法 */
/* getter & setter */
}
如上述代码,各操作实现类Bean的名字,即各实现类上方@Service
注解括号中的标识,与操作类型通过枚举OperationTypeEnum
实现了映射。
- 计算操作策略接口
CalculateOperationStrategy
只有一个方法calculate()
。
接口不再赘述,直接看实现类。
@Service("calculateOperationStrategy")
public class CalculateOperationStrategyImpl implements CalculateOperationStrategy {
/**
* '将所有类型为OperationService的Bean注入为一个Map
* key为Bean的name
*/
@Autowired
private Map operationServiceMap;
/**
* 计算
*
* @param num1
* @param num2
* @param type
* @return
*/
@Override
public Integer calculate(Integer num1, Integer num2, OperationTypeEnum type) {
// 该操作类型对应的策略模式实现类Bean的name
String strategyBeanName = type.getStrategyBeanName();
if (operationServiceMap.get(strategyBeanName) != null) {
// 执行对应策略模式实现类的doOperation方法
return operationServiceMap.get(strategyBeanName).doOperation(num1, num2);
} else {
throw new RuntimeException("没有对应的操作类型");
}
}
- 我们通过
@Autowired
接口直接将所有类型为OperationService
的Bean注入为一个Map,Map的 key 为 Bean的name。该步骤的具体实现原理请看我的上一篇文章:【Spring】@Autowired 注入类的集合类型深度解剖。 - 刚已知传入的操作类型枚举
OperationTypeEnum
中包含对应实现类Bean的name,所以我们可以这个name作为key,从刚才的Map中get到对应操作类型的实现类,最后直接调用该实现类的doOperation()
方法。
- 最后看测试类
@RunWith(SpringRunner.class)
@SpringBootTest(classes = PpValidatorApplication.class)
public class CalculateOperationStrategyTest {
@Autowired
private CalculateOperationStrategy calculateOperationStrategy;
@Test
public void testCalculate() {
Integer num1 = 66;
Integer num2 = 6;
System.out.println("计算方式:"
+ OperationTypeEnum.ADD.getDesc()
+ " ---> 计算结果:"
+ calculateOperationStrategy.calculate(num1, num2, OperationTypeEnum.ADD));
System.out.println("计算方式:"
+ OperationTypeEnum.SUBTRACT.getDesc()
+ " ---> 计算结果:"
+ calculateOperationStrategy.calculate(num1, num2, OperationTypeEnum.SUBTRACT));
System.out.println("计算方式:"
+ OperationTypeEnum.MULTIPLY.getDesc()
+ " ---> 计算结果:"
+ calculateOperationStrategy.calculate(num1, num2, OperationTypeEnum.MULTIPLY));
}
}
执行结果如下
计算方式:相加 ---> 计算结果:72
计算方式:相减 ---> 计算结果:60
计算方式:相乘 ---> 计算结果:396
!!!完美!!!
总结
- 策略模式可以根据程序上下文中类型不同,执行类型对应的实现逻辑。后续修改、替换都很方便。
- 借助 Spring的
@Autowired
注解可以直接注入类的集合类型 这一特点,可以优雅地实现策略模式。具体实现原理请戳:【Spring】@Autowired 注入类的集合类型深度解剖。- 各实现类只需要维护各自类中的
doOperation()
方法即可。- 如需扩展类型,只需要新增一个实现类,实现
OperationService
接口,并将实现类Bean的名字和类型维护到字典枚举OperationTypeEnum
中。