经典设计模式之策略模式【如何重构聚合支付平台,对接【支付宝,微信,银联支付】】

写在前面:设计模式源于生活,而又高于生活!
为什么要使用设计模式重构代码

使用设计模式可以重构整体架构代码、提高代码复用性、扩展性、减少代码冗余问题。
 
Java高级工程师装逼的技能!
什么是策略模式

策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理,最终可以实现解决多重if判断问题。

1.环境(Context)角色:持有一个Strategy的引用
2.抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
3.具体策略(ContextStrategy)角色:包装了相关的算法或行为。
策略模式应用场景

比如搭建聚合支付平台的时候,这时候需要对接很多第三方支付接口,比如支付宝、微信支付、银联支付等。通过传统if代码判断的,后期的维护性非常差!

public  String toPayHtml2(String payCode){
    if(payCode.equals("ali_pay")){
        return  "调用支付宝接口...";
    }
    if(payCode.equals("union_pay")){
        return  "调用银联支付接口";
    }
    if(payCode.equals("weChat_pay")){
        return  "调用微信支付接口...";
    }
    return  "未找到该接口...";
}
这时候可以通过策略模式解决多重if判断问题。

策略模式架构图


策略模式环境搭建
Maven依赖信息


        org.springframework.boot
        spring-boot-starter-parent
        2.0.1.RELEASE
   

   
       
       
            org.springframework.boot
            spring-boot-starter-web
       

       
            org.projectlombok
            lombok
            1.16.10
       

       
            commons-lang
            commons-lang
            2.6
       

       
            org.mybatis.spring.boot
            mybatis-spring-boot-starter
            1.1.1
       

       
       
            mysql
            mysql-connector-java
       

   

PayStrategy(抽象角色)

/**
 * @title: PayStrategy  共同算法定义的骨架
 */
public interface PayStrategy {
    /**
     *  策略模式共同算法的骨架
     */
    String toPayHtml();
}
ConcreteStrategy (具体实现角色)

/**
 * @title: AliPayStrategy
 */
@Component
public class AliPayStrategy implements PayStrategy {
 
    @Override
    public String toPayHtml() {
        return "调用支付宝支付接口...";
    }
}
@Component
public class UnionPayStrategy implements PayStrategy {
    @Override
    public String toPayHtml() {
        return "调用银联支付接口...";
    }
}
@Component
public class WeChatPayStrategy implements PayStrategy {
    @Override
    public String toPayHtml() {
        return "调用微信支付接口";
    }
}
PayContextService (上下文)

/**
 * @title: PayContextStrategy
 */
@Component
public class PayContextStrategy {
    @Autowired
    private PaymentChannelMapper paymentChannelMapper;
    @Autowired
    private SpringUtils springUtils;
 
    public String toPayHtml(String payCode){
        //1.使用payCode参数查询数据库获取beanid
        PaymentChannelEntity paymentChannel = paymentChannelMapper.getPaymentChannel(payCode);
        if(paymentChannel==null){
            return BaseReturnInfo.PAYMENTCHANNEL_IS_NULL;
        }
        //2.获取到beanid之后,使用spring容器获取实例对象
        String strategyBeanId = paymentChannel.getStrategyBeanId();
        if(StringUtils.isBlank(strategyBeanId)){
            return BaseReturnInfo.STRATEGYBEANID_IS_BLANK;
        }
        // 3.执行该实现的方法即可.... aliPayStrategy
        PayStrategy payStrategy = springUtils.getBean(strategyBeanId, PayStrategy.class);
        // 4.执行具体策略算法
        return payStrategy.toPayHtml();
    }
}
SpringUtils

/**
 *  使用beanid 获取spring容器中的bean对象
 */
@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 getBean(Class clazz){
        return getApplicationContext().getBean(clazz);
    }
 
    //通过name,以及Clazz返回指定的Bean
    public static T getBean(String name,Class clazz){
        return getApplicationContext().getBean(name, clazz);
    }
 
}
 

数据库访问层

/*
 Navicat MySQL Data Transfer
 Source Server         : MySQL
 Source Server Type    : MySQL
 Source Server Version : 50720
 Source Host           : localhost:3306
 Source Schema         : design_pattern
 Target Server Type    : MySQL
 Target Server Version : 50720
 File Encoding         : 65001
 Date: 08/05/2019 09:20:48
*/
 
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
 
-- ----------------------------
-- Table structure for payment_channel
-- ----------------------------
DROP TABLE IF EXISTS `payment_channel`;
CREATE TABLE `payment_channel`  (
  `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `CHANNEL_NAME` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '渠道名称',
  `CHANNEL_ID` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '渠道ID',
  `strategy_bean_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '策略执行beanid',
  PRIMARY KEY (`ID`, `CHANNEL_ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '支付渠道 ' ROW_FORMAT = Dynamic;
 
-- ----------------------------
-- Records of payment_channel
-- ----------------------------
INSERT INTO `payment_channel` VALUES (4, '支付宝渠道', 'ali_pay', 'aliPayStrategy');
INSERT INTO `payment_channel` VALUES (5, '银联支付渠道', 'union_pay', 'unionPayStrategy');
INSERT INTO `payment_channel` VALUES (6, '微信支付渠道', 'wechat_pay', 'weChatPayStrategy');
 
SET FOREIGN_KEY_CHECKS = 1;
数据库访问层

@Data
public class PaymentChannelEntity {
   /** ID */
   private Integer id;
   /** 渠道名称 */
   private String channelName;
   /** 渠道ID */
   private String channelId;
   /**
    * 策略执行beanId
    */
   private String strategyBeanId;
 
}
Mapper层

public interface PaymentChannelMapper {
     @Select("\n" +
             "SELECT  id as id ,CHANNEL_NAME as CHANNELNAME ,CHANNEL_ID as CHANNELID,strategy_bean_id AS strategybeanid\n" +
             "FROM payment_channel where CHANNEL_ID=#{payCode}")
     public PaymentChannelEntity getPaymentChannel(String payCode);
}
BaseReturnInfo 

public interface BaseReturnInfo {
 
    String  PAYMENTCHANNEL_IS_NULL="没有该渠道信息";
 
    String STRATEGYBEANID_IS_BLANK="该渠道没有配置beanid";
 
    String PAYCODE_IS_BLANK="渠道code不能为空";
}
Controller层

/**
 * @title: PayController
 */
@RestController
public class PayController {
    @Autowired
    private PayContextStrategy payContextStrategy;
 
    @RequestMapping("/toPayHtml")
    public  String toPayHtml(String payCode){
        if(StringUtils.isBlank(payCode)){
            return BaseReturnInfo.PAYCODE_IS_BLANK;
        }
        return payContextStrategy.toPayHtml(payCode);
    }
}
application.yml

###服务启动端口号
server:
  port: 8080
spring:
###数据库相关连接      
  datasource:
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/design_pattern?useUnicode=true&characterEncoding=UTF-8&useSSL=true
####打印MyBatias日志    
logging:
  level:
  ### 开发环境使用DEBUG 生产环境info或者error
   com.xuyu.mapper: DEBUG
启动类

@SpringBootApplication
@MapperScan("com.xuyu.mapper")
@EnableAutoConfiguration
public class AppSpringBoot {
    public static void main(String[] args) {
        SpringApplication.run(AppSpringBoot.class);
    }
}
效果

优点:策略模式最终帮助我们解决在实际开发中多重if判断问题、提高扩展性、维护性增强、提高代码可读性。
缺点:后期维护不同策略类是非常多、定义类比较多、代码量增大。
优点大于缺点。
--------------------- 
作者:力软快速开发平台 
来源:CSDN 
原文:https://blog.csdn.net/weixin_42831704/article/details/90750923 
版权声明:本文为博主原创文章,转载请附上博文链接!

你可能感兴趣的:(Spring核心技术,设计模式)