模板方法设计模式应用场景
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(); // 生成该产品的订单信息
达到预期目的。这里的预期指的是:抽象类作为父类,可以通过抽象方法回调到子类的实现,这是一个很神奇的过程。可以观察如下的调用链
- 抽象类的子类实例
nikeShoesProduct
-> 调用本类的makeOrder();
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("该产品未上线,无法生成订单"); } }
- 抽象类的
makeOrder()
-> 调用payOnline()
,这是本文的关键点
payOnline() 到底调用的是抽象类的 payOnline() 还是实现类的 payOnline()
答案是:实现类的 payOnline()
分析
这本应该是个很基础的问题,之前写代码一直停留在看代码的程度,记死了this代表本类,super代表父类,这是不够准确的,只有在debug下才暴露了自己的问题。
其实在一个调用链上,this代表调用链起点的对象,实现类通过super访问了父类的方法,运行时,父类的this即是实现类
延伸
模板方法实现了父类调用子类的场景,而为什么采取继承抽象类而不是直接继承普通类
- 抽象类不能被实例化, this 的对象直接确定为实现类, 业务上提供单一的入口
1.1 值得一提的是,抽象类中makeOrder() 中调用的payOnline()同样会在编译后变成this.payOnlin - 抽象类同时参与适配器模式,
abstract class HttpServlet extends GenericServlet
适配了GenericServlet
,也让Servlet很好的支持Http协议,DispatcherServlet
借力打力实现了Spring 的核心控制器。Spring中更典型的例子应该是 适配器模式、spring相关