在项目中如果遇到业务需要针对不同情况做不同处理的场景,最简单的办法就是在一个方法中使用多个if-else分支进行判断。
但如果分支情况多了之后,后期代码维护将会变得比较困难。
使用策略模式可以优化这个问题。
场景
有一个游戏类,玩不同的游戏时有不同的效果。
1 |
|
1 | public class PlayGameOld { |
使用多个if-else分支进行判断,当然可以满足业务需求。但当后期增加新游戏时,需要硬编码修改这段代码添加新的分支,后期维护会越来越困难。
使用策略模式+工厂模式改进
本质上多个if-else分支的处理方式是面向过程,策略模式的处理方式则是面向对象
代码实现
首先创建一个顶层的策略模式,里面有两个方法,isSupport方法用于判断实现类是否支持处理,play则是实际的业务逻辑方法。
1 | public interface IGameStrategy { |
接着创建两个不同的实现类
1 | /** |
1 | /** |
接下来创建一个工厂类(该工厂类是一个单例,由于重点不是这个工厂类,所以并没有严格实现double-check,不重要)。
这个工厂类根据传入的Game对象,内部进行判断,返回一个对应的策略实现类
1 | public class MyFactory { |
测试
1 |
|
优缺点
- 优点
- 代码改动小:以后新增游戏时,无需修改获取策略类的代码,因为工厂类内部会自己判断返回哪个策略类
- 代码层次结构更清晰、利于代码维护。
- 缺点
- 不符合开闭原则:以后新增游戏时,仍需在工厂类初始化策略类时新增新的游戏策略类(尽管只需一行改动)
- 文件数量增加:原来代码逻辑只写在一个文件中,现在每多一个策略就需要多一个代码文件
黑科技:借助Spring进行改进
上面的方式存在一个瑕疵:新增游戏时,仍需在工厂类初始化策略类时硬编码增加新的游戏策略类。
而借助Spring,我们可以将所有策略类注册到Spring容器中,从而使得获取bean更加优雅
代码实现
Game和策略接口IGameStrategy无需改动
两个策略实现类OWStrategy、LoLStrategy都加上注解@Service注册到容器中
修改工厂类:
- 使用Spring的构造器注入,可以优雅的获得所有策略实现类。
- 将工厂类也注册进容器中:
@Component
1 |
|
测试
使用Spring Test
1 |
|
由于工厂类也注册进了容器,我们直接通过容器获取工厂类,代码调用更简洁。
如果后面增加了新的游戏策略,如:
1 | /** |
我们甚至不需要修改原来的任何代码,工厂类会通过容器自动找到这个新的策略实现类
1 |
|
优雅!