一、描叙
1、为什么要使用策略模式?
-
在某些业务场景下,我想添加 qq登录,微信登录,阿里账号登录,网易账号第三方登录,但是在某一天可能因为一些原因,我们不需要其中一个第三方登录了,患者第三方不在提供支持,比如网易公司不再提供登录了, 那我们需要修改代码,移除网易账号登录功能,但是某天网易公司账号有可以使用了, 我们又需要修改代码来实现这一功能
-
如果我们使用了策略模式, 我们在后台会有一个管理列表,可以直接禁用/ 启用某一个功能
-
通过策略Id (前端传递) 找到对应的容器bend,调用对应的实现方法即可
-
如果哪天第三方不在支持或不想要了,直接禁用即可, 如果哪天又想要了,直接启动即可, 如果想在添加新的策略,也无需改变原有的所有代码,大大降低了耦合,避免业务改动带来的一系列隐藏问题
二、代码实现
当前项目使用 springboot 2.0.1 + mybatis3.1
1、sql 脚本, 策略表
CREATE TABLE `t_admin_strategy` (
`id` varchar(32) NOT NULL COMMENT 'ID',
`strategy_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '策略名',
`strategy_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '策略id',
`strategy_type` int(2) NOT NULL COMMENT '策略type(字典code)',
`strategy_bean_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '策略执行 spring容器的beanid',
`disable` int(1) NOT NULL DEFAULT '0' COMMENT '是否禁用(字典code 0-否 1-是)',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='策略';
2、目录结构
添加了 发送消息的策略 和支付策略
2、SpringUtils 工具类
@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);
}
}
3、AdminStrategy 策略类
/**
* 策略表
*/
@Data
@TableName("t_admin_strategy")
public class AdminStrategy {
/**
* 策略Id
*/
private String id;
/**
* 策略名
*/
private String strategyName;
/**
* 策略Id
*/
private String strategyId;
/**
* 策略类型(如: 1-短信 2-支付 3-登录)
*/
private Integer strategyType;
/**
* spring容器 beanId ,用于找到对应的策略方法
*/
private String strategyBeanId;
/**
* 是否禁用
*/
private Integer disable = 0;
}
3、AdminStrategyMapper
public interface AdminStrategyMapper extends BaseMapper<AdminStrategy> {
}
4、StrategyContext 策略Id 获取bend类
/**
* 策略
*/
@Component
public class StrategyContext {
@Autowired
private AdminStrategyMapper strategyMapper;
/**
* 通过策略Id 寻找容器Id
* @param strategyId
* @param strategyType
* @param t
* @param <T>
* @return
*/
public <T> T getStrategy(String strategyId, Integer strategyType, Class<T> t) {
// strategyId bean id 验证参数
if (StringUtils.isEmpty(strategyId)) {
return null;
}
if (strategyType == null) {
return null;
}
if (t == null) {
return null;
}
// 根据策略id查询 (策略id不能重复)
AdminStrategy strategy = strategyMapper.selectOne(new LambdaQueryWrapper<AdminStrategy>()
.eq(AdminStrategy::getStrategyId, strategyId)
.eq(AdminStrategy::getStrategyType, strategyType)
.eq(AdminStrategy::getDisable, 0)
);
if (strategy == null) {
return null;
}
String strategyBeanId = strategy.getStrategyBeanId();
if (StringUtils.isEmpty(strategyBeanId)) {
return null;
}
T bean = SpringUtils.getBean(strategyBeanId, t);
return bean;
}
}
5、支付策略
接口
/**
* 支付策略
*/
public interface PayStrategy {
String toHtml();
}
实现1
/**
* 策略if 阿里支付
*/
@Component
public class AliPayStrategy implements PayStrategy {
@Override
public String toHtml() {
return "对接阿里";
}
}
实现2
/**
* 策略if 银联支付
*/
@Component
public class UnionPayStrategy implements PayStrategy {
@Override
public String toHtml() {
return "对接银联";
}
}
6、消息发送策略
接口
/**
* 消息策略
*/
public interface MsgStrategy {
/**
* 共同行为方法
*
* @return
*/
String sendMsg(String phone);
}
实现1
/**
* 策略if 阿里云发送短信
*/
@Component
public class AliYunStrategy implements MsgStrategy {
@Override
public String sendMsg(String phone) {
return "使用阿里云发送短信";
}
}
实现2
/**
* 策略if 华为发送短信
*/
@Component
public class HuaWeiStrategy implements MsgStrategy {
@Override
public String sendMsg(String phone) {
return "使用华为发送短信";
}
}
7、测试接口类
@RestController
public class TestController{
@Autowired
private StrategyContext strategyContext;
@GetMapping("/sendMsg")
public String sendMsg(String strategyId, String phone) {
// type = 短信
MsgStrategy msgStrategy = strategyContext.getStrategy(strategyId, 1, MsgStrategy.class);
if (msgStrategy == null) {
return "当前渠道已经关闭或者是不存在";
}
return msgStrategy.sendMsg(phone);
}
@GetMapping("/toPayHtml")
public String toPayHtml(String strategyId) {
// type = 支付
PayStrategy payStrategy = strategyContext.getStrategy(strategyId, 2, PayStrategy.class);
if (payStrategy == null) {
return "当前渠道已经关闭或者是不存在";
}
return payStrategy.toHtml();
}
}
三、测试
策略表数据,
1、发送短信策略测试
传递不同的策略Id 根据策略自动调用不同的实现
2、支付策略测试
传递不同的策略Id 根据策略自动调用不同的实现
3、关闭渠道测试
禁用阿里云短信功能
已经关闭了
-
以上部分内容来自于蚂蚁课堂 http://www.mayikt.com/
-
个人开源项目(通用后台管理系统)–> https://gitee.com/wslxm/spring-boot-plus2 , 喜欢的可以看看
-
本文到此结束,如果觉得有用,动动小手点赞或关注一下呗,将不定时持续更新更多的内容…,感谢大家的观看!