008:基于策略模式重构if判断
1 策略模式课程介绍
课程内容
- 什么是策略模式
- 策略模式的应用场景
- 策略模式的实现方案有哪些?
- 基于工厂模式实现策略模式
- 基于数据库方式实现策略模式
- 站在架构师角度,设计多行为策略模式框架
2 策略模式基本介绍与应用场景
策略模式基本概念
解决多重if判断问题 有多个不同的策略,选择其中一种策略
策略模式应用场景
- 联合登录 QQ联合、微信联合、钉钉联合登录框架
存在共同的抽象行为 联合登录操作
if (login_type=”qq”){
//对接QQ
}
if (login_type=”weixin”){
//对接微信
}
if (login_type=”钉钉”){
//对接微信
}
后期维护性太差,要专门通过数据库策略表进行维护管理 - 发送短信提醒 对接阿里云、腾讯云、其他短信服务商,保证高可用
- 视频网站 播放视频的时候 展示线路1、线路2
- 聚合支付系统 对接支付宝、银联支付、微信支付
3 策略模式类图结构分析
抽象行为概念:都是在做同一件事情,底层实现不一样
多个不同抽象行为策略设计
4 基于工厂模式实现策略
基于工厂模式实现策略
public interface MsgStrategy {
/**
* 共同行为方法
* @param phone
* @return
*/
String sendMsg(String phone);
}
@Component
public class AliYunStrategy implements MsgStrategy {
@Override
public String sendMsg(String phone) {
return "使用阿里云发送短信" + phone;
}
}
@Component
public class HuaWeiStrategy implements MsgStrategy {
@Override
public String sendMsg(String phone) {
return "使用华为发送短信" + phone;
}
}
public class FactoryStrategy {
private static Map<String, MsgStrategy> msgStrategys = new ConcurrentHashMap<>();
static {
msgStrategys.put("huawei", new HuaWeiStrategy());
msgStrategys.put("aliyun", new AliYunStrategy());
}
public static MsgStrategy getContextStrategy(String strategyId) {
return msgStrategys.get(strategyId);
}
}
@RestController
public class MsgService {
@RequestMapping("/sendMsg")
public String sendMsg(String strategyId, String phone) {
MsgStrategy contextStrategy = FactoryStrategy.getContextStrategy(strategyId);
return contextStrategy.sendMsg(phone);
}
}
运行结果:
5 使用Spring容器实现策略
使用Spring容器实现策略
@Component
public class StrategyContext {
@Autowired
private StrategyMapper strategyMapper;
public <T> T getStrategy(String strategyId, Class<T> t) {
// strategyId beanId
if (StringUtils.isEmpty(strategyId)) {
return null;
}
return SpringUtils.getBean(strategyId, t);
}
}
@RestController
public class MsgService {
@Autowired
private StrategyContext strategyContext;
@RequestMapping("/sendMsg")
public String sendMsg(String strategyId, String phone) {
MsgStrategy msgStrategy = strategyContext.getStrategy(strategyId, MsgStrategy.class);
if (msgStrategy == null) {
return "当前渠道已经关闭或者不存在";
}
return msgStrategy.sendMsg(phone);
}
}
@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);
}
}
运行结果:
6 基于Spring和数据库表实现策略
数据库表结构
DROP TABLE IF EXISTS `meite_strategy`;
CREATE TABLE `meite_strategy` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`strategy_name` varchar(32) NOT NULL COMMENT '策略名称',
`strategy_id` varchar(32) NOT NULL COMMENT '策略id',
`strategy_type` varchar(32) NOT NULL COMMENT '策略类型',
`strategy_bean_id` varchar(255) DEFAULT NULL COMMENT '策略执行beanId',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='策略表';
-- ----------------------------
-- Records of meite_strategy
-- ----------------------------
INSERT INTO `meite_strategy` VALUES ('6', '腾讯云', 'tencent_sms', 'send_msg', 'tencentStrategy');
INSERT INTO `meite_strategy` VALUES ('7', '阿里云', 'aliYun_sms', 'send_msg', 'aliYunStrategy');
INSERT INTO `meite_strategy` VALUES ('8', '华为云', 'huaWei_sms', 'send_msg', 'huaWeiStrategy');
INSERT INTO `meite_strategy` VALUES ('9', '阿里Pay', 'ali_pay', 'pay', 'aliPayStrategy');
INSERT INTO `meite_strategy` VALUES ('10', '银联Pay', 'yinlian_pay', 'pay', 'unionPayStrategy');
多个策略如何封装:在表中加上type字段标记,可以实现多个不同抽象策略行为
支付相关策略
public interface PayStrategy {
String toPayHtml();
}
@Component
public class AliPayStrategy implements PayStrategy {
@Override
public String toPayHtml() {
return "对接阿里支付";
}
}
@Component
public class UnionPayStrategy implements PayStrategy {
@Override
public String toPayHtml() {
return "对接银联支付";
}
}
@RestController
public class PayService {
@Autowired
private StrategyContext strategyContext;
@GetMapping("/toPayHtml")
public String toPayHtml(String strategyId) {
PayStrategy payStrategy = strategyContext.getStrategy(strategyId, "pay", PayStrategy.class);
if (payStrategy == null) {
return "当前渠道已经关闭或者是不存在";
}
return payStrategy.toPayHtml();
}
}
改造策略上下文兼容多种策略类型
@Component
public class StrategyContext {
@Autowired
private StrategyMapper strategyMapper;
public <T> T getStrategy(String strategyId, Class<T> t) {
// strategyId beanId
if (StringUtils.isEmpty(strategyId)) {
return null;
}
return SpringUtils.getBean(strategyId, t);
}
public <T> T getStrategy(String strategyId, String strategyType, Class<T> t) {
// strategyId 验证参数
if (StringUtils.isEmpty(strategyId)) {
return null;
}
if (StringUtils.isEmpty(strategyType)) {
return null;
}
if (t == null) {
return null;
}
// 根据策略id查询
QueryWrapper<MeiteStrategy> queryWrapper = new QueryWrapper<MeiteStrategy>();
queryWrapper.eq("strategy_id", strategyId);
queryWrapper.eq("strategy_type", strategyType);
MeiteStrategy meiteStrategy = strategyMapper.selectOne(queryWrapper);
if (meiteStrategy == null) {
return null;
}
String strategyBeanId = meiteStrategy.getStrategyBeanId();
if (StringUtils.isEmpty(strategyBeanId)) {
return null;
}
return SpringUtils.getBean(strategyBeanId, t);
}
}
@Data
@TableName("meite_strategy")
public class MeiteStrategy {
private Long id;
private String strategyName;
private String strategyId;
private String strategyType;
private String strategyBeanId;
}
public interface StrategyMapper extends BaseMapper<MeiteStrategy> {
}
运行结果:
7 策略模式实现动态开关
策略模式好处:灵活动态开关,数据库增加字段表示隐藏和展示
数据库增加字段表示逻辑删除
ALTER TABLE `meite_strategy` ADD `deleted` tinyint(1) DEFAULT 0 COMMENT '逻辑删除字段';
策略实体类MeiteStrategy增加字段
@TableLogic
private Integer deleted = 0;
运行结果(查看mapper日志,mybatis-plus底层封装条件deleted = 0):
源码下载地址(mayikt_designPattern_8.zip):
链接:https://pan.baidu.com/s/1wWKZN1MbXICZVW1Vxtwe6A
提取码:fire