文档章节

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

须臾之余
 须臾之余
发布于 05/08 09:29
字数 1336
阅读 2137
收藏 74

写在前面:设计模式源于生活,而又高于生活!

为什么要使用设计模式重构代码

使用设计模式可以重构整体架构代码、提高代码复用性、扩展性、减少代码冗余问题。

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依赖信息

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
    </parent>
    <dependencies>
        <!-- sprinboot web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.10</version>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.1.1</version>
        </dependency>
        <!-- mysql 依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>

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> 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);
    }

}

 

数据库访问层

/*
 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判断问题、提高扩展性、维护性增强、提高代码可读性。
缺点:后期维护不同策略类是非常多、定义类比较多、代码量增大。
优点大于缺点。

 

© 著作权归作者所有

须臾之余
粉丝 33
博文 7
码字总数 9956
作品 0
吉安
程序员
私信 提问
加载中

评论(18)

page_zxy
page_zxy

引用来自“Skqing”的评论

跟工厂模式啥区别

引用来自“须臾之余”的评论

工厂模式:工厂接口定义了所有子工厂执行标准,各个产品的生产都有各自的工厂,生产的工艺,生成的高科技程度都是不一样的,用户只需要关心生产的厂家。
策略模式:过程不一样,但结果一样,关心过程。比如聚合支付统一返回form表单
👍
须臾之余
须臾之余

引用来自“Totoro1024”的评论

你这是强行使用设计模式,还用的不对,这不是策略模式
这确实是策略模式呀,而且设计模式就是这样使用的
Totoro1024
Totoro1024
你这是强行使用设计模式,还用的不对,这不是策略模式
须臾之余
须臾之余

引用来自“暴走的懒羊羊”的评论

说一下我的看法,大家讨论下。 首先策略模式要解决的是:不同算法之间相互替换,而不是多if,else!多个if-else可以用责任链模式来解决! 其次,数据从前端流到后端,再流到数据库,然后又回到后端,就是为了解决一个问题:哪个算法跟这个参数匹配!可谓是大费周章…… 我觉得策略模式可能是最简单的一个模式了,无非是一个接口和多个实现,达到应用与具体算法的解耦罢了! 不知道我的理解有什么不妥吗?
也是可以使用责任链设计模式,但责任链模式链式执行,效率不高,策略模式使用数据库形式来维持,可以再设置个开关,当不需要使用这个策略算法的时候,可用设置为关闭状态;责任链模式适合多条件判断,比如在网关层次需要做这些工作,比如:1.API接口限流,2.黑名单拦截,3.用户会话信息拦截,每个行为可以封装成一个对象,通过链表按照一定顺序链式执行。
暴走的懒羊羊
暴走的懒羊羊
说一下我的看法,大家讨论下。 首先策略模式要解决的是:不同算法之间相互替换,而不是多if,else!多个if-else可以用责任链模式来解决! 其次,数据从前端流到后端,再流到数据库,然后又回到后端,就是为了解决一个问题:哪个算法跟这个参数匹配!可谓是大费周章…… 我觉得策略模式可能是最简单的一个模式了,无非是一个接口和多个实现,达到应用与具体算法的解耦罢了! 不知道我的理解有什么不妥吗?
须臾之余
须臾之余

引用来自“Skqing”的评论

跟工厂模式啥区别
还有抽象工厂:比如在Spring中,有单例bean,被代理的bean,原型bean,List的bean,作用域不同的bean,产生每种bean的方式都不同,使用抽象工厂设计模式来生产具体的bean
须臾之余
须臾之余

引用来自“Skqing”的评论

跟工厂模式啥区别
工厂模式:工厂接口定义了所有子工厂执行标准,各个产品的生产都有各自的工厂,生产的工艺,生成的高科技程度都是不一样的,用户只需要关心生产的厂家。
策略模式:过程不一样,但结果一样,关心过程。比如聚合支付统一返回form表单
Skqing
Skqing
跟工厂模式啥区别
须臾之余
须臾之余

引用来自“凶残的花生米”的评论

确定这个是优点大于缺点,如果是两个程序员做这个同样的功能,第一个用if两分钟写完了,第二个刚刚建好数据库表。我想实际中第二个第二天就可以滚蛋了
主要还是大多程序员不会去使用设计模式,这对以后得扩展性不高。
凶残的花生米
凶残的花生米
确定这个是优点大于缺点,如果是两个程序员做这个同样的功能,第一个用if两分钟写完了,第二个刚刚建好数据库表。我想实际中第二个第二天就可以滚蛋了
【聚合支付平台】如何智用【模板+工厂】设计模式来实现异步回调

写在前面:设计模式源于生活,而又高于生活! 异步回调流程 解析报文(验证签名) 日志收集(相同) 如果解析报文成功的话,修改支付状态为已经成功.返回不同的支付结果 模版方法设计模式 提...

须臾之余
05/12
0
0
PHP设计模式(一):简介及创建型模式

我们分三篇文章来总结一下设计模式在PHP中的应用,这是第一篇创建型模式。 一、设计模式简介 首先我们来认识一下什么是设计模式: 设计模式是一套被反复使用、容易被他人理解的、可靠的代码设...

juhenj
2014/05/15
228
2
设计模式的作用

设计模式描述的是软件设计,因此它是独立于编程语言的,但是最终实现仍然要使用编程语言来表达。设计模式不像算法技巧,可以照搬照用,它是建立在对“面向对象”纯熟、深入理解的基础上的经验...

Dwyane_Coding
2018/01/22
0
0
Javascript设计模式与开发实践详解(二:策略模式)

上一章我们介绍了单例模式及JavaScript惰性单例模式应用 这一次我主要介绍策略模式 策略模式是定义一系列的算法,把它们一个个封装起来,并且让他们可以互相替换。 比方说在现实中很多时候也...

littl_Prince
2016/04/06
0
0
【设计模式】简单工厂模式 Simple Factory Pattern

简单工厂模式Simple Factory Pattern【Simple Factory Pattern】是设计模式里最简单的一个模式,又叫静态工厂模式【Static Factory Pattern】,这个模式没有收录在GOF 23 个模式中,因为他非...

风之源
2018/07/19
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Redux

Redux概念 Redux = Reducer + Flux,数据层框架,将所有数据都存储到store中 Redux的工作流程 Antd的使用 安装npm install antd --save...

星闪海洋
50分钟前
2
0
OSChina 周一乱弹 —— 你们谁看见了我的诺贝尔奖

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @脚板薯 :这么晚不睡只为找到一首歌,晚安。 ♫Say You Want Me♪ ♫Say You Want Me♪ - Augustana 手机党少年们想听歌,请使劲儿戳(这里)...

小小编辑
今天
210
14
我为什么要写微信公众号

埋一颗种子,细心呵护,静待她枝繁叶茂,葱郁参天 V2论坛上有个帖子【做程序员最重要的还是一定要有自己的作品】,作者写道: 能有一个作品和你的名字联系在一起,应当成为在职业生涯前期着意...

运维咖啡吧
今天
3
0
数据库

数据库架构 数据库架构可以分为存储文件系统和程序实例两大块,而程序实例根据不同的功能又可以分为如下小模块。 1550644570798 索引模块 常见的问题有: 为什么要使用索引 什么样的信息能成...

一只小青蛙
今天
5
0
PHP常用经典算法实现

<? //-------------------- // 基本数据结构算法 //-------------------- //二分查找(数组里查找某个元素) function bin_sch($array, $low, $high, $k){ if ( $low <= $high){ $mid = int......

半缘修道半缘君丶
昨天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部