【设计模式】优雅的策略模式

1,828 阅读4分钟

借助强大的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注解可以直接注入类的集合类型 这一特点,优雅地实现策略模式。

  1. 相加、相减、相乘三种操作方式实现同一个接口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;
    }
}
  1. 三种操作的 类型 和操作 实现类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实现了映射。

  1. 计算操作策略接口CalculateOperationStrategy只有一个方法calculate()。 接口不再赘述,直接看实现类。
@Service("calculateOperationStrategy")
public class CalculateOperationStrategyImpl implements CalculateOperationStrategy {
    /**
     * '将所有类型为OperationService的Bean注入为一个Map
     * key为Bean的name
     */
    @Autowired
    private Map<String, OperationService> 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的 keyBean的name。该步骤的具体实现原理请看我的上一篇文章:【Spring】@Autowired 注入类的集合类型深度解剖
  • 刚已知传入的操作类型枚举OperationTypeEnum中包含对应实现类Bean的name,所以我们可以这个name作为key,从刚才的Map中get到对应操作类型的实现类,最后直接调用该实现类的doOperation()方法。

点开 doOperation() 方法,有三个实现

  1. 最后看测试类
@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中。

分享自我的简书 HelloLittleRain(链接戳我)