概述
定义:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
通俗的讲,模板方法模式是通过把不变行为搬到超类,去除子类里面的重复代码提现它的优势,它提供了一个很好的代码复用平台。当不可变和可变的方法在子类中混合在一起的时候,不变的方法就会在子类中多次出现,这样如果摸个方法需要修改则需要修改很多个,虽然这个这个问题在设计之初就应该想好。这个时候模板方法模式就起到了作用了,通过模板方法模式把这些重复出现的方法搬到单一的地方,这样就可以帮助子类摆脱重复不变的纠缠。
UML
代码实现
举个好懂的例子,小时候笔者家里穷,在农村上小学的时候考试都是每个学生手抄试卷,因为那个时候学校还没有试卷印刷。全班五十多个学生每个学生都要重复抄一遍黑板的试卷,并且像笔者这样的近视眼很容易就抄错了,8抄成3,7抄成1等到,然后明明做对了但是分数就是不高,导致笔者一直是全班倒数。这就是个很严重的重复不可变的问题,现在条件好了不少,学生不需要抄试卷,试卷印刷就解决了这个重复抄试卷的问题。模板方法也是类似。
不用设计模式的实现
学生甲抄的试卷
public class TestPaperA {
/**
* 试卷第一题
*/
public void testQuestion1() {
System.out.println("1.小龙女是杨过的什么亲戚?() " +
"A.小姨妈 B.大姨妈 C.姑妈 D.舅妈");
System.out.println("答案:C \n");
}
/**
* 试卷第二题
*/
public void testQuestion2() {
System.out.println("2.全真教的首任掌门是谁?" +
"A.周伯通 B.欧阳锋 C.王重阳 D.西门吹牛");
System.out.println("答案:C \n");
}
/**
* 试卷第三题
*/
public void testQuestion3() {
System.out.println("3.《天龙八部》中被封为南院大王的大侠是谁?" +
"A.段誉 B.乔峰 C.慕容复 D.段智兴");
System.out.println("答案:B \n");
}
}
学生乙抄的试卷
public class TestPaperB {
/**
* 试卷第一题
*/
public void testQuestion1() {
System.out.println("1.小龙女是杨过的什么亲戚?() " +
"A.小姨妈 B.大姨妈 C.姑妈 D.舅妈");
System.out.println("答案:A\n");
}
/**
* 试卷第二题
*/
public void testQuestion2() {
System.out.println("2全真教的首任掌门是谁?" +
"A.周伯通 B.欧阳锋 C.王重阳 D.西门吹牛");
System.out.println("答案:C\n");
}
/**
* 试卷第三题
*/
public void testQuestion3() {
System.out.println("3.《天龙八部》中被封为南院大王的大侠是谁?" +
"A.段誉 B.乔峰 C.慕容复 D.段智兴");
System.out.println("答案:D\n");
}
}
客户端代码
public class Client {
public static void main(String[] args) {
System.out.println("学生甲的试卷:\n");
TestPaperA stuA = new TestPaperA();
stuA.testQuestion1();
stuA.testQuestion2();
stuA.testQuestion3();
System.out.println("学生乙的试卷:\n");
TestPaperB stuB = new TestPaperB();
stuB.testQuestion1();
stuB.testQuestion2();
stuB.testQuestion3();
}
}
很容易发现上面两个学生抄的试卷有很多重复的地方,比如试卷的题目,输出答案的方法,这些都在每个学生试卷类中混合在一起了,既不利于维护,也不利于浏览,下面看一下模板方法模式是怎么改进的。
模版方法的实现
首先改造试卷类,将该类改为抽象类,在该类中我添加了三个抽象的方法用于子类实现,学生都是要作答的,但是答案不一样,所以可以将作答的过程作为重复不变的方法提取出来,代码如下:
public abstract class TestPaper {
public void testQuestion1() {
System.out.println("\n1.小龙女是杨过的什么亲戚?() " +
"A.小姨妈 B.大姨妈 C.姑妈 D.舅妈 ");
System.out.println("答案:" + answer1());
}
public void testQuestion2() {
System.out.println("\n2.全真教的首任掌门是谁?" +
"A.周伯通 B.欧阳锋 C.王重阳 D.西门吹牛");
System.out.println("答案:" + answer2());
}
public void testQuestion3() {
System.out.println("\n3.《天龙八部》中被封为南院大王的大侠是谁?" +
"A.段誉 B.乔峰 C.慕容复 D.段智兴");
System.out.println("答案:" + answer3());
}
public abstract String answer1();
public abstract String answer2();
public abstract String answer3();
public void exam() {
testQuestion1();
testQuestion2();
testQuestion3();
}
}
首先来看第一个学生的考试情况
public class TestPaperA extends TestPaper{
@Override
public String answer1() {
return "A";
}
@Override
public String answer2() {
return "B";
}
@Override
public String answer3() {
return "D";
}
}
其他学生的试卷可能答案不是一样的,但是基本的答题过程就是一样的,所以就不重复写了,下面看下客户端代码:
public class Client {
public static void main(String[] args) {
TestPaper testPaper = new TestPaperA();
testPaper.exam();
}
}
运行结果:
1.小龙女是杨过的什么亲戚?() A.小姨妈 B.大姨妈 C.姑妈 D.舅妈
答案:A
2.全真教的首任掌门是谁?A.周伯通 B.欧阳锋 C.王重阳 D.西门吹牛
答案:B
3.《天龙八部》中被封为南院大王的大侠是谁?A.段誉 B.乔峰 C.慕容复 D.段智兴
答案:D
总结
模板方法模式就是为了将重复不变的代码提取到一个抽象类中。当我们要完成在某一细节层次一致的一个过程或一系列步骤,但其个别步骤在更详细的层次上的实现可能不同时,我们通常考虑用模板方法模式来处理。