一、策略模式介绍
1. 解决的问题
主要解决多种算法类似的情况下,使用条件语句所带来的复杂和难以维护。
2. 定义
策略模式是一种行为设计模式,它能让你定义一系列算法,并将每种算法分别放入独立的类中,以使算法的对象能够相互替换。
3. 应用场景
- 当想使用对象中各种不同的算法变体,并希望能在运行时切换算法时,可使用策略模式。
- 当有许多仅在执行某些行为时略有不同的相似类时,可使用策略模式。
- 如果算法在上下文的逻辑中不是特别重要,使用策略模式能将类的业务逻辑与其算法实现细节隔离开来。
- 当类中使用了复杂条件运算符以在同一算法的不同变体中切换时,可使用策略模式。
二、策略模式优缺点
1. 优点
- 可以在运行时切换对象的算法。
- 可以将算法的实现和使用算法的代码隔离开来。
- 可以使用组合来代替继承。
- 开闭原则:无需对上下文进行修改就能够引入新的策略。
2. 缺点
- 如果算法极少发生改变,那么没必要引入新的类和接口。使用策略模式只会让程序过于复杂。
- 客户端必须知晓策略间的不同,因为客户端要选择合适的策略。
- 许多语言支持函数类型功能,允许在一组匿名函数中实现不同版本的算法。这样,使用这些函数的方式就和使用策略对象完全相同,无需借助额外的类和接口来保持代码简洁。
三、策略模式应用实例:SpringBoot 优雅实现策略模式
1. 实例场景
618已经来临,相信大家都已经开始了买买买的囤货模式,加入购物车,点击支付,选择微信、支付宝、或者银行卡就可以顺利买到自己心仪已久的特促。
今天,就支付选择策略为例,讲一下如何使用 SpringBoot 优雅实现策略模式。
2. 状态模式实现
2.1 工程结构
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 服务类
支付服务接口
/**
* 支付服务接口
*/
public interface PayService {
/**
* 支付账单
*
* @param billAmount 账单金额
*/
void payBill(Integer billAmount);
}
支付宝支付服务实现类
/**
* 支付宝支付服务实现类
*/
@Slf4j
@Service("AliPayService")
public class AliPayServiceImpl implements PayService {
/**
* 支付账单
*
* @param billAmount 账单金额
*/
@Override
public void payBill(Integer billAmount) {
log.info("支付宝支付:您已支付{}元,请确认是否为本人操作", billAmount);
}
}
微信支付服务实现类
/**
* 微信支付服务实现类
*/
@Slf4j
@Service("WeChartPayService")
public class WeChatPayServiceImpl implements PayService {
/**
* 支付账单
*
* @param billAmount 账单金额
*/
@Override
public void payBill(Integer billAmount) {
log.info("微信支付:您已支付{}元,请确认是否为本人操作", billAmount);
}
}
银行卡支付服务实现类
/**
* 银行卡支付服务实现类
*/
@Slf4j
@Service("BankCardPayService")
public class BandCardPayServiceImpl implements PayService {
/**
* 支付账单
*
* @param billAmount 账单金额
*/
@Override
public void payBill(Integer billAmount) {
log.info("银行卡支付:您已支付{}元,余额1000元,请确认是否为本人操作", billAmount);
}
}
2.2.2 上下文
支付服务Context
/**
* 支付服务Context
*/
@Component
public class PayServiceContext {
/**
* 支付服务map
*/
@Autowired
private final Map<String, PayService> strategyPayServiceMap = new ConcurrentHashMap<>();
/**
* 获取支付服务
*
* @param id 服务id
* @return PayService
*/
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 测试验证类
@SpringBootTest
@RunWith(SpringRunner.class)
public class PayServiceTest {
/**
* 支付服务Context
*/
@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 测试结果
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)会创建一个特定策略对象并将其传递给上下文。上下文则会提供一个设置器以便客户端在运行时替换相关联的策略。
设计模式并不难学,其本身就是多年经验提炼出的开发指导思想,关键在于多加练习,带着使用设计模式的思想去优化代码,就能构建出更合理的代码。