【java_基础深入】模板方法设计模式的妙用 : 回调子类实现

模板方法设计模式应用场景

HttpServlet 的 service() 就是一个模板方法,它实现了Servlet规范,
DispatcherServlet 借助service() 的模板方法,拓展了自己的逻辑。
HttpServlet 是一个抽象类。抽象类是实现模板方法的基石,以下先举个简单例子,再从底层证明

实例

产品线定义:

/**
 * @Author james
 * @Description 鞋子产品线, 抽象类。该类实现了产品的规范
 * @Date 2019/12/23
 */
public abstract class ShoesProduct implements Product{

	// 默认是不上线
    private boolean available = false;

    public boolean isAvailable() {
        return available;
    }

    public void setAvailable(boolean available) {
        this.available = available;
    }

    /**
     * 默认留给子类实现,未实现,视为不支持功能,调用就抛异常
     *
     */
    protected void payOnline() {
        throw new RuntimeException("该产品未开通线上支付功能");
    }

    protected void sevenDay(){
        throw new RuntimeException("该产品不支持七天无理由退款");
    }

    /**
     * 鞋子产品线生成订单的模板方法
     *
     */
    @Override
    public void makeOrder() {
        if (isAvailable()) {
            payOnline();
            sevenDay();
        } else {
            throw new RuntimeException("该产品未上线,无法生成订单");
        }
    }
}

具体产品线实现

/**
 * @Author james
 * @Description 产品线的具体实现:Nike系列产品线
 * @Date 2019/12/23
 */
public class NikeShoesProduct extends ShoesProduct {
    // 产品名
    private String name;

    // 是否联名
    private boolean isJoint = false;

    @Override
    protected void payOnline() {
        System.out.println("支持支付宝付款");
    }

    /**
     * 联名发售, 自己拓展的业务功能
     */
    private void jointOffer() {
        System.out.println(name + " 联名Bape发售");
    }

    /**
     * 具体产品生成订单的实现
     */
    @Override
    public void makeOrder() {
        if (isJoint()) {
            jointOffer();
            super.makeOrder();
        } else {
            super.makeOrder();
        }
    }

    /**
     * 确定该产品是否上线, 为该产品命名
     *
     * @param isAvailable 是否上线
     */
    public NikeShoesProduct(boolean isAvailable, String name) {
        this.name = name;
        super.setAvailable(isAvailable);
    }

    public NikeShoesProduct(boolean isAvailable, boolean isJoint, String name) {
        this.name = name;
        this.isJoint = isJoint;
        super.setAvailable(isAvailable);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean isJoint() {
        return isJoint;
    }

    public void setJoint(boolean joint) {
        isJoint = joint;
    }
}

模板方法的业务场景

		// nikeID 
        ShoesProduct nikeShoesProduct = new NikeShoesProduct(true, true, "nike ID 冬季系列");
        nikeShoesProduct .makeOrder(); // 生成该产品的订单信息

在这里插入图片描述
达到预期目的。这里的预期指的是:抽象类作为父类,可以通过抽象方法回调到子类的实现,这是一个很神奇的过程。可以观察如下的调用链

  1. 抽象类的子类实例nikeShoesProduct -> 调用本类的 makeOrder();
  2. nikeShoesProduct .makeOrder() -> 执行super.makeOrder() -> 调用抽象类ShoesProduct.makeOrder()
    以下抽象类的实现:
    /**
     * 默认留给子类实现,未实现,视为不支持功能,调用就抛异常
     *
     */
    protected void payOnline() {
        throw new RuntimeException("该产品未开通线上支付功能");
    }
    
    protected void sevenDay(){
        throw new RuntimeException("该产品不支持七天无理由退款");
    }
    
    /**
     * 生成订单
     *
     */
    @Override
    public void makeOrder(){
        if (isAvailable()) {
            payOnline();
            sevenDay();
        } else {
            throw new RuntimeException("该产品未上线,无法生成订单");
        }
    }
    
  3. 抽象类的makeOrder()-> 调用 payOnline()这是本文的关键点
    payOnline() 到底调用的是抽象类的 payOnline() 还是实现类的 payOnline()
    答案是:实现类的 payOnline()

分析

这本应该是个很基础的问题,之前写代码一直停留在看代码的程度,记死了this代表本类,super代表父类,这是不够准确的,只有在debug下才暴露了自己的问题。
其实在一个调用链上,this代表调用链起点的对象,实现类通过super访问了父类的方法,运行时,父类的this即是实现类

在这里插入图片描述
在这里插入图片描述

延伸

模板方法实现了父类调用子类的场景,而为什么采取继承抽象类而不是直接继承普通类

  1. 抽象类不能被实例化, this 的对象直接确定为实现类, 业务上提供单一的入口
    1.1 值得一提的是,抽象类中makeOrder() 中调用的payOnline()同样会在编译后变成this.payOnlin
  2. 抽象类同时参与适配器模式,abstract class HttpServlet extends GenericServlet 适配了GenericServlet,也让Servlet很好的支持Http协议, DispatcherServlet借力打力实现了Spring 的核心控制器。Spring中更典型的例子应该是 适配器模式、spring相关
©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页