Java设计模式-模板方法模式
0.前言
模板方法模式(TemplateMethod Pattern):父类(抽象类)中定义处理流程的框架,在子类中实现具体处理。
1.模板方法原理
父类(抽象类)中只定义一些子类需要的抽象方法 和 这些方法的调用步骤(在一个具体的方法中)。
子类实现了父类的抽象方法的具体处理过程。
也就是说,不同的子类虽然具体实现可以各不相同,但是有着一样的处理流程。
若还是觉得抽象的话,看下面这个具体的例子就知道了:
小明和小红都是程序员,每天都会:起床,吃饭,写代码,睡觉。
这相当于父类中的四个抽象方法,父类规定了这些方法的调用顺序为:起床,吃饭,写代码,睡觉。
但是呢:
小明(相当于程序员父类的子类)的具体实现为:8点起床,吃饭吃的外卖,写后端代码,11点睡觉。
小红(相当于程序员父类的子类)的具体实现为:7点起床,吃饭吃自己做的饭菜,写前端代码,10点睡觉。
虽然具体实现不一样,但是四个方法的处理流程是一样的。
注意:这个流程中也可以有具体处理过程一摸一样的方法,需在父类中以具体的方法体现。
比如:小明和小红每天都锻炼半小时,这样原本的程序员父类就有四个抽象方法:起床,吃饭,写代码,睡觉。两个具体的方法:锻炼半小时,处理过程(定义了方法调用顺序)。在处理过程这个具体的方法中,调用顺序为:起床,吃饭,写代码,锻炼半小时,睡觉。
后面的代码也会按照这个例子实现。
2.模板方法模式中的角色
抽象父类AbstractClass:负责定义所需使用的模板方法,可以是抽象方法,也可以有具体的方法。还需有一个具体方法来确定这些方法的调用顺序和一些其他的操作。
具体子类ConcreteClass:负责具体实现抽象父类的抽象方法。无需担心方法的调用和具体处理流程。
3.模板方法模式的UML类图
非常简单,就两个类:
4.代码实现
接下来,用代码模拟上述例子:
程序员父类(抽象父类)
/**
* 模板方法模式
* 程序员父类(抽象父类)
* 4个抽象方法:起床,吃饭,写代码,睡觉
* 2个具体的方法:锻炼半小时,处理过程
*/
public abstract class Coder {
public abstract void getup();//起床
public abstract void eat();//吃饭
public abstract void code();//写代码
public abstract void sleep();//睡觉
public void exercise(){
System.out.println("锻炼半小时");
}
//模板(定义了各个方法的调用顺序和一些其他操作)
public void TemplateMethod(){
getup();
eat();
code();
exercise();
sleep();
}
}
小明(具体实现类):
public class Ming extends Coder {
@Override
public void getup() {
System.out.println("8点起床");
}
@Override
public void eat() {
System.out.println("吃饭吃的外卖");
}
@Override
public void code() {
System.out.println("写后端代码");
}
@Override
public void sleep() {
System.out.println("11点睡觉");
}
}
小红(具体实现类)
public class Red extends Coder{
@Override
public void getup() {
System.out.println("7点起床");
}
@Override
public void eat() {
System.out.println("吃饭吃自己做的饭菜");
}
@Override
public void code() {
System.out.println("写前端代码");
}
@Override
public void sleep() {
System.out.println("10点睡觉");
}
}
5.编码测试
测试编码:
public class Test {
public static void main(String[] args) {
//模板方法模式
System.out.println("模板方法模式测试\n");
System.out.println("小明的一天*****");
Coder ming = new Ming();
ming.TemplateMethod();//执行模板方法
System.out.println();//换行
System.out.println("小红的一天*****");
Coder red = new Red();
red.TemplateMethod();//执行模板方法
}
}
测试结果:
模板方法模式测试
小明的一天*****
8点起床
吃饭吃的外卖
写后端代码
锻炼半小时
11点睡觉
小红的一天*****
7点起床
吃饭吃自己做的饭菜
写前端代码
锻炼半小时
10点睡觉
可看到按照抽象父类指定的顺序执行,子类只管实现方法,而不管各方法调用顺序流程。
6.模板模式中的钩子方法
试想一下这个场景:小明今天放假了,不写代码。(假设上班就写代码,不上班就不写代码)
那要如何改进上述的模板模式呢?
修改如下:
添加一个是否需要工作的方法(注意是具体的方法,不是抽象方法,这样子类可以选择性的重写),返回值为布尔类型,用true代表需要写代码,false代表不需要写代码。在模板方法中在原本需要执行code()方法的地方加入判断语句即可。
这种在模板模式中抽象父类中,一个默认不做任何事,子类可以选择性重写的方法,称为钩子方法。
具体编码如下:
public abstract class Coder {
public abstract void getup();//起床
public abstract void eat();//吃饭
public abstract void code();//写代码
public abstract void sleep();//睡觉
public void exercise(){
System.out.println("锻炼半小时");
}
//模板(定义了各个方法的调用顺序和一些其他操作)
public void TemplateMethod(){
getup();
eat();
if (isWork()){
code();//上班就写代码,不上班不写代码
}
exercise();
sleep();
}
//钩子方法:判断是否上班
public boolean isWork(){
return true;//设置默认值
}
}
小明的类需要重写该方法,并且返回false。
public class Ming extends Coder {
//不上班
@Override
public boolean isWork() {
return false;
}
@Override
public void getup() {
System.out.println("8点起床");
}
@Override
public void eat() {
System.out.println("吃饭吃的外卖");
}
@Override
public void code() {
System.out.println("写后端代码");
}
@Override
public void sleep() {
System.out.println("11点睡觉");
}
}
测试代码不需要改变,测试结果为:
模板方法模式测试
小明的一天*****
8点起床
吃饭吃的外卖
锻炼半小时
11点睡觉
小红的一天*****
7点起床
吃饭吃自己做的饭菜
写前端代码
锻炼半小时
10点睡觉
可以看到,小明的一天就没有写代码了。而小红依然不变,而若小红也不需要写代码时,同样重写isWork()方法并返回false即可。
7.小结
使用场景:当要完成的一个过程它需要经过一系列步骤,而且一些步骤大致相同时,但其个别步骤实现可能不同,需要考虑模板方法模式。
优点:模板方法模式可以使 逻辑处理通用化,无需在每个类中写逻辑处理(如方法调用顺序),实现了代码复用。