模板方法模式是定义一个操作中算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变算法的结构即可重定义该算法的某些特定步骤。
通俗点的理解说就是完成一件事情,有固定的数个步骤,但是每个步骤根据对象的不同,而实现细节不同;就可以在父类中定义一个完成该事情的总方法,按照完成事件需要的步骤去调用其每个步骤的实现方法。每个步骤的具体实现,由子类完成。
小A和小B去参加考试的故事。
public class TestPageA {
// 试题1
public void testQuestion1() {
System.out.println("杨过得到,后来给了郭靖,炼成倚天剑、屠龙刀的玄铁可能是()a.球磨铸铁 b.马口铁 c.高速合金钢 " +
"碳素纤维");
System.out.println("答案:b");
}
// 试题2
public void testQuestion2 () {
System.out.println("杨过、程英、陆无双铲除了情花,造成[] a. 使这种植物不再害人b.使种珍稀物种灭绝" +
"C. 破坏了那个生物圈的生态平衡d. 造成该地区沙漠化");
System.out.println(" 答案: a");
}
// 试题3
public void testQuestion3() {
System.out.println("蓝凤凰致使华山师徒、桃谷六仙咽吐不止,如果你是大夫,会给他们开什么药[ 1a.阿司匹林b.牛黄解毒片c.氟账酸d.让他们喝大量的生牛奶e.以上:全不对");
System.out.println("答案: c");
}
}
public class TestPageB {
// 试题1
public void testQuestion1() {
System.out.println("杨过得到,后来给了郭靖,炼成倚天剑、屠龙刀的玄铁可能是()a.球磨铸铁 b.马口铁 c.高速合金钢 " +
"碳素纤维");
System.out.println("答案:a");
}
// 试题2
public void testQuestion2 () {
System.out.println("杨过、程英、陆无双铲除了情花,造成[] a. 使这种植物不再害人b.使种珍稀物种灭绝" +
"C. 破坏了那个生物圈的生态平衡d. 造成该地区沙漠化");
System.out.println(" 答案: b");
}
// 试题3
public void testQuestion3() {
System.out.println("蓝凤凰致使华山师徒、桃谷六仙咽吐不止,如果你是大夫,会给他们开什么药[ 1a.阿司匹林b.牛黄解毒片c.氟账酸d.让他们喝大量的生牛奶e.以上:全不对");
System.out.println("答案: a");
}
}
public class Test {
public static void main(String[] args) {
System.out.println("小A的试卷");
TestPageA testPageA = new TestPageA();
testPageA.testQuestion1();
testPageA.testQuestion2();
testPageA.testQuestion3();
System.out.println("小B的试卷");
TestPageB testPageB = new TestPageB();
testPageB.testQuestion1();
testPageB.testQuestion2();
testPageB.testQuestion3();
}
}
通过上面的代码我们可以看出小A和小B的试卷除了答案不相同,没什么不一样,这样写又容易出错,又难以维护。如果老师要更改题目,那么两个人都要更改代码。接下来我们用模板方法的方式解决。
public abstract class TestPage {
public void testQuestion1() {
System.out.println("杨过得到,后来给了郭靖,炼成倚天剑、屠龙刀的玄铁可能是()a.球磨铸铁 b.马口铁 c.高速合金钢 " +
"碳素纤维");
if (isAnswer()) {
System.out.println("答案:"+answer1());
} else {
System.out.println("答案:同学未填写答案");
}
}
public void testQuestion2 () {
System.out.println("杨过、程英、陆无双铲除了情花,造成[] a. 使这种植物不再害人b.使种珍稀物种灭绝" +
"C. 破坏了那个生物圈的生态平衡d. 造成该地区沙漠化");
System.out.println("答案:"+answer2());
}
// 试题3
public void testQuestion3() {
System.out.println("蓝凤凰致使华山师徒、桃谷六仙咽吐不止,如果你是大夫,会给他们开什么药[ 1a.阿司匹林b.牛黄解毒片c.氟账酸d.让他们喝大量的生牛奶e.以上:全不对");
System.out.println("答案: "+answer3());
}
// 回答问题方法1
public abstract String answer1();
// 回答问题方法2
public abstract String answer2();
// 回答问题方法3
public abstract String answer3();
// 是否填写答案
public abstract boolean isAnswer () ;
}
public class TestPageA extends TestPage{
@Override
public String answer1() {
return "b";
}
@Override
public String answer2() {
return "a";
}
@Override
public String answer3() {
return "c";
}
@Override
public boolean isAnswer() {
return false;
}
}
public class TestPageB extends TestPage {
@Override
public String answer1() {
return "a";
}
@Override
public String answer2() {
return "b";
}
@Override
public String answer3() {
return "a";
}
@Override
public boolean isAnswer() {
return true;
}
}
public class Test {
public static void main(String[] args) {
System.out.println("---------小A的试卷--------");
TestPage testPageA = new TestPageA();
testPageA.testQuestion1();
testPageA.testQuestion2();
testPageA.testQuestion3();
System.out.println("---------小B的试卷---------");
TestPage testPageB = new TestPageB();
testPageB.testQuestion1();
testPageB.testQuestion2();
testPageB.testQuestion3();
}
}
此时更多的学生来回答试卷,只不过在试卷答案模板上填写选择题的答案,这是每个人唯一的同。在代码中添加的isAnswer方法在模板方法模式也称钩子方法,通过子类覆盖父类的钩子方法来决定某一特定步骤是否需要执行。
模板方法模式是通过把不变的行为搬移到抽象类,去除子类中的重复代码。
优点:
模板方法模式在一个类中定义算法,而由它的子类实现细节的处理。
模板方法是一种代码复用的基本技术。它们在类库中尤为重要,它们提取了类库中的公共行为。
模板方法模式导致一种反向的控制结构,这种结构有时被称为“好莱坞法则” ,即“别找我们,,我们找你”通过一个父类调用其子类的操作(而不是相反的子类调用父类),通过对子类的扩展增加新的行为,符合“开闭原则”。
缺点:
每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,但是更加符合“单一职责原则”,使得类的内聚性得以提高。
对一些复杂的算法进行分割,将其算法中固定不变的部分设计为模板方法和父类具体方法,而一些可以改变的细节由其子类来实现。
子类中公共的行为提取出来并集中到一个抽象类中以避免代码重复。
通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制。