设计模式 —— 模板方法模式

简介

模板方法模式(Template Method Pattern)定义一个操做中算法的框架,而将一些步骤延迟到子类中。模板方法模式使得子类能够不改变一个算法的结构便可重定义该算法的某些特定步骤。java

模板方法模式是结构最简单的行为型设计模式,在其结构中只存在父类与子类之间的继承关系。经过使用模板方法模式,能够将一些复杂流程的实现步骤封装在一系列基本方法中,在抽象父类中提供一个称之为模板方法的方法来定义这些基本方法的执行次序,而经过其子类来覆盖某些步骤,从而使得相同的算法框架能够有不一样的执行结果。算法

模板方法中的方法能够分为两大类:模板方法和基本方法。设计模式

模板方法

一个模板方法是定义在抽象类中的,把基本操做方法组合在一块儿造成一个总算法或一个总行为的方法。 一个抽象类能够有任意多个模板方法,而不限于一个。每个模板方法均可以调用任意多个具体方法。服务器

基本方法

基本方法又能够分为三种:抽象方法(Abstract Method)、具体方法(Concrete Method)和钩子方法(Hook Method)。微信

  1. 抽象方法:一个抽象方法由抽象类声明,由具体子类实现。在Java语言里抽象方法以abstract关键字标示;
  2. 具体方法:一个具体方法由抽象类声明并实现,而子类并不实现或置换;
  3. 钩子方法:一个钩子方法由抽象类声明并实现,而子类会加以扩展。一般抽象类给出的实现是一个空实现,做为方法的默认实现。

实例

对接过第三方支付的小伙伴都知道,在接入第三方支付时,通常支付结果都会经过异步回调的形式,通知商户服务器。而咱们获得这些数据时通常都是同一流程的处理方式:验签 — 更新订单状态 — 给第三方支付服务器响应。 下面以支付宝和微信为例:支付宝和微信的验签方式和响应结果方式都是不同的, 而更新订单状态都是商户这边处理因此业务逻辑是同样的。框架

抽象模板类:异步

public abstract class AbstractPayNotifyTemplate {

    /** * 支付异步回调处理 * * @param params 回调参数 */
    public void onNotify(Map<String, String> params) {
        //验证签名
        final boolean sign = verifySign(params);
        if (sign) {
            // 给第三方支付服务器回复支付失败状态
            setResponse(PayStatus.ERROR);
            return;
        }
        //从参数获取订单编号并更新订单支付状态,为支付成功
        final String orderSn = params.get("out_trade_no");
        updateOrderPayStatusSuccess(orderSn);
        // 给第三方支付服务器回复支付成功状态
        setResponse(PayStatus.SUCCESS);
    }

    /** * 验签 * * @param params 回调参数 * @return 验签结果 */
    protected abstract boolean verifySign(Map<String, String> params);

    /** * 更新订单支付状态为支付成功 * * @param orderSn 订单编号 */
    private void updateOrderPayStatusSuccess(String orderSn) {
        // 根据订单编号更新订单支付状态为支付成功
    }

    /** * 给第三方支付返回 * * @param status 支付状态 */
    protected abstract void setResponse(PayStatus status);
}
复制代码

具体模板类:ide

// 支付宝支付回调类
public class AliPayNotifyTemplate extends AbstractPayNotifyTemplate {

    @Override
    protected boolean verifySign(Map<String, String> params) {
        // 调用支付宝验签接口, 并返回验签结果
        return true;
    }

    @Override
    protected void setResponse(PayStatus status) {
        String res = Objects.equals(PayStatus.SUCCESS, status) ? "success" : "error";

        // 调用 ResponseUtils 直接返回 res 字符串
    }
}

// 微信支付回调类
public class WxPayNotifyTemplate extends AbstractPayNotifyTemplate {

    @Override
    protected boolean verifySign(Map<String, String> params) {
        // 调用微信支付验签接口, 并返回验签结果
        return true;
    }

    @Override
    protected void setResponse(PayStatus status) {
        String returnCode = "FAIL";
        String returnMsg = "";
        if (Objects.equals(PayStatus.SUCCESS, status)) {
            returnCode = "SUCCESS";
            returnMsg = "OK";
        }
        String res = String.format("<xml><return_code><![CDATA[%s]]></return_code><return_msg><![CDATA[%s]]></return_msg></xml>", returnCode, returnMsg);

        // 调用 ResponseUtils 返回 res xml格式内容
    }
}
复制代码

固然这只是简单例子,帮助理解模板方法模式。实际项目中还带有各类参数、异常的处理、工具类的封装,以及其余设计模式的应用。工具

优势

  1. 提升代码复用性。将相同部分的代码放在抽象的父类中;微信支付

  2. 提升了拓展性。将不一样的代码放入不一样的子类中,经过对子类的扩展增长新的行为;

  3. 可实现一种反向控制结构,经过子类覆盖父类的钩子方法来决定某一特定步骤是否须要执行。(此处实例没有体现)

缺点

引入了抽象类,每个不一样的实现都须要一个子类来实现,致使类的个数增长,从而增长了系统实现的复杂度。

适用场景

  1. 对一些复杂的算法进行分割,将其算法中固定不变的部分设计为模板方法和父类具体方法,而一些能够改变的细节由其子类来实现;
  2. 各子类中公共的行为应被提取出来并集中到一个公共父类中以免代码重复;
  3. 须要经过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制。