前言
我写博客主要是为了梳理知识及归纳总结,我的每一篇博客都会超过3000字,并且都会附上相应的代码或图片,如有任何问题请帮忙指出,谢谢!
需求模拟
在电商系统中支付模块至关重要,支付一般包括支付宝支付、微信支付和银联支付,如何优雅设计支付模块?
违背设计模式的实现
使用if…else实现,伪代码如下:
public String toPay(String type){
if("ALI_PAY".equals(type)){
return "支付宝支付";
}else if("WECHAT_PAY".equals(type)){
return "微信支付";
}else if("UNION_PAY".equals(type)){
return "银联支付";
}
return "支付类型异常";
}
策略模式
1.定义
策略(Strategy)模式的定义:该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理
2.优点
- 多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句,如 if…else 语句、switch…case 语句。
- 策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。
- 策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。
- 策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
- 策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。
划重点:解决复杂业务场景下多重条件语句;避免重复代码;开闭原则;隔离性;高扩展。
3.模式结构
策略模式的主要角色如下:
- 抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
- 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。
- 环境(Context)类:持有一个策略类的引用,最终给客户端调用。
4.模式结构图
设计模式重构
1.抽象策略
/**
* 支付定义接口
* @author winter
*/
public interface PayInterface {
/**
* 支付接口
* @param payRequest
* @return
*/
String toPay(String payRequest);
}
2.具体策略
/**
* 阿里支付业务类
* @author winter
*/
@Service
public class AliPayService implements PayInterface {
@Override
public String toPay(String payRequest) {
System.out.println("阿里支付执行业务。。。。");
return "阿里支付";
}
}
/**
* 微信支付业务类
* @author winter
*/
@Service
public class WechatPayService implements PayInterface {
@Override
public String toPay(String payRequest) {
System.out.println("微信支付执行业务。。。。");
return "微信支付";
}
}
/**
* 银联支付业务类
* @author winter
*/
@Service
public class UnionPayService implements PayInterface {
@Override
public String toPay(String payRequest) {
System.out.println("银联支付执行业务。。。。");
return "银联支付";
}
}
3.环境类
环境类的设计我采用了反射机制通过类型来直接获取正确的支付方式,具体如下:
枚举类:存放支付类型和对应的类名
/**
* 策略枚举
* @author winter
*/
@Getter
public enum StrategyEnum {
/**
* 支付宝支付
*/
ALI_PAY("ALI_PAY","aliPayService"),
/**
* 微信支付
*/
WECHAT_PAY("WECHAT_PAY","wechatPayService"),
/**
* 银联支付
*/
UNION_PAY("UNION_PAY","unionPayService");
/**
* 方法名
*/
private String name;
/**
* 类名 beanName
*/
private String beanName;
StrategyEnum(String name, String beanName) {
this.name = name;
this.beanName = beanName;
}
}
Bean工具类:通过beanName获取到实例
@Component
public class SpringContextUtils implements ApplicationContextAware {
/**
* 上下文对象实例
*/
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* 获取applicationContext
* @return
*/
public ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 通过name获取 Bean.
* @param name beanId
* @return
*/
public Object getBean(String name){
return getApplicationContext().getBean(name);
}
/**
* 通过class获取Bean.
* @param clazz 类名
* @return
*/
public <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
}
/**
* 通过name,以及Clazz返回指定的Bean
* @param name beanId
* @param clazz 类名
* @return
*/
public <T> T getBean(String name,Class<T> clazz){
return getApplicationContext().getBean(name, clazz);
}
}
环境类:
/**
* 环境类
* @since JDK 1.8
* @author winter
**/
@Component
public class PayContext {
@Autowired
private SpringContextUtils springContextUtils;
/**
* 发送短信
* @param payRequest 入参
* @return
*/
public String toPay(String payRequest) {
//通过支付类型获取到支付类的beanId
String beanId = StrategyEnum.valueOf(payRequest).getBeanName();
//初始化bean对象
PayInterface payInterface = springContextUtils.getBean(beanId, PayInterface.class);
return payInterface.toPay(payRequest);
}
}
4.客户端调用
@RestController
@RequestMapping("/test")
public class PayController {
@Autowired
private PayContext payContext;
@GetMapping("/pay")
public String pay(String payRequest){
return payContext.toPay(payRequest);
}
}
以上就是通过策略模式优雅完成了支付模块的设计。
总结
策略模式一般都会和方法模板模式一起使用,策略模式是最典型可以优化if…else的复杂业务,通过使用这种设计模式后可以很好的满足隔离性和扩展性要求,也方便承接不断新增的需求。
如果深入了解会发现 策略模式、适配器模式和组合模式的使用方式非常相似。