说在前面:如果想最快速度温习这一设计模式,可直接跳到第4点的总结,不过其余3点个人觉得也非常有意思的!感兴趣的童鞋可以看看┗|`O′|┛ 嗷~~
模板方法模式是一个比较实用且简单的设计模式,它的关键点有:
(1)父类提供好方法模板,交给子类去实现
(2)父类定义好方法的执行顺序,对外暴露出一个执行方法,让子类调用执行。
举一个贴近生活的例子:我们每个人的早上、下午、晚上都可以做不同的事:
(1)男程序员:早上敲代码、下午摸鱼、晚上多人运动
(2)女程序员:早上敲代码、下午Shopping、晚上看剧吃沙拉美容
(3)···
我们可以发现,不同的人有不同的行为,而每个人的行为都是有相同的执行顺序的(早上、下午、晚上)
我们回顾上面的两位成员,用代码表示是这样的:
public class GeGe {
public void morning() {
System.out.println("男程序员在敲代码");
}
public void afternoon() {
System.out.println("男程序员在摸鱼");
}
public void evening() {
System.out.println("男程序员在多人运动");
}
public void start() {
morning();
afternoon();
evening();
}
}
public class MeiMei {
public void morning() {
System.out.println("女程序员在敲代码");
}
public void afternoon() {
System.out.println("女程序员在Shopping");
}
public void evening() {
System.out.println("女程序员在看剧吃沙拉和护肤,美美哒");
}
public void start() {
morning();
afternoon();
evening();
}
}
如果还有一个DiDi(弟弟)类,他也要过每一天的生活,那样在程序里还要重新手写这几个方法,再实现它的逻辑,太龊了,一点都不程序员,所以我们很快可以想到:把相同的方法抽取出来,聚合到一个抽象类Human当中,让GeGe和MeiMei去继承这个抽象类,实现抽象方法,这样就优雅多了吧
public abstract class Human {
protected abstract void morning();
protected abstract void afternoon();
protected abstract void evening();
protected void start() {
morning();
afternoon();
evening();
}
}
到这里,你会看到 Human 就像一个模板类一样,提供好了三个方法让子类去重写,并且提供了调用方法。GeGe类就变成这样了:
public class GeGe extends Human {
@Override
public void morning() {
System.out.println("男程序员在敲代码");
}
@Override
public void afternoon() {
System.out.println("男程序员在摸鱼");
}
@Override
public void evening() {
System.out.println("男程序员在多人运动");
}
public static void main(String[] args) {
Human gege = new GeGe();
gege.start();
}
}
// 男程序员在敲代码
// 男程序员在摸鱼
// 男程序员在多人运动
同理,MeiMei 类就不贴出来了。这样的好处是:
(1)Human(人类)类已经提供好需要实现的方法,GeGe(哥哥)不需要去思考自己需要写哪些方法,直接重写父类要求的方法即可。
(2)由于在父类中已经提供了方法,里面定义好每个方法的执行顺序,子类按照父类给定的调用顺序去编写逻辑即可。
我们来回顾一下 Human 这一段代码
public abstract class Human {
protected abstract void morning();
protected abstract void afternoon();
protected abstract void evening();
protected void start() {
morning();
afternoon();
evening();
}
}
上面这段代码已经可以称为一个“合格”的模板方法模式了,但是还差一步,那就是标题中的“final”关键字。
final 关键字有以下作用:
(1)使一个方法永远无法被重写(重要!)
(2)使一个变量的值永远不能发生改变(引用变量则代表引用地址的值)
(3)还有其它作用,但不是这篇博客的重点,可以参考 final关键字的所有作用
在模板方法模式下,子类不能破坏父类的调用顺序,所以为了保证这一点,我们需要在方法上加上 final 关键字。
public final void start() {
morning();
afternoon();
evening();
}
到这里为止,我对模板方法模式的关键点做了小小的总结:
(1)模板方法模式是一个由父类提供好抽象方法以及调用顺序、由子类实现具体逻辑的设计模式
(2)使用 final 关键字的作用是确保子类无法破坏父类定义好的调用顺序,否则违背了该模式的定义(可以对final关键字的作用展开哦)
钩子方法,第一次听到这个名字的时候,我的表情是这样的,为什么叫钩子,钩啥玩意儿?
后来搜了一下它的定义,简单理解,钩子方法:管理着某个方法,决定该方法是否执行的一个管理者
文字较为晦涩,我们直接来看下面这段代码:
public abstract class Human {
protected abstract void morning();
protected abstract void afternoon();
protected abstract void evening();
protected void start() {
if (doSthOnMorning()) {
morning();
}
afternoon();
evening();
}
protected boolean doSthOnMorning() {
return false;
}
}
上面这段代码中的 doSthOnMorning 方法就是钩子方法:它决定了 morning 方法是否执行。如代码所示:如果早上没事做,就不需要调用 morning() 方法去告诉别人自己早上做了什么了。
我们可以这样理解钩子方法:它就是一个业务方法的开关按钮。
(1)父类总结好一套抽象方法(算法)模板,并对外暴露出一个通用的、不可被重写的执行方法,其内部定义好模板的调用顺序,让子类去调用。
(2)父类提供好每个算法模板的钩子(hook)方法,让子类自行决定是否要执行对应的方法
(1)子类实现父类提供的抽象方法模板和钩子函数,自行实现每个抽象方法模板的逻辑
(2)子类通过操控钩子函数决定是否执行子类中实现好的方法模板
关于模板方法设计模式到这里就结束了,它并不复杂,但是解释清楚父类和子类各自的作用,可以让面试官对你清晰的思路点赞!
各位小伙伴转载、收藏、点赞、评论,让我们一起进步!
作者:大众程序猿
座右铭:比我们优秀的人多了去,但这并不防止我们追梦!