一、策略模式介绍
1. 解决的问题
主要解决多种算法类似的情况下,使用条件语句所带来的复杂和难以维护。
2. 定义
策略模式是一种行为设计模式,它能让你定义一系列算法,并将每种算法分别放入独立的类中,以使算法的对象能够相互替换。
3. 应用场景
- 当想使用对象中各种不同的算法变体,并希望能在运行时切换算法时,可使用策略模式。
- 当有许多仅在执行某些行为时略有不同的相似类时,可使用策略模式。
- 如果算法在上下文的逻辑中不是特别重要,使用策略模式能将类的业务逻辑与其算法实现细节隔离开来。
- 当类中使用了复杂条件运算符以在同一算法的不同变体中切换时,可使用策略模式。
二、策略模式优缺点
1. 优点
- 可以在运行时切换对象的算法。
- 可以将算法的实现和使用算法的代码隔离开来。
- 可以使用组合来代替继承。
- 开闭原则:无需对上下文进行修改就能够引入新的策略。
2. 缺点
- 如果算法极少发生改变,那么没必要引入新的类和接口。使用策略模式只会让程序过于复杂。
- 客户端必须知晓策略间的不同,因为客户端要选择合适的策略。
- 许多语言支持函数类型功能,允许在一组匿名函数中实现不同版本的算法。这样,使用这些函数的方式就和使用策略对象完全相同,无需借助额外的类和接口来保持代码简洁。
三、策略模式应用实例:SpringBoot 优雅实现策略模式
1. 实例场景
618已经来临,相信大家都已经开始了买买买的囤货模式,加入购物车,点击支付,选择微信、支付宝、或者银行卡就可以顺利买到自己心仪已久的特促。
今天,就支付选择策略为例,讲一下如何使用 SpringBoot 优雅实现策略模式。
2. 状态模式实现
2.1 工程结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| strategy-pattern └─ src ├─ main │ └─ java │ └─ org.design.pattern.strategy │ ├─ service │ │ ├─ PayService.java │ │ └─ impl │ │ ├─ AliPayServiceImpl.java │ │ ├─ WeChatPayServiceImpl.java │ │ └─ BandCardPayServiceImpl.java │ └─ context │ └─ PayServiceContext.java └─ test └─ java └─ org.design.pattern.strategy └─ PayServiceTest.java
|
2.2 代码实现
2.2.1 服务类
支付服务接口
1 2 3 4 5 6 7 8 9 10 11 12
|
public interface PayService {
void payBill(Integer billAmount); }
|
支付宝支付服务实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
@Slf4j @Service("AliPayService") public class AliPayServiceImpl implements PayService {
@Override public void payBill(Integer billAmount) { log.info("支付宝支付:您已支付{}元,请确认是否为本人操作", billAmount); } }
|
微信支付服务实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
@Slf4j @Service("WeChartPayService") public class WeChatPayServiceImpl implements PayService {
@Override public void payBill(Integer billAmount) { log.info("微信支付:您已支付{}元,请确认是否为本人操作", billAmount); } }
|
银行卡支付服务实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
@Slf4j @Service("BankCardPayService") public class BandCardPayServiceImpl implements PayService {
@Override public void payBill(Integer billAmount) { log.info("银行卡支付:您已支付{}元,余额1000元,请确认是否为本人操作", billAmount); } }
|
2.2.2 上下文
支付服务Context
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
@Component public class PayServiceContext {
@Autowired private final Map<String, PayService> strategyPayServiceMap = new ConcurrentHashMap<>();
public PayService getPayService(String id) { PayService payService = this.strategyPayServiceMap.get(id); if (ObjectUtils.isEmpty(payService)) { throw new IllegalArgumentException("no such " + id + " PayService"); } return payService; } }
|
2.3 测试验证
2.3.1 测试验证类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @SpringBootTest @RunWith(SpringRunner.class) public class PayServiceTest {
@Autowired private PayServiceContext payServiceContext;
@Test public void test() { this.payServiceContext.getPayService("WeChartPayService").payBill(100); this.payServiceContext.getPayService("AliPayService").payBill(200); this.payServiceContext.getPayService("BankCardPayService").payBill(300); } }
|
2.3.2 测试结果
1 2 3
| 2021-06-06 14:38:11.045 INFO 7652 --- [ main] o.d.p.s.s.impl.WeChatPayServiceImpl : 微信支付:您已支付100元,请确认是否为本人操作 2021-06-06 14:38:11.047 INFO 7652 --- [ main] o.d.p.s.service.impl.AliPayServiceImpl : 支付宝支付:您已支付200元,请确认是否为本人操作 2021-06-06 14:38:11.047 INFO 7652 --- [ main] o.d.p.s.s.impl.BandCardPayServiceImpl : 银行卡支付:您已支付300元,余额1000元,请确认是否为本人操作
|
四、策略模式结构

上下文(Context)维护执行具体策略的引用,仅通过策略接口与该对象进行交流。
当上下文需要运行算法时,它会在其已连接的策略对象上调用执行方法。上下文不清楚其所涉及的策略类型与算法的执行方式。
策略(Strategy)接口是所有具体策略的通用接口,它声明了一个上下文用于执行策略的方法。
具体策略(Concrete Strategies)实现了上下文所用算法的各种不同变体。
客户端(Client)会创建一个特定策略对象并将其传递给上下文。上下文则会提供一个设置器以便客户端在运行时替换相关联的策略。
设计模式并不难学,其本身就是多年经验提炼出的开发指导思想,关键在于多加练习,带着使用设计模式的思想去优化代码,就能构建出更合理的代码。
源码地址:https://github.com/yiyufxst/design-pattern-java
参考资料:
小博哥重学设计模式:https://github.com/fuzhengwei/itstack-demo-design
深入设计模式:https://refactoringguru.cn/design-patterns/catalog