精讲23种设计模式-008:基于策略模式重构if判断

1 策略模式课程介绍

课程内容

  1. 什么是策略模式
  2. 策略模式的应用场景
  3. 策略模式的实现方案有哪些?
  4. 基于工厂模式实现策略模式
  5. 基于数据库方式实现策略模式
  6. 站在架构师角度,设计多行为策略模式框架

2 策略模式基本介绍与应用场景

策略模式基本概念
解决多重if判断问题 有多个不同的策略,选择其中一种策略

策略模式应用场景

  1. 联合登录 QQ联合、微信联合、钉钉联合登录框架
    存在共同的抽象行为 联合登录操作
    if (login_type=”qq”){
    //对接QQ
    }
    if (login_type=”weixin”){
    //对接微信
    }
    if (login_type=”钉钉”){
    //对接微信
    }
    后期维护性太差,要专门通过数据库策略表进行维护管理
  2. 发送短信提醒 对接阿里云、腾讯云、其他短信服务商,保证高可用
  3. 视频网站 播放视频的时候 展示线路1、线路2
  4. 聚合支付系统 对接支付宝、银联支付、微信支付

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值