定义
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
类图
解决问题
模板方法就是提供一个算法框架,框架里面的步骤有些是父类已经定好的,有些需要子类自己实现。相当于要去办一件事情,行动的流程已经定好了,但有些步骤需要自己去做,而有些步骤可能别人帮我们做了。就拿建网站来说,一般的程序是购买域名–>购买空间–>上传网站–>备案–>审核,每个网站的创建必须经过这样的固定程序,但除了审核不用建站者关心,其他的步骤都要建站者自己去完成。
应用实例
现在我们很多家庭都有了豆浆机,豆浆的营养价值不用我多说了。制作豆浆的程序简单点来说就是选材—>添加配料—>浸泡—>放到豆浆机打碎,通过添加不同的配料,可以制作出不同口味的豆浆,但是选材、浸泡和放到豆浆机打碎这几个步骤对于制作每种口味的豆浆都是一样的。
抽象类
package templatemethod.pattern;
//豆浆类,抽象类
public abstract class SoyaMilk {
//这是模板方法,用final修饰,不允许子类覆盖。模板方法定义了制作豆浆的程序
final void prepareRecipe(){
selectMaterial();
addCondiments();
soak();
beat();
}
//选材方法,选择黄豆
void selectMaterial(){
System.out.println("第一步、选择好了新鲜黄豆");
}
//可以添加不同的配料,在这里设置为抽象方法,子类必须实现
abstract void addCondiments();
//浸泡
void soak(){
System.out.println("第三步、黄豆和配料开始浸泡,大概需要5个小时");
}
//放到豆浆机打碎
void beat(){
System.out.println("第四步、黄豆的配料放到豆浆机打碎");
}
}
红枣豆浆
package templatemethod.pattern;
//红枣豆浆
public class ReddatesSoyaMilk extends SoyaMilk{
//实现父类的添加配料方法
@Override
void addCondiments() {
System.out.println("第二步、添加红枣配料");
}
}
核桃豆浆
package templatemethod.pattern;
//核桃豆浆
public class NutSoyaMilk extends SoyaMilk{
//实现父类的添加配料方法
@Override
void addCondiments() {
System.out.println("第二步、添加核桃配料");
}
}
测试类
package templatemethod.pattern;
public class SoyaMilkTest {
public static void main(String[] args){
//制作红枣豆浆
System.out.println();
System.out.println("-----制作红枣豆浆步骤-------");
SoyaMilk reddatesSoyaMilk = new ReddatesSoyaMilk();
reddatesSoyaMilk.prepareRecipe();
//制作核桃豆浆
System.out.println();
System.out.println("-----制作核桃豆浆步骤-------");
SoyaMilk nutSoyaMilk = new NutSoyaMilk();
nutSoyaMilk.prepareRecipe();
}
}
运行结果
优点
(1)、算法只存在于一个地方,也就是在父类中,容易修改。需要修改算法时,只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改。
(2)、实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用。
(3)、既统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的结构保持不变,同时由子类提供部分步骤的实现。 (4)、提供了一个基本框架,容易扩展子类。模板方法有框架控制如何做事情,而由使用框架的人指定框架算法中每个步骤的细节。子类只要继承父类,实现抽象方法,就可以使用父类的算法。
缺点
(1)、模板方法使用继承方式复用代码,如果要在基本算法里面增加一个步骤,而该步骤是抽象的话,每个子类都要修改代码,实现这个步骤。
JDK类库中的模版方法模式
java.util.Collections#sort()
java.io.InputStream#skip()
java.io.InputStream#read()
java.util.AbstractList#indexOf()