1.类图和定义
Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.(定义一个操作中的算法框架,而将一些步骤延迟到子类中。使得子类可以不用改变一个算法的结构即可重定义该算法的某些特定步骤)
模板方法涉及的角色:
- 抽象模板:定义一个或多个抽象操作,以便让子类实现;定义并实现了一个模板方法。这个模板方法一般是一个具体方法,他给出了顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类去实现。
- 具体模板:实现了父类所有的抽象操作,他们是一个顶级逻辑的组成步骤;具体模板可以有多个不同的实现。
模板方法模式中的方法:
- 基本方法(primitive method):具体算法的逻辑步骤,如上图的doOperation1(),由子类实现,并在模板方法中被调用。
- 模板方法(template method):实现对基本方法的调用的方法,一般是一个具体方法,完成固定的逻辑。模板方法一般会在抽象类中定义,并由子类不加以修改地完全继承下来。
2.代码
抽象模板类的代码:
public abstract class AbstractClass {
/**
* 模板方法的声明和实现
*/
public final void templateMethod() {
//调用基本方法(有子类实现)
doOperation1();
//调用基本方法(有子类实现)
doOPeration2();
//调用基本方法(已经实现)
doOperation3();
}
//基本方法声明
protected abstract void doOperation1();
//基本方法声明
protected abstract void doOperation2();
//基本方法(已经实现)
protected final void doOperation3() {
System.out.println("doOperation3()");
}
}
具体模板角色代码:
public class ConcreteClass1 extends AbstractClass {
/**
*基本方法的实现
*/
public void doOperation1() {
System.out.println("doOPeration1()");
}
public void doOperation2() {
System.out.println("doOPeration2()");
}
}
场景类代码:
AbstractClass class1 = new ConcreteClass1();
class1.templateMethod();
3.java语言中使用的模板方法模式
作为Java对Web系统的解决方案,HttpServlet技术是建立在模板方法模式的基础之上的。HttpServlet类提供了一个service()方法。这个方法调用了7个do方法中的一个或几个,完成对客户端调用的处理。这个do方法则要由具体HttpServlet类提供。在这里,service()方法便是模板方法,7个do方法便是基本方法。详情请查看源代码。
4.模板方法模式在代码重构中的应用
一个初级程序员往往会将很多代码放在一个很大的方法里面,造成一个几千行的大方法。这样的方法应当拆分成一些较小的方法,拆分的策略可以使用模板方法模式。
4.1.将大方法打破
代码清单如下:
public void bigMethod() {
...
代码块1
...
代码块2
...
代码块3
...
代码块4
...
代码块5
...
}
首先,将这个大方法作为模板方法。将这个大方法打碎成为很多的步骤,每一个步骤的代码划分到一个新的小方法里面,由模板方法调用一个个小方法,代码如下:
public void bigMethod() {
step1();
step2();
if (...) {
step3();
}
if (...) {
step4();
}
if (...) {
step5();
}
}
private void step1() {
原来的代码块1
}
private void step2() {
原来的代码块2
}
...
上面代码省略了step3()、step4()和step5()方法,他们的构造同step1()和step2()方法。
4.2.建立取值方法
经过上面步骤后,一个需要解决的问题就是那些原来在大方法内部声明的变量。这些变量原本都是局部变量。为了能将这些变量与基本方法共享,有两种做法:一是将他们改为超类层次上的私有或保护变量;二是建立一些抽象取值方法,而将取值方法的实现推迟到子类。
这样做的好处,是可以将状态的声明尽量推迟到子类,而使得等级结构的高端与状态无关。
4.3.建立常量方法
与属性一样,常量也应当以常量取值方法的形式封装,这个常量方法所做的唯一事情就是返回这个常量。
4.4.如此反复
如此反复直到所有的基本方法都变成基本上一样的令人满意的细粒度,而且所有的常量都放到了常量方法里面。这个时候就得到了一个类,里面有一个模板方法和一系列的基本方法。
一般来说,到此重构可以结束了。但是,如果这些方法里面有一些特征相同、功能相同而细节不同的方法,那么重构过程就可以利用继承和继承带来的多态性继续下去。
4.5.以多态性取代条件转移
将所考虑的类作为模板方法模式的抽象类,设计一些具体子类,再将基本方法中特征相同、功能相同而细节不同的基本方法划分到不同的具体子类里面去。假设方法step3(),step4()和step5()是特征相同、功能相同而实现不同的方法,那么这三个方法可以作为一个新方法newMethod()的多态性体现,划分到三个不同的子类中去,代码如下:
public abstract class AbstractClass {
public void bigMethod() {
step1();
step2();
newMethod();
}
protected abstract void step1();
protected abstract void step2();
protected abstract void newMethod();
}
public class ConcreteClass1 extends AbstractClass {
public void newMethod() {
原来的代码块3
}
}
public class ConcreteClass2 extends AbstractClass {
public void newMethod() {
原来的代码块4
}
}
经过重构后的类图如下:
在对一个继承的等级结构做重构时,一个应当遵循的原则便是,将行为尽量移动到机构的高端,而将状态尽量移动到结构的低端。