文章目录
- 一、设计模式
- 1. 为什么需要使用设计模式
- 2. 设计模式的分类
- 3. 什么是策略模式
- 4. 为什么叫做策略模式
- 5. 策略模式优缺点
- 6. 策略模式应用场景
- 7. Spring框架中使用的策略模式
- 二、策略模式~聚合短信服务
- 2.1. 依赖引入
- 2.2. 抽象公共行为接口
- 2.3. 具体策略接口实现类
- 2.4. 策略枚举
- 2.5. 获取具体策略实现
- 2.6. 策略工厂
- 2.7. 聚合短信服务测试
- 三、聚合短信服务2
- 3.1. 策略工厂调整
- 3.2. 聚合短信测试
- 四、聚合短信3
- 4.1. 策略上下文
- 4.2. SpringContext上下文工具类
- 4.3. 聚合短信测试
- 五、聚合短信+聚合支付(企业内部升级)
- 5.1. 相关SQL语句
- 5.2. 策略实体
- 5.3. 策略接口
- 5.4.策略上下文
- 5.5. 聚合短信和聚合支付测试
- 5.6. mapper扫描配置
- 5.7. 依赖
- 5.8. yml配置
- 5.9. 开源地址
一、设计模式
1. 为什么需要使用设计模式
使用设计模式可以重构整体架构代码、提交代码复用性、扩展性、减少代码冗余问题。
2. 设计模式的分类
创建型模式
工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
。
结构型模式
适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式
行为模式
策略模式、模板方法模式、观察者模式
、迭代子模式、责任链模式
、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
策略模式
3. 什么是策略模式
策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理,最终可以实现解决多重if判断问题。
1.环境(Context)角色:持有一个Strategy的引用。
2.抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
3.具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。
定义策略接口->实现不同的策略类->利用多态或其他方式调用策略
4. 为什么叫做策略模式
每个if判断都可以理解为就是一个策略。
5. 策略模式优缺点
优点
算法可以自由切换(高层屏蔽算法,角色自由切换)
避免使用多重条件判断(如果算法过多就会出现很多种相同的判断,很难维护)
扩展性好(可自由添加取消算法 而不影响整个功能)
缺点
策略类数量增多(每一个策略类复用性很小,如果需要增加算法,就只能新增类)
所有的策略类都需要对外暴露(使用的人必须了解使用策略,这个就需要其它模式来补充,比如工厂模式、代理模式)
6. 策略模式应用场景
聚合支付平台
比如搭建聚合支付平台的时候,这时候需要对接很多第三方支付接口,比如支付宝、微信支付、小米支付等。
通过传统if代码判断的,后期的维护性非常差!
这时候可以通过策略模式解决多重if判断问题。
7. Spring框架中使用的策略模式
ClassPathXmlApplicationContext Spring底层Resource接口采用策略模式
Spring 为 Resource 接口提供了如下实现类:
UrlResource:访问网络资源的实现类。
ClassPathResource:访问类加载路径里资源的实现类。
FileSystemResource:访问文件系统里资源的实现类。
ServletContextResource:访问相对于 ServletContext 路径里的资源的实现类:
InputStreamResource:访问输入流资源的实现类。
ByteArrayResource:访问字节数组资源的实现类。
1、new ClassPathXmlApplicationContext("");
2.进入该构造函数
4.SpringBean初始化 SimpleInstantiationStrategy
SimpleInstantiationStrategy 简单初始化策略
CglibSubclassingInstantiationStrategy CGLIB初始化策略
二、策略模式~聚合短信服务
2.1. 依赖引入
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.4</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.gblfy</groupId><artifactId>design-pattern</artifactId><version>0.0.1-SNAPSHOT</version><name>design-pattern</name><description>design-pattern</description><properties><java.version>1.8</java.version></properties><dependencies><!--字符串工具类--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.12.0</version></dependency><!--数据json处理--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.79</version></dependency><!--SpringMVC--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>
2.2. 抽象公共行为接口
MsgStrategy
package com.gblfy.strategy;/*** 抽象公共行为接口** @author gblfy* @date 2022-03-13*/
public interface MsgStrategy {/*** 共同行为方法** @param phone* @return*/String sendMsg(String phone);
}
2.3. 具体策略接口实现类
调用(阿里云)短信息服务
package com.gblfy.strategy.impl;import com.gblfy.strategy.MsgStrategy;
import org.springframework.stereotype.Service;/*** 调用(阿里云)短信息服务** @author gblfy* @date 2022-03-13*/
@Service
public class AliYunStrategy implements MsgStrategy {@Overridepublic String sendMsg(String phone) {return "调用(阿里云)短信息服务";}
}
调用(华为云)短信息服务
package com.gblfy.strategy.impl;import com.gblfy.strategy.MsgStrategy;
import org.springframework.stereotype.Service;/*** 调用(华为云)短信息服务** @author gblfy* @date 2022-03-13*/
@Service
public class HuaWeiStrategy implements MsgStrategy {@Overridepublic String sendMsg(String phone) {return "调用(华为云)短信息服务";}
}
调用(腾讯云)短信息服务
package com.gblfy.strategy.impl;import com.gblfy.strategy.MsgStrategy;
import org.springframework.stereotype.Service;/*** 调用(腾讯云)短信息服务** @author gblfy* @date 2022-03-13*/
@Service
public class TencentStrategy implements MsgStrategy {@Overridepublic String sendMsg(String phone) {return "调用(腾讯云)短信息服务";}
}
2.4. 策略枚举
package com.gblfy.enums;/*** 策略枚举,存放所有策略的实现** @author gblfy* @date 2022-03-13*/
public enum SmsEnumStrategy {/*** 支付宝短信*/ALI_SMS("com.gblfy.strategy.impl.AliYunStrategy"),/*** 华为云短信*/HUAWEI_SMS("com.gblfy.strategy.impl.HuaWeiStrategy"),/*** 腾讯云短信*/TENCENT_SMS("com.gblfy.strategy.impl.TencentStrategy");/*** class 名称*/private String className;SmsEnumStrategy(String className) {this.setClassName(className);}public String getClassName() {return className;}public void setClassName(String className) {this.className = className;}
}
2.5. 获取具体策略实现
package com.gblfy.strategy;import com.gblfy.factory.StrategyFactory;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;/*** 获取具体策略实现** @author gblfy* @date 2022-03-13*/
@Component
public class SmsContextStrategy {/*** 获取具体策略实现*/public String getStrategy(String strategyId, String phone) {if (StringUtils.isEmpty(strategyId)) {return "paycode 不能为空";}// 第1种:使用策略工厂获取具体策略的实现MsgStrategy msgStrategy = StrategyFactory.getPayStrategy(strategyId);if (msgStrategy == null) {return ",没有找到具体策略的实现...";}return msgStrategy.sendMsg(phone);}
}
2.6. 策略工厂
package com.gblfy.factory;import com.gblfy.enums.SmsEnumStrategy;
import com.gblfy.strategy.MsgStrategy;/*** 使用策略工厂获取具体策略实现** @author gblfy* @date 2022-03-13*/
public class StrategyFactory {//工厂初始化public static MsgStrategy getPayStrategy(String strategyType) {try {// 1.获取枚举中classNameString className = SmsEnumStrategy.valueOf(strategyType).getClassName();// 2.使用java反射技术初始化类return (MsgStrategy) Class.forName(className).newInstance();} catch (Exception e) {return null;}}
}
2.7. 聚合短信服务测试
/*** 测试链接:* http://localhost:8080/sendMsgByEnumSfactory?strategyId=ALI_SMS&phone=123456* http://localhost:8080/sendMsgByEnumSfactory?strategyId=HUAWEI_SMS&phone=123456* http://localhost:8080/sendMsgByEnumSfactory?strategyId=TENCENT_SMS&phone=123456*/
package com.gblfy.controller;import com.gblfy.strategy.SmsContextStrategy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class MsgController {@Autowiredprivate SmsContextStrategy contextStrategy;/*** 使用枚举+工厂+策略模式实现聚合短信服务* * @param strategyId 策略ID* @param phone 手机号码* @return*/@GetMapping("/sendMsgByEnumSfactory")public String sendMsgByEnumSfactory(@RequestParam("strategyId") String strategyId,@RequestParam("phone") String phone) {return contextStrategy.getStrategy(strategyId, phone);}/*** 测试链接:* http://localhost:8080/sendMsgByEnumSfactory?strategyId=ALI_SMS&phone=123456* http://localhost:8080/sendMsgByEnumSfactory?strategyId=HUAWEI_SMS&phone=123456* http://localhost:8080/sendMsgByEnumSfactory?strategyId=TENCENT_SMS&phone=123456*/
}
三、聚合短信服务2
1.使用工厂模式初始化具体策略class
2.将所有具体实现的策略存放到map集合中(枚举类中)
3.key:ALI_PAY value:com.gblfy.service.AliPayStrategy
3.1. 策略工厂调整
package com.gblfy.factory;import com.gblfy.strategy.MsgStrategy;
import com.gblfy.strategy.impl.AliYunStrategy;
import com.gblfy.strategy.impl.HuaWeiStrategy;
import com.gblfy.strategy.impl.TencentStrategy;import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;public class FactoryStrategy {private static Map<String, MsgStrategy> msgStrategyMap = new ConcurrentHashMap<>();static {msgStrategyMap.put("huawei", new HuaWeiStrategy());msgStrategyMap.put("tencent", new TencentStrategy());msgStrategyMap.put("aliyun", new AliYunStrategy());}public static MsgStrategy getContextStrategy(String strategyId) {return msgStrategyMap.get(strategyId);}
}
3.2. 聚合短信测试
package com.gblfy.controller;import com.gblfy.factory.FactoryStrategy;
import com.gblfy.strategy.MsgStrategy;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class MsgController {/*** 使用工厂+策略模式实现聚合短信服务** @param strategyId* @param phone* @return*/@GetMapping("/sendMsgByfactory")public String sendMsgByfactory(@RequestParam("strategyId") String strategyId,@RequestParam("phone") String phone) {MsgStrategy contextStrategy = FactoryStrategy.getContextStrategy(strategyId);return contextStrategy.sendMsg(phone);}/*** 测试链接:* http://localhost:8080/sendMsgByfactory?strategyId=huawei&phone=123456* http://localhost:8080/sendMsgByfactory?strategyId=tencent&phone=123456* http://localhost:8080/sendMsgByfactory?strategyId=aliyun&phone=123456*/
}
四、聚合短信3
使用springIOC代替反射,提高效率,动态切换(无需改动项目)
4.1. 策略上下文
package com.gblfy.strategy;import com.gblfy.utils.SpringContextUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;@Component
public class StrategyContext {public MsgStrategy getStrategy(String strategyId) {if (StringUtils.isEmpty(strategyId)) {return null;}return SpringContextUtils.getBean(strategyId, MsgStrategy.class);}}
4.2. SpringContext上下文工具类
package com.gblfy.utils;import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;/*** 获取Spring上下文工具类** @author gblfy* @date 2022-03-13*/
@Component
public class SpringContextUtils implements ApplicationContextAware {private static ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {SpringContextUtils.applicationContext = applicationContext;}//获取applicationContextpublic 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返回指定的Beanpublic static <T> T getBean(String name, Class<T> clazz) {return getApplicationContext().getBean(name, clazz);}}
4.3. 聚合短信测试
/*** 使用工厂+策略模式+SpringIOC实现聚合短信服务** @param strategyId* @param phone* @return*/@GetMapping("/sendMsgBySpringIOC")public String sendMsgBySpringIOC(@RequestParam("strategyId") String strategyId,@RequestParam("phone") String phone) {MsgStrategy contextStrategy = strategyContext.getStrategy(strategyId);return contextStrategy.sendMsg(phone);}/*** 测试链接:* http://localhost:8080/sendMsgBySpringIOC?strategyId=aliYunStrategy&phone=123456* http://localhost:8080/sendMsgBySpringIOC?strategyId=huaWeiStrategy&phone=123456* http://localhost:8080/sendMsgBySpringIOC?strategyId=tencentStrategy&phone=123456*/
五、聚合短信+聚合支付(企业内部升级)
5.1. 相关SQL语句
drop database IF EXISTS `design_pattern`;
create database `design_pattern`;
use `design_pattern`;SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for gblfy_strategy
-- ----------------------------
DROP TABLE IF EXISTS `gblfy_strategy`;
CREATE TABLE `gblfy_strategy` (`id` int NOT NULL AUTO_INCREMENT 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 NOT NULL COMMENT '策略类型(0-支付服务,1-短信服务)',`strategy_bean_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '策略执行beanid实例',`deleted` int NOT NULL COMMENT '逻辑删除字段(0-有效 1-无效,默认为0)',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '策略配置表' ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of gblfy_strategy
-- ----------------------------
INSERT INTO `gblfy_strategy` VALUES (6, '腾讯云短信', 'tencent_sms', 1, 'tencentStrategy', 0);
INSERT INTO `gblfy_strategy` VALUES (7, '阿里云短信', 'aliYun_sms', 1, 'aliYunStrategy', 0);
INSERT INTO `gblfy_strategy` VALUES (8, '华为云短信', 'huaWei_sms', 1, 'huaWeiStrategy', 0);
INSERT INTO `gblfy_strategy` VALUES (9, '阿里支付', 'ali_pay', 0, 'aliPayStrategy', 0);
INSERT INTO `gblfy_strategy` VALUES (10, '银联支付', 'yinlian_pay', 0, 'unionPayStrategy', 0);SET FOREIGN_KEY_CHECKS = 1;
5.2. 策略实体
package com.gblfy.entity;import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;@Data
@TableName("gblfy_strategy")
public class GblfyStrategy {// 策略配置主键@TableId(value = "id", type = IdType.ASSIGN_ID)private Long id;//策略名称(阿里云短信、银联支付)@TableField("strategy_name")private String strategyName;//策略ID@TableField("strategy_id")private String strategyId;//策略类型(发短信或者调用支付)@TableField("strategy_type")private String strategyType;//策略具体执行beanId@TableField("strategy_bean_id")private String strategyBeanId;//逻辑删除字段@TableLogicprivate Integer deleted;
}
5.3. 策略接口
package com.gblfy.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gblfy.entity.GblfyStrategy;/*** 策略接口** @author gblfy* @date 2022-03-13*/
public interface StragegyMapper extends BaseMapper<GblfyStrategy> {
}
5.4.策略上下文
package com.gblfy.strategy;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.gblfy.entity.GblfyStrategy;
import com.gblfy.mapper.StragegyMapper;
import com.gblfy.utils.SpringContextUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;@Component
public class StrategyContext {@Autowiredprivate StragegyMapper stragegyMapper;// public MsgStrategy getStrategy(String strategyId) {// if (StringUtils.isEmpty(strategyId)) {// return null;// }// return SpringContextUtils.getBean(strategyId, MsgStrategy.class);// }// public <T> T getStrategy(String stragegyId,Class<T> t){// if (StringUtils.isEmpty(stragegyId)){// return null;// }// return SpringContextUtils.getBean(stragegyId, t);// }public <T> T getStrategy(String stragegyId, String stragegyType, Class<T> t) {if (StringUtils.isEmpty(stragegyId)) {return null;}if (StringUtils.isEmpty(stragegyType)) {return null;}if (t == null) {return null;}//根据策略id和策略类型查询具体策略实例beanIdGblfyStrategy gblfyStrategy = stragegyMapper.selectOne(new QueryWrapper<GblfyStrategy>().lambda().eq(GblfyStrategy::getStrategyId, stragegyId).eq(GblfyStrategy::getStrategyType, stragegyType));if (gblfyStrategy == null) {return null;}String strategyBeanId = gblfyStrategy.getStrategyBeanId();if (StringUtils.isEmpty(strategyBeanId)) {return null;}return SpringContextUtils.getBean(strategyBeanId, t);}
}
5.5. 聚合短信和聚合支付测试
/*** 多个不同服务(短信+支付)抽象封装* mysql+SpringIOC+策略模式实现聚合短信服务和聚合支付服务** @param strategyId* @param stragegyType* @param phone* @return*/@GetMapping("/sendMsgByMysqlAndSpringIOC")public String sendMsgBySpringIOC(@RequestParam("strategyId") String strategyId,@RequestParam("stragegyType") String stragegyType,@RequestParam("phone") String phone) {MsgStrategy strategy = strategyContext.getStrategy(strategyId, stragegyType, MsgStrategy.class);if (strategy == null) {return "当前渠道已关闭或者不存在,请核实!";}return strategy.sendMsg(phone);}/*** 测试链接(聚合短信):* http://localhost:8080/sendMsgByMysqlAndSpringIOC?strategyId=ali_pay&stragegyType=1&phone=123456* http://localhost:8080/sendMsgByMysqlAndSpringIOC?strategyId=ali_pay&stragegyType=1&phone=123456* http://localhost:8080/sendMsgByMysqlAndSpringIOC?strategyId=ali_pay&stragegyType=1&phone=123456** 测试链接(聚合支付):* http://localhost:8080/sendMsgByMysqlAndSpringIOC?strategyId=ali_pay&stragegyType=0&phone=123456*/
5.6. mapper扫描配置
package com.gblfy.config;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;@Configuration
@MapperScan("com.gblfy.mapper")
public class MybatisPlusConfig {}
5.7. 依赖
<!--mybatis-plus 持久化--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency>
5.8. yml配置
server:port: 8080
spring:datasource:username: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/design_pattern?useUnicode=true&characterEncoding=UTF-8
logging:level:com.gblfy.mapper: DEBUG
mybatis-plus:configuration:log-impl:mapper-locations: classpath:mappers/*.xml
5.9. 开源地址
https://gitee.com/gblfy/design-pattern