基于【策略模式】设计多渠道发送消息
2020-08-15 本文已影响0人
Sunny捏
前言:设计模式源于生活
策略模式的基本概念
策略模式将可变的部分从程序中抽象分离成算法接口,在该接口下分别封装一系列算法实现,并使他们可以相互替换,从而导致客户端程序独立于算法的改变。
策略模式应用场景
1.解决我多重if条件判断
2.有共同行为,但是有不同的业务逻辑(例如:支付模式[支持多种支付模式],直播线路模式[支持多种线路切换],消息发送渠道模式[支持多种消息渠道发送])
策略模式优缺点
优点:
1.扩展性良好
2.算法自由切换
3.更好的代码复用性
缺点:
1.策略类会随着扩展增多,不方便维护
2.需要了解更多的业务细节
策略模式流程图
1.工厂维护行为(FactoryContext)角色:定义出一个工厂维护对象的引用的上下文方法,也是对外的唯一入口
2.抽象策略行为(Strategy)角色:定义出一个具有共同行为的接口,这个角色通常由一个Java抽象类或者Java接口实现
3.具体策略执行(ConcreteStrategy)角色:具体执行者接到转发请求后,直接执行业务逻辑
接下来我将通过三种方式实现策略模式
通用maven依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.11</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
<!-- mysql 依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
通用工具类
@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);
}
}
基于静态内存模式,实现策略模式
定义抽象行为策略
public abstract class BehaviorStrategy {
/**
* 执行具体消息策略模式
*/
protected abstract void specificMsgStrategy ();
}
kafka消息渠道
@Slf4j
public class KafkaStrategy extends BehaviorStrategy {
@Override
protected void specificMsgStrategy() {
log.info("执行kafkaMQ消息模式发送消息");
}
}
rabbit消息渠道
@Slf4j
public class RabbitStrategy extends BehaviorStrategy {
@Override
protected void specificMsgStrategy() {
log.info("执行rabbitMQ消息模式发送消息");
}
}
rocket消息渠道
@Slf4j
public class RocketStrategy extends BehaviorStrategy {
@Override
protected void specificMsgStrategy() {
log.info("执行rocketMQ消息模式发送消息");
}
}
工厂模式类,用于维护对象的引用
public class FactoryStrategy {
/**
* 存储策略
*/
private static Map<String, BehaviorStrategy> stringBehaviorStrategyMap = new ConcurrentHashMap<>();
/**
* 初始化策略
*/
static {
stringBehaviorStrategyMap.put("kafka", new KafkaStrategy());
stringBehaviorStrategyMap.put("rabbit", new RabbitStrategy());
stringBehaviorStrategyMap.put("rocket", new RocketStrategy());
}
/**
* 执行具体策略
* @param strategyId
* @return
*/
public static BehaviorStrategy getStrategy(String strategyId) {
//待优化,如果查询空,友好提示
return stringBehaviorStrategyMap.get(strategyId);
}
}
测试类
public class Test {
public static void main(String[] args) {
BehaviorStrategy kafka = FactoryStrategy.getStrategy("kafka");
kafka.specificMsgStrategy();
}
}
效果图,选择策略是kafka模式,那么执行的业务逻辑就是kafka啦
基于工厂+spring容器实现策略模式
定义抽象行为策略
public abstract class BehaviorStrategy {
/**
* 执行具体消息策略模式
*/
public abstract void specificMsgStrategy ();
}
kafka消息渠道
@Slf4j
@Component
public class KafkaStrategy extends BehaviorStrategy {
@Override
public void specificMsgStrategy() {
log.info("执行kafkaMQ消息模式发送消息");
}
}
rabbit消息渠道
@Slf4j
@Component
public class RabbitStrategy extends BehaviorStrategy {
@Override
public void specificMsgStrategy() {
log.info("执行rabbitMQ消息模式发送消息");
}
}
rocket消息渠道
@Slf4j
@Component
public class RocketStrategy extends BehaviorStrategy {
@Override
public void specificMsgStrategy() {
log.info("执行rocketMQ消息模式发送消息");
}
}
工厂模式,通过将策略具体执行者,注入到容器中,工厂通过beanid到容器中执行不同的策略具体执行者,相比第一种就简单很多
@Component
public class FactoryStrategy {
public <T> T getStrategy(String strategyId,Class<T> t) {
if (StringUtils.isBlank(strategyId)) {
return null;
}
T bean = SpringUtils.getBean(strategyId, t);
return bean;
}
}
controller类
@RestController
public class StrategyController {
@Autowired
private FactoryStrategy factoryStrategy;
@GetMapping("/getStrategy")
public void getStrategy(@RequestParam("strategyId") String strategyId) {
BehaviorStrategy strategy = factoryStrategy.getStrategy(strategyId, BehaviorStrategy.class);
strategy.specificMsgStrategy();
}
}
效果图
访问:http://localhost:8080/getStrategy?strategyId=rabbitStrategy
当参数策略值为:rabbit具体执行者,那么工厂会自动到容器找到相应的执行者执行业务逻辑
基于spring+db+工厂实现策略模式
sql语句
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for strategy
-- ----------------------------
DROP TABLE IF EXISTS `strategy`;
CREATE TABLE `strategy` (
`id` int(11) 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` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '灵活-多种策略模式\r\n消息\r\n邮箱\r\n支付\r\n等等',
`strategy_bean_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '策略执行beanid',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '策略' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of strategy
-- ----------------------------
INSERT INTO `strategy` VALUES (1, 'kafka消息渠道', 'kafka', 'mq', 'kafkaDBStrategy');
INSERT INTO `strategy` VALUES (2, 'rabbitmq消息渠道', 'rabbit', 'mq', 'rabbitDBStrategy');
INSERT INTO `strategy` VALUES (3, 'rocketmq消息渠道', 'rouket', 'mq', 'rocketDBStrategy');
INSERT INTO `strategy` VALUES (4, '163邮箱渠道', '163', 'email', '163EmailStrategy');
SET FOREIGN_KEY_CHECKS = 1;
application.yml文件
###服务启动端口号
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/strategy?useUnicode=true&characterEncoding=UTF-8&useSSL=true&serverTimezone=UTC
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
####打印MyBatias日志
logging:
level:
### 开发环境使用DEBUG 生产环境info或者error
com.xuyu.mapper: DEBUG
entity类
@Data
@TableName("strategy")
public class StrategyEntity {
@TableId
private Integer id;
@TableField("strategy_name")
private String strategyName;
@TableField("strategy_id")
private String strategyId;
@TableField("strategy_type")
private String strategyType;
@TableField("strategy_bean_id")
private String strategyBeanId;
}
定义抽象行为策略
public abstract class BehaviorStrategy {
/**
* 执行具体消息策略模式
*/
protected abstract void specificMsgStrategy ();
}
mapper类
@Repository
public interface StrategyMapper {
/**
* 根据策略id和类型查询不同的策略
*
* @param strategyId 策略id
* @param strategyType 策略类型
* @return
*/
@Select("SELECT id as id, strategy_name as strategyName, strategy_id as strategyId, strategy_type as strategyType, strategy_bean_id as strategyBeanId\n" +
"FROM strategy s WHERE s.strategy_id = #{strategyId} and strategy_type = #{strategyType}")
StrategyEntity getStrategyByStrategyId(@Param("strategyId") String strategyId, @Param("strategyType") String strategyType);
}
kafka消息渠道
@Component
@Slf4j
public class KafkaDBStrategy extends BehaviorStrategy {
@Override
public void specificMsgStrategy() {
log.info("执行kafkaMQ消息模式发送消息");
}
}
rabbit消息渠道
@Component
@Slf4j
public class RabbitDBStrategy extends BehaviorStrategy {
@Override
public void specificMsgStrategy() {
log.info("执行rabbitMQ消息模式发送消息");
}
}
rocket消息渠道
@Component
@Slf4j
public class RocketDBStrategy extends BehaviorStrategy {
@Override
public void specificMsgStrategy() {
log.info("执行rocketMQ消息模式发送消息");
}
}
工厂模式,通过将策略具体执行者,注入到容器中,工厂通过beanid到容器中执行不同的策略具体执行者,相比第一种就简单很多
@Component
public class FactoryDBStrategy {
@Autowired
private StrategyMapper strategyMapper;
public <T> T getStrategy(String strategyId, String type, Class<T> t) {
if (StringUtils.isBlank(strategyId)) {
return null;
}
if (StringUtils.isBlank(type)) {
return null;
}
StrategyEntity strategyEntity = strategyMapper.getStrategyByStrategyId(strategyId, type);
if (Objects.isNull(strategyEntity)) {
return null;
}
T bean = SpringUtils.getBean(strategyEntity.getStrategyBeanId(), t);
return bean;
}
}
controller类
@RestController
public class StrategyDBController {
@Autowired
private FactoryDBStrategy factoryDBStrategy;
@GetMapping("/getDBStrategy")
public void getStrategy(@RequestParam("strategyId") String strategyId, @RequestParam("strategyType") String strategyType) {
BehaviorStrategy strategy = factoryDBStrategy.getStrategy(strategyId, strategyType, BehaviorStrategy.class);
strategy.specificMsgStrategy();
}
}
效果图
访问:http://localhost:8080/getDBStrategy?strategyId=rouket&strategyType=mq
到此策略模式就结束啦