經典策略模式-如何重構聚合支付平臺,對接【支付寶,微信,銀聯支付】

爲什麼要使用設計模式重構代碼

使用設計模式可以重構整體架構代碼、提高代碼複用性、擴展性、減少代碼冗餘問題。

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判斷問題、提高擴展性、維護性增強、提高代碼可讀性。
缺點:後期維護不同策略類是非常多、定義類比較多、代碼量增大。
優點大於缺點。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章