策略模式:在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。
何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。
如何解决:将这些算法封装成一个一个的类,任意地替换。
关键代码:实现同一个接口。
应用实例: 1、诸葛亮的锦囊妙计,每一个锦囊就是一个策略。 2、旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。 3、JAVA AWT 中的 LayoutManager。
策略模式三种角色:
1、环境上下文(Context)角色:持有一个Strategy引用,委托策略变量调用具体策略所实现的策略接口中的方法。
2、策略(Strategy)角色:抽象角色,通常由一个接口或抽象类实现,此角色给出所有的具体策略类所需接口。
3、具体策略(ConcreteStrategy)角色:具体实现策略接口的类,包装了相关的算法或行为。
废话不多说,直接上手:
项目背景:聚合支付平台,涉及到多种支付方式选择。
环境准备:JDK8、springboot2.0.x、idea工具、mysql数据库...
传统做法:
解释:可以看到需要根据支付code码,来调用不同的支付方式,可能还会有其他更多支付方式...显而易见,只是调用三方接口处理逻辑不同,但最终都是封装html表单给支付接口。这里使用if判断,也是目前大多数人的处理方式,简单直观,但是后期的代码维护性很差!
使用策略模式:
先根据需要创建一个支付渠道关系表
(可以根据需要增加其他字段,如回调url,是否删除等字段,建议将这块放到后台管理中)
整体流程可以缩概为如下图所示:
弄懂整个原理和流程,下面进入开发阶段
1、定义mapper(这里用的是springboot整合mybatis):
<mapper namespace="com.zm.test.mapper.PaymentChannelMapper">
<select id="getPaymentChannel" resultType="com.zm.test.entity.PaymentChannelEntity">
SELECT id as id ,channel_name as channelName ,channel_id as channelId,strategy_bean_id AS strategybeanid
FROM payment_channel where channel_id=#{payCode}
</select>
</mapper>
1、定义具体策略ConcreteStrategy:
public interface PayStrategy {
/**
* 策略模式算法核心
* @return
*/
String toPayM();
}
2、定义具体策略Strategy:
@Component
public class AliPayStrategy implements PayStrategy {
/**
* 策略模式算法核心
*
* @return
*/
@Override
public String toPayM() {
return "使用支付宝支付....";
}
}
@Component
public class WXPayStrategy implements PayStrategy {
/**
* 策略模式算法核心
*
* @return
*/
@Override
public String toPayM() {
return "使用微信支付....";
}
}
@Component
public class YinLIanPayStrategy implements PayStrategy {
/**
* 策略模式算法核心
*
* @return
*/
@Override
public String toPayM() {
return "使用银联支付....";
}
}
3、实现环境上下文Context:
@Component
public class PayContextStrategy {
@Resource
private PaymentChannelMapper paymentChannelMapper;
/**
* 1、根据payCode从数据库中获取对应的beanId
* 2、根据beanId从spring容器中获取对应的实例
* 3、执行对应支付方法
* 4、建议优化:可将数据存储在内存中,减少查询数据库的消耗
* @param payCode
* @return
*/
public String toPayM(String payCode){
PaymentChannelEntity entity = paymentChannelMapper.getPaymentChannel(payCode);
if (entity == null || StringUtils.isEmpty(entity.getStrategyBeanId())){
return "参数错误,没有对应渠道信息";
}
PayStrategy payStrategy = SpringUtils.getBean(entity.getStrategyBeanId(),PayStrategy.class);
return payStrategy.toPayM();
}
}
至此,代码开发完毕~~
可以写个demo试下是否成功
@RestController
public class PayController {
@Autowired
private PayContextStrategy payContextStrategy;
@GetMapping("/toPayM")
public String toPayM(String payCode){
return payContextStrategy.toPayM(payCode);
}
}
注意:这里有个细节问题,数据库存储的beanId一定要跟我们定义的bean保持一致,否则根据beanId去spring容器中获取实例对象会失败!
附根据beanId获取对应实例对象的utils
/**
* 根据beanId 从spring容器中获取对象
*/
@Component
public class SpringUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
//获取applicationContext
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
//通过name获取 Bean.
public static Object getBean(String name){
return getApplicationContext().getBean(name);
}
//通过class获取Bean.
public static <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
}
//通过name,以及Clazz返回指定的Bean
public static <T> T getBean(String name,Class<T> clazz){
return getApplicationContext().getBean(name, clazz);
}
}
优点:策略模式在实际开发中,解决多重if判断问题,便于后期代码维护,增强可读性;
缺点:如果后期策略类型增多,定义类也会相应增多,增加代码量。
完~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。