精讲23种设计模式-策略模式~聚合短信服务和聚合支付服务

一、设计模式
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 {
    
    
    @Override
    public 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 {
    
    
    @Override
    public 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 {
    
    
    @Override
    public 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.获取枚举中className
            String 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 {
    
    

    @Autowired
    private 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;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    
    
        SpringContextUtils.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);
    }

}
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;
    //逻辑删除字段
    @TableLogic
    private 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 {
    
    
    @Autowired
    private 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和策略类型查询具体策略实例beanId
        GblfyStrategy 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: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: 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

猜你喜欢

转载自blog.csdn.net/weixin_40816738/article/details/123464338