11/24 设计模式之模板方法模式 Template Method Pattern

978 阅读3分钟

类别:行为型设计模式

目的:定一个代码模板,确保代码执行时会把模板中的代码一并执行,降低漏写约定代码的可能

完整代码参考:1drv.ms/u/s!AquRvPz…

典型场景

这里拿集成支付网关举例,比如业务中对支付的抽象如下

方法 作用
create 发起支付
query 查询订单
refund 退款

在上面执行的每一步进行日志记录

对应的接口Pay.java参考如下

public interface Pay {
    void create();

    void query();

    void refund();
}

实现一个支付方式,比如支付宝支付,在每一个支付步骤进行日志记录,参考如下:

import java.util.logging.Logger;

public class AliPay implements Pay {
    Logger logger = Logger.getLogger(Pay.class.getName());

    @Override
    public void create() {
        logger.info("alipay create");
        System.out.println("call alipay create api");
    }

    @Override
    public void query() {
        logger.info("alipay query");
        System.out.println("call alipay query api");
    }

    @Override
    public void refund() {
        logger.info("alipay refund");
        System.out.println("call alipay refund api");
    }
}

上面代码由一个人维护是ok的,但是如果由另一个人新增一个支付(即实现另一个Pay接口)比如微信支付,可能无法保证日志记录相关代码会被编码了,这种情况就可以使用模板方法模式

模式实现

Pay接口修改抽象类,并把日志记录相关代码移入Pay抽象类中,参考如下:

public abstract class Pay {
    Logger logger = Logger.getLogger(Pay.class.getName());

    public void create() {
        logger.info("alipay create");
        doCreate();
    }

    public void query() {
        logger.info("alipay query");
        doQuery();
    }

    public void refund() {
        logger.info("alipay refund");
        doRefund();
    }

    abstract protected void doCreate();

    abstract protected void doQuery();

    abstract protected void doRefund();
}

可以看到

  1. 把Pay接口改为Pay抽象类后,不改变这个抽象类对外暴露的接口
  2. 日志记录代码编码在了抽象类中
  3. 具体发起支付的相关代码抽象在了doxxx方法中
  4. 这样在实现一个新的支付方式时,实现protected的doxxx系列方法记录,调用方使用create, query, refund进行支付操作
  5. 确保日志记录代码(模板代码)一定会被执行

上面这个Pay抽象类中的create, query, refund中会被执行的日志记录相关代码即使模板,doxxx系列方法就是需要用户填充到模板中的代码

比如实现一个新的微信支付,参考如下

public class WechatPay extends Pay {
    @Override
    public void doCreate() {
        System.out.println("call wechatpay create api");
    }

    @Override
    public void doQuery() {
        System.out.println("call wechatpay query api");
    }

    @Override
    public void doRefund() {
        System.out.println("call wechatpay refund api");
    }
}

可以看到实现一个新的微信支付不用关心前置的日志操作了

对应的调用代码参考如下:

var wechatPay = new WechatPay();
wechatPay.create();
wechatPay.query();
wechatPay.refund();

执行效果如下:

-w544
可以看到模板代码执行了

UML

-w644

为什么组合优于继承

这里模板方法模式使用了抽象类Pay来定义模板代码,抽象类的使用需要通过继承来实现比如WechatPay extends Pay,因为很多语言是单继承的,java也是,这个时候WechatPay因为已经使用了继承,就不能再继承另一个类来增强其自生的功能了,即这种情况 组合优于继承,实际编码中,大部分都是这种情况,即变成了组合优于继承这种说法

为什么模板方法模式更好

  1. 通用模板代码只需在抽象类中维护一份
  2. 确保模板代码执行了,确保收集到了需要的数据

一些注意的点

模板代码这种存在部分具体实现,部分抽象实现的代码适合使用抽象类来实现

参考资料

  1. www.geeksforgeeks.org/template-me…