Skip to content

笔记八 模板模式(Template Pattern)

1. 模板模式—是什么

专业版

  • 含义:定义了一个操作中的算法的骨架,而将部分步骤的实现在子类中完成

  • 内在含义:是基于继承的代码复用的基本技术。

生活版

例如:

  • 在造房子的时候,地基、走线、水管都一样,只有在建筑的后期才有加壁橱加栅栏等差异。
  • 西游记里面菩萨定好的 81 难,这就是一个顶层的逻辑骨架。

2. 模板模式—作用

  • 封装不变部分,扩展可变部分。

  • 提取公共代码,便于维护。

  • 行为由父类控制,子类实现。

3. 模板模式—核心使用规则

将这些通用算法行为抽象出来在抽象类中,其他步骤在子类实现。

  • AbstractClass :抽象类,定义并实现一个模板方法。这个模板方法定义了算法的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类去实现

  • ConcreteClass: 实现实现父类所定义的一个或多个抽象方法。

4. 模板模式—应用场景

有多个子类共有的方法,且逻辑相同;重要的、复杂的方法,可以考虑作为模板方法。

  • 聚合支付平台中系统回调代码

  • Servlet 请求

5. 模板模式—案例 1:聚合支付平台-回调

聚合支付平台-异步回调-使用模板模式-结构图,如下:

1. 定义共同算法骨架

  • 创建抽象类 :AbstractPayCallbackTemplate.java
  • 共同算法骨架:asyncCallBack()异步回调
java
package com.pay.service.abstracts;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * Created by Calvin on 2019/5/11
 * 异步回调-模板方法的抽象类
 */
@Slf4j
@Component
public abstract class AbstractPayCallbackTemplate {

    /**
     * 定义共同算法骨架
     *
     * 聚合支付-异步回调
     * 1. 验签
     * 2. 记录支付回调日志
     * 3. 更改订单状态
     *
     * @return 支付结果
     */
    public String asyncCallBack(){
        // 1.支付回调参数验证。
        Map<String,String> verifySignature = verifySignature();
        // 2.参数验证成功,写入到日志中。
        callBackPayLog(verifySignature);
        String analysisCode = verifySignature.get("analysisCode");
        if(!analysisCode.equals("200")){
            return resultFail();
        }
        // 3. 执行回调异步相关逻辑
        return asyncService(verifySignature);
    }

    /**
     * 聚合支付-记录支付回调日志
     *
     * @param verifySignature 报文验签
     */
    @Async
    public void callBackPayLog(Map<String, String> verifySignature) {
        log.info(">>>>>第二步:记录支付回调日志.....verifySignature:{}", verifySignature);
    }

    /**
     * 聚合支付-验签(每个子类需要实现)
     *
     * @return 验证结果
     */
    protected abstract Map<String, String> verifySignature();

    /**
     * 聚合支付-执行异步回调业务 (每个子类需要实现)
     *
     * @return  返回支付结果
     */
    protected abstract String asyncService(Map<String, String> verifySignature);

    /**
     * 聚合支付-异步返回结果 (每个子类需要实现)
     *
     * @return 返回支付成功结果
     */
    protected abstract String resultSuccess();

    /**
     * 聚合支付-异步返回失败(每个子类需要实现)
     *
     * @return 返回支付失败结果
     */
    protected abstract String resultFail();
}

2. 将部分步骤的实现在子类中完成。

  • 创建子类 :
    • 支付宝回调实现类 AliPayCallbackServiceImpl.java
    • 银联回调实现类 UnionPayCallbackServiceImpl.java
  • 部分步骤行为:
    • 验签 verifySignature()
    • 执行异步回调业务 asyncService(Map<String, String> verifySignature)
    • 支付成功 resultSuccess()
    • 支付失败 resultFail()
java
package com.pay.service.impl;

import com.pay.service.AliPayCallbackService;
import com.pay.service.abstracts.AbstractPayCallbackTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by Calvin on 2019/5/11
 */
@Service
@Slf4j
public class AliPayCallbackServiceImpl extends AbstractPayCallbackTemplate implements AliPayCallbackService {

    @Override
    protected Map<String, String> verifySignature() {
        /**
         * 下面代码为伪代码:支付宝回调报文
         */
        log.info(">>>>> 第一步:解析【支付宝】据报文.....verifySignature()");
        Map<String, String> verifySignature = new HashMap<>();
        verifySignature.put("price", "1399");
        verifySignature.put("orderDes", "充值蚂蚁课堂永久会员");
        // 支付状态为1表示为成功....
        verifySignature.put("aliPayMentStatus", "1");
        verifySignature.put("aliPayOrderNumber", "201910101011");
        // 解析报文是否成功 200 为成功..
        verifySignature.put("analysisCode", "200");
        return verifySignature;
    }

    @Override
    protected String asyncService(Map<String, String> verifySignature) {
        log.info(">>>>> 第三步: 执行回调后的业务方法 asyncService() 参数 verifySignatureMap:{}", verifySignature);
        String paymentStatus = verifySignature.get("aliPayMentStatus");
        if (paymentStatus.equals("1")) {
            String aliPayOrderNumber = verifySignature.get("aliPayOrderNumber");
            log.info(">>>> orderNumber:{aliPayOrderNumber},已经支付成功 修改订单状态为已经支付...");
        }
        return resultSuccess();
    }

    @Override
    protected String resultSuccess() {
        return "【支付宝】 支付成功!";
    }

    @Override
    protected String resultFail() {
        return "【支付宝】 支付失败!";
    }
}
java
package com.pay.service.impl;

import com.pay.service.UnionPayCallbackService;
import com.pay.service.abstracts.AbstractPayCallbackTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
@Slf4j
public class UnionPayCallbackServiceImpl extends AbstractPayCallbackTemplate implements UnionPayCallbackService {

    @Override
    protected Map<String, String> verifySignature() {

        /**
         * 下面代码为伪代码:银联支付回调报文
         */
        log.info(">>>>>第一步:解析【银联】支付数据报文.....verifySignature()");
        Map<String, String> verifySignature = new HashMap<>();
        verifySignature.put("price", "1399");
        verifySignature.put("orderDes", "充值蚂蚁课堂永久会员");
        // 支付状态为1表示为成功....
        verifySignature.put("paymentStatus", "1");
        verifySignature.put("orderNumber", "201910101011");
        // 解析报文是否成功 200 为成功..
        verifySignature.put("analysisCode", "200");
        return verifySignature;
    }

    @Override
    protected String asyncService(Map<String, String> verifySignature) {
        log.info(">>>>> 第三步: 执行回调后的业务方法 asyncService() 参数 verifySignatureMap:{}", verifySignature);
        String paymentStatus = verifySignature.get("paymentStatus");
        if (paymentStatus.equals("1")) {
            String orderNumber = verifySignature.get("orderNumber");
            log.info(">>>>orderNumber:{orderNumber},已经支付成功 修改订单状态为已经支付...");
        }
        return resultSuccess();
    }

    @Override
    protected String resultSuccess() {
        return "【银联】 支付成功!";
    }

    @Override
    protected String resultFail() {
        return "【银联】 支付失败!";
    }
}

3. 编写聚合支付平台-回调接口

  • 支付回调接口 :PayCallbackController.java

  • 参数支付回调模板类型:templateId

    • ALI_PAY
    • UNION_PAY
  • 请求接口 GET : /pay/async/callback/{templateId}

  • 返回异步回调结果:asyncCallBack()

java
package com.pay.controller;

import com.pay.service.abstracts.AbstractPayCallbackTemplate;
import com.pay.factory.TemplateFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(value = "pay")
public class PayCallbackController {

    /**
     * 聚合支付平台-支付回调接口
     * @param templateId 模板ID
     * @return 支付回调结果
     */
    @GetMapping("async/callback/{templateId}")
    public String asyncCallBack(@PathVariable String templateId) {
        AbstractPayCallbackTemplate payCallbackTemplate = TemplateFactory.getPayCallbackTemplate(templateId);
        if (null == payCallbackTemplate) {
            return "支付回调模板ID有误,没有相应的支付回调模板";
        }
        return payCallbackTemplate.asyncCallBack();
    }

}

4. 创建工厂,根据对应支付回调模板类型,返回对应的类型支付回调结果。

  • 创建一个枚举:PayCallBackTemplateEnum.java

    java
    package com.pay.enumeration;
    
    import lombok.AllArgsConstructor;
    import lombok.Getter;
    
    @Getter
    @AllArgsConstructor
    public enum PayCallbackTemplateEnum {
    
        ALI_PAY("com.pay.service.impl.AliPayCallbackServiceImpl"),
        UNION_PAY("com.pay.service.impl.UnionPayCallbackServiceImpl"),
        ;
        /** class 完整地址 **/
        private String className;
    }
  • 创建工厂:TemplateFactory.java,实例化对应的模板类型。

    java
    package com.pay.factory;
    
    import com.pay.enumeration.PayCallbackTemplateEnum;
    import com.pay.service.abstracts.AbstractPayCallbackTemplate;
    import com.pay.utils.ContextUtils;
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * @Auther: Calvin
     * @Date: 2019/5/15
     * @Description:
     */
    @Slf4j
    public class TemplateFactory {
    
        public static AbstractPayCallbackTemplate getPayCallbackTemplate(String templateId){
            AbstractPayCallbackTemplate abstractPayCallbackTemplate = null;
            // 1. 获取枚举中的ClassName
            try {
                String className = PayCallbackTemplateEnum.valueOf(templateId).getClassName();
                // 2. 使用java 反射技术初始化类
                abstractPayCallbackTemplate = (AbstractPayCallbackTemplate)Class.forName(className).newInstance();
            } catch (Exception e) {
                log.error("============== 反射类不存在,错误:{} ====================", e.getMessage());
            }
            return abstractPayCallbackTemplate;
        }
    }

6. 结果演示

代码链接:https://github.com/1016280226/design-patterns

控制台输出:

verilog
2020-09-03 23:40:09.762  INFO 3556 --- [nio-8081-exec-1] c.p.s.impl.AliPayCallbackServiceImpl     : >>>>> 第一步:解析【支付宝】据报文.....verifySignature()
2020-09-03 23:40:19.577  INFO 3556 --- [nio-8081-exec-1] c.p.s.a.AbstractPayCallbackTemplate      : >>>>>第二步:记录支付回调日志.....verifySignature:{aliPayOrderNumber=201910101011, orderDes=充值蚂蚁课堂永久会员, price=1399, analysisCode=200, aliPayMentStatus=1}
2020-09-03 23:40:33.757  INFO 3556 --- [nio-8081-exec-1] c.p.s.impl.AliPayCallbackServiceImpl     : >>>>> 第三步: 执行回调后的业务方法 asyncService() 参数 verifySignatureMap:{aliPayOrderNumber=201910101011, orderDes=充值蚂蚁课堂永久会员, price=1399, analysisCode=200, aliPayMentStatus=1}
2020-09-03 23:40:36.058  INFO 3556 --- [nio-8081-exec-1] c.p.s.impl.AliPayCallbackServiceImpl     : >>>> orderNumber:{aliPayOrderNumber},已经支付成功 修改订单状态为已经支付...
2020-09-03 23:41:13.135  INFO 3556 --- [nio-8081-exec-2] c.p.s.impl.UnionPayCallbackServiceImpl   : >>>>>第一步:解析【银联】支付数据报文.....verifySignature()
2020-09-03 23:41:13.136  INFO 3556 --- [nio-8081-exec-2] c.p.s.a.AbstractPayCallbackTemplate      : >>>>>第二步:记录支付回调日志.....verifySignature:{orderNumber=201910101011, orderDes=充值蚂蚁课堂永久会员, price=1399, analysisCode=200, paymentStatus=1}
2020-09-03 23:41:13.136  INFO 3556 --- [nio-8081-exec-2] c.p.s.impl.UnionPayCallbackServiceImpl   : >>>>> 第三步: 执行回调后的业务方法 asyncService() 参数 verifySignatureMap:{orderNumber=201910101011, orderDes=充值蚂蚁课堂永久会员, price=1399, analysisCode=200, paymentStatus=1}
2020-09-03 23:41:13.136  INFO 3556 --- [nio-8081-exec-2] c.p.s.impl.UnionPayCallbackServiceImpl   : >>>>orderNumber:{orderNumber},已经支付成功 修改订单状态为已经支付...

7. 模板模式—优缺点

优点:

  • 模板方法模式通过把不变的行为搬移到超类,去除了子类中的重复代码。

  • 子类实现算法的某些细节,有助于算法的扩展。

  • 通过一个父类调用子类实现的操作,通过子类扩展增加新的行为,符合“开放-封闭原则”。

缺点:

  • 每个不同的实现都需要定义一个子类,这会导致类的个数的增加,设计更加抽象。