一、模式动机
准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。这就是模版方法模式的用意。
模版方法模式需要开发抽象类和具体子类的设计师之间的协作。一个设计师负 责给出一个算法的轮廓和骨架,另一些设计师则负责给出这个算法的各个逻辑步骤。代表这些具体逻辑步骤的方法称做基本方法(primitive method);而将这些基本法方法总汇起来的方法叫做模版方法(template method),这个设计模式的名字就是从此而来。
二、模式定义
模板方法(Template Method):定义一个操作中算法的骨架,而将一些 步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。模板方法是一种类行为型模式。
三、模式结构
四、参与者
- AbstractClass:抽象类
- ConcreteClass:具体子类
五、示例代码
package design.pattern;
// 抽象模板
abstract class TestPaper {
public void testQuestion1() {
System.out.println("杨过得到,后来给了郭靖,炼成倚天剑、屠龙刀的玄铁可能是[ ] a.球磨铸铁 b.马口铁 c.高速合金钢 d.碳素纤维 ");
System.out.println("答案: " + answer1());
}
public void testQuestion2() {
System.out.println("杨过、程英、陆无双铲除了情花,造成[ ] a.使这种植物不再害人 b.使一种珍稀物种灭绝 c.破坏了那个生物圈的生态平衡 d.造成该地区沙漠化 ");
System.out.println("答案: " + answer2());
}
public void testQuestion3() {
System.out.println("蓝凤凰的致使华山师徒、桃谷六仙呕吐不止,如果你是大夫,会给他们 开什么药[ ] a.阿司匹林 b.牛黄解毒片 c.氟哌酸 d.让他们喝大量的生牛奶 e.以上全不对 ");
System.out.println("答案: " + answer3());
}
public abstract String answer1();
public abstract String answer2();
public abstract String answer3();
}
// A实现
class TestPaperA extends TestPaper {
public String answer1() {
return "b";
}
public String answer2() {
return "c";
}
public String answer3() {
return "a";
}
}
// B实现
class TestPaperB extends TestPaper {
public String answer1() {
return "c";
}
public String answer2() {
return "a";
}
public String answer3() {
return "a";
}
}
public class Template {
public static void main(String[] args) {
System.out.println("学生甲的试卷: ");
TestPaper studentA = new TestPaperA();
studentA.testQuestion1();
studentA.testQuestion2();
studentA.testQuestion3();
System.out.println("学生乙的试卷: ");
TestPaper studentB = new TestPaperB();
studentB.testQuestion1();
studentB.testQuestion2();
studentB.testQuestion3();
}
}
模式结构
附加示例
极品飞车中有很多的汽车,但是从操作角度看大同小异。无非是起步(StartUp)、行驶(Run)、停车(Stop)等行为。
结合Template Method模式讲就是在这个程序中,结构(对汽车的操作)是稳定的,但是变化在于各个子步骤(操作行为的具体实现)。
// 抽象模板车
/**
* 在这段代码中,抽象方法StartUp、Run、Stop叫做primitive operation(原语操作),它们是在子类中的扩展点。
*
*/
abstract class AbstractCar {
protected abstract String startUp();
protected abstract String run();
protected abstract String stop();
/**
* AbstractCar中的DriveOnTheRoad方法叫做template method(模板方法),
* template method用primitive operation定义一个算法,是相对稳定的部分。
* (子类中重新定义primitive operation)。
*/
public void driveOnTheRoad() {
System.out.println(startUp());
System.out.println(run());
System.out.println(stop());
}
}
// 具体车
class BORA extends AbstractCar {
protected String startUp() {
return "BORA is startup";
}
protected String run() {
return "BORA is running";
}
protected String stop() {
return "BORA is stopped";
}
public static void main(String[] args) {
AbstractCar car = new BORA();
car.driveOnTheRoad();
}
}
优点
- 模板方法模式在一个类中形式化地定义算法,而由它的子类实现细节的处理。 模板方法模式的优势是,在子类定义详细的处理算法时不会改变算法的结构。
- 模板方法是一种代码复用的基本技术,它们在类库中尤为重要,它们提取了类库中的公共行为。
- 模板方法导致一种反向的控制结构,一个父类调用一个子类的操作,而不是相反。
缺点
- 模板方法的缺点在于每个不同的实现都需要定义一个子类,这会导致类的个数增加,但是更加符合类职责的分配原则,使得类的内聚性得以提高。
模式使用
- 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
- 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。
- 对一些复杂的算法进行分割,将其算法中固定不变的部分设计为模板方法和父类具体方法,而一些可以改变的细节由其子类来实现。
模式应用
- 模板方法模式主要应用于框架设计,以确保父类控制处理流程的逻辑顺序(如框架的初始化)。
- 模板方法模式鼓励我们恰当使用继承,此模式可以用来改写一些拥有相同功能的相关类,将可复用的一般性的行为代码移到父类里面,而将特殊化的行为代码移到子类里面。这也进一步说明,虽然继承复用存在一些问题,但是在某些情况下还是可以给开发人员带来方便,模板方法模式就是体现继承优势的模式之一。
- 在模板方法模式中,子类不显式调用父类的方法,而是通过覆盖父类的方法来实现某些具体的业务逻辑,父类控制对子类的调用,这种机制被称为好莱坞原则(Hollywood Principle),即“不要给我们打电话,我们会给你打电话(Don‘t call us, we’ll call you)”。
- 在模板方法模式中,好莱坞原则体现在:子类不需要调用父类,而通过父类来调用子类,将某些步骤的实现写在子类中,由父类来控制整个过程。