乐趣区

一分钟学会《模板方法模式》

前言
只有光头才能变强。文本已收录至我的 GitHub 仓库,欢迎 Star:https://github.com/ZhongFuCheng3y/3y

在上一篇有读者说,一分钟就看完门面模式了,所以今天的标题就取《一分钟学会模板方法模式》
回顾前面所写过的设计模式:

给女朋友讲解什么是代理模式
包装模式就是这么简单啦
单例模式你会几种写法?
工厂模式理解了没有?
策略模式原来就这么简单!
三分钟学会门面模式!

无论是面试还是个人的提升,设计模式是必学的。今天来讲解模板方法模式~
一、模板方法模式
1.1 模板方法模式现实例子
大家都知道,我每次写原创技术文章,开头总会有“只有光头才能变强”。我当然不可能每次写文章的时候都去复制这句话 (因为这样太麻烦了)。
我有自己的写作模板,给大家看一下:

前言和最后都是固定下来的,至于第一点和第二点就得看是写什么文章,写不同的文章对应的内容也是不一样的。
每次我写文章的时候,只要在这个模板上添加我自己想写的东西就好了,就不用每次都复制一遍相同的内容,这样就大大减少我的工作量啦。
1.2 回到代码世界
代码来源于生活,同样地我可以将我写文章的过程用代码来描述,大家来看一下。
3y 每篇文章都会有“前言”和“最后”的内容,3y 把这两个模块写出来了。

// 3y 的文章模板
public class Java3yWriteArticle {

// 前言
public void introduction() {
System.out.println(“ 只有光头才能变强 ”);
}

// 最后
public void theLast() {
System.out.println(“ 关注我的公众号:Java3y”);

}
}

3y 写文章的时候,3y 可能就会这样使用:

// 3y 写文章
public static void main(String[] args) {

Java3yWriteArticle writeArticle = new Java3yWriteArticle();

// 前言
writeArticle.introduction();

// 实际内容
System.out.println(“ 大家好,我是 3y,今天来给大家分享我写的模板方法模式 ”);

// 最后
writeArticle.theLast();
}

这样是可以完成 3y 写文章的功能,但是这样做好吗?这时候 3y 女朋友也想写文章,她的文章同样也想有“前言”和“最后”两个模块,所以 3y 女朋友的文章模板是这样的:

// 3y 女朋友的文章模板
public class Java3yGFWriteArticle {

// 前言
public void introduction() {
System.out.println(“balabalabalalabalablablalab”);
}

// 最后
public void theLast() {
System.out.println(“balabalabalalabalablablalab”);

}
}
那 3y 女朋友写文章的时候,可能也会这样使用:

// 3y 女朋友写文章
public static void main(String[] args) {
Java3yGFWriteArticle java3yGFWriteArticle = new Java3yGFWriteArticle();

// 前言
java3yGFWriteArticle.introduction();

// 实际内容
System.out.println(“3y 是傻子,不用管他 ”);

// 最后
java3yGFWriteArticle.theLast();

}
可以发现 3y 和 3y 女朋友要写文章的时是要重复调用 introduction(); 和 theLast();。并且,3y 的文章模板和 3y 女朋友的文章模板中的“前言”和“最后”只是实现内容的不同,却定义了两次,明显就是重复的代码。面对重复的代码我们会做什么?很简单,抽取出来!
于是我们就可以抽取出一个通用的 WriteArticle(为了方便调用,我们还将写文章的步骤封装成一个方法):

// 通用模板
public abstract class WriteArticle {

// 每个人的“前言”都不一样,所以抽象 (abstract)
protected abstract void introduction();

// 每个人的“最后”都不一样,所以抽象 (abstract)
protected abstract void theLast();

// 实际要写的内容,每个人的“实际内容”都不一样,所以抽象 (abstract)
protected abstract void actualContent();

// 写一篇完整的文章 (为了方便调用,我们将这几个步骤分装成一个方法)
public final void writeAnCompleteArticle() {

// 前言
introduction();

// 实际内容
actualContent();

// 最后
theLast();
}
}

所以,3y 的模板就可以继承通用模板,在通用模板上实现自己想要的就好了:

// 3y 的文章模板
public class Java3yWriteArticle extends WriteArticle {

// 前言
@Override
public void introduction() {
System.out.println(“ 只有光头才能变强 ”);
}

// 最后
@Override
public void theLast() {
System.out.println(“ 关注我的公众号:Java3y”);

}
@Override
protected void actualContent() {
System.out.println(“ 大家好,我是 3y,今天来给大家分享我写的模板方法模式 ”);
}
}
同样地,3y 女朋友的文章模板也是类似的:

// 3y 女朋友的文章模板
public class Java3yGFWriteArticle extends WriteArticle {

// 前言
@Override
public void introduction() {
System.out.println(“balabalabalalabalablablalab”);
}

// 最后
@Override
public void theLast() {
System.out.println(“balabalabalalabalablablalab”);

}

@Override
protected void actualContent() {
System.out.println(“3y 是傻子,不用管他 ”);
}
}

想要真正写文章的时候就十分方便了:

// 3y 写文章
public static void main(String[] args) {

WriteArticle java3ywriteArticle = new Java3yWriteArticle();
java3ywriteArticle.writeAnCompleteArticle();
}

// 3y 女朋友写文章
public static void main(String[] args) {
WriteArticle java3yGFWriteArticle = new Java3yGFWriteArticle();
java3yGFWriteArticle.writeAnCompleteArticle();
}
要点:

把公共的代码抽取出来,如果该功能是不确定的,那我们将其修饰成抽象方法。
将几个固定步骤的功能封装到一个方法中,对外暴露这个方法,就可以非常方便调用了。

嗯,上面的就是模板方法模式,就这么简单!
1.3 模板方法模式介绍
《设计模式之禅》:
定义一个操作中的算法框架,而将一些步骤延迟到子类中。使子类可以不改变一个算法的结构即可重定义该算法的某些步骤。
根据我们上面的例子,来讲讲这段话的含义:

定义一个操作中的算法框架,而将一些步骤延迟到子类中。
WriteArticle 中有一个 writeAnCompleteArticle() 方法,该方法定义了发文章的所有步骤,但是这些步骤大多是抽象的,得由子类来实现。

使子类可以不改变一个算法的结构即可重定义该算法的某些步骤
外界是通过调用 writeAnCompleteArticle() 方法来写文章的,子类如果改变具体的实现就会间接改变了算法的细节。

比如 3y 在文章模板中的 introduction() 改了,“只有充钱才能变强”

@Override
public void introduction() {
System.out.println(“ 只有充钱才能变强 ”);
}

我们没有碰过 writeAnCompleteArticle() 的代码,但再次调用这个方法的时候,具体的实现就会发生改变 ( 因为 writeAnCompleteArticle 受子类的具体实现影响)
下面我们看一下模板方法模式的通用类图:

在模板方法模式中,也有几个术语,根据我们的例子中的注释,我给大家介绍一下:

// 抽象模板类
public abstract class WriteArticle {

// 基本方法
protected abstract void introduction();

// 基本方法
protected abstract void theLast();

// 基本方法
protected abstract void actualContent();

// 模板方法
public final void writeAnCompleteArticle() {
introduction();
actualContent();
theLast();
}
}

// 具体模板类
public class Java3yWriteArticle extends WriteArticle {

// 实现基本方法
@Override
public void introduction() {
System.out.println(“ 只有充钱才能变强 ”);
}

// 实现基本方法
@Override
public void theLast() {
System.out.println(“ 关注我的公众号:Java3y”);
}

// 实现基本方法
@Override
protected void actualContent() {
System.out.println(“ 大家好,我是 3y,今天来给大家分享我写的模板方法模式 ”);
}
}

基本方法:在子类实现,并且在模板方法中被调用

模板方法:定义了一个框架,实现对基本方法的调用,完成固定的逻辑。

1.4 模板方法的优缺点
优点:

封装不变的部分,扩展可变的部分。把认为是不变的部分的算法封装到父类,可变部分的交由子类来实现!
提取公共部分的代码,行为由父类控制,子类实现!

缺点:
抽象类定义了部分抽象方法,这些抽象的方法由子类来实现,子类执行的结果影响了父类的结果 (子类对父类产生了影响),会带来阅读代码的难度!
1.5 模板方法模式 JDK 应用
最经典的就是 JUC 包下的 AQS(AbstractQueuedSynchronizer) 了。AQS 是什么?
AQS 其实就是一个可以给我们实现锁的框架。内部实现的关键是:先进先出的队列、state 状态
我们可以看一下 AQS 定义的 acquire()

public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}

acquire() 相当于模板方法,tryAcquire(arg) 相当于基本方法。

最后
模板方法模式也很简单呀,一个抽象类有基本方法 (等着被子类实现的方法),有模板方法 (对外暴露、调用基本方法、定义了算法的框架),那就完事了。
推荐阅读和参考资料:

《设计模式之禅》
https://blog.csdn.net/carson_ho/article/details/54910518
https://blog.csdn.net/hguisu/article/details/7564039

乐于分享和输出干货的 Java 技术公众号:Java3y。关注即可领取海量的视频资源!

精彩回顾:
2018 总结【14 本书免费送】
觉得我的文章写得不错,不妨点一下赞!

退出移动版