基于【策略模式】设计多渠道发送消息

时间:2022-07-24
本文章向大家介绍基于【策略模式】设计多渠道发送消息,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

前言:设计模式源于生活

策略模式的基本概念

策略模式将可变的部分从程序中抽象分离成算法接口,在该接口下分别封装一系列算法实现,并使他们可以相互替换,从而导致客户端程序独立于算法的改变。

策略模式应用场景

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 '灵活-多种策略模式rn消息rn邮箱rn支付rn等等',
  `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 strategyBeanIdn" +
            "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

到此策略模式就结束啦