一、引言
在生活中存在着大量在某一细节层次上实现步骤相同的一些事物或过程,但是这些事物的个别或全部步骤在更详细的层次上的具体实现可能不同。举个简单的例子来说:包饺子和包混沌都具有和面、拌馅、动手包、下水煮这几个步骤,但它们的每个步骤中既有相同的元素,也有各自的特点。那么在实现这些事物时,为了提高代码的复用性、便于代码的修改和避免重复编码,我们就需要用到模版方法模式。
二、模式解析
2.1 模式分析
模板方法模式定义了一个操作中算法的主要步骤和骨架,并将细节步骤延迟到在子类中定义,仅在父类中预留相应的接口,这样,可以在不改变一个算法结构的前提下通过定义新的子类并重新定义该算法的某些步骤来实现一个新的事物。
如图-1给出的模版方法模式UML图所示,模版方法模式首先从所有总体步骤相同的事物中抽象出一个抽象模版方法类AbstractClass,该类中的TamplateMethod方法定义了所有事物的总体实现步骤。而每个总体步骤的具体实现则被抽象为了PrimativeMethodn()方法,这些Primative方法将在具体子类ConcretClass中根据各个事物的特点进行重写。这样如果想要增加新的事物,就只需要定义一个新的AbstractClass的子类并重写定具有不同实现步骤的抽象方法即可,增加了代码的可复用性。
图-1 模板方法模式UML图
2.2 具体实现
我们依然用引言中包饺子和包混沌的例子说明模版方法模式。我们首先定义一个抽象制作食物类MakeFood,也就是图-1中的AbstractClass类,该类定义了包饺子和包馄炖一致的总体流程,并将他们不同的具体实现部分抽象为了不同的抽象方法。
abstract class MakeFood {
/**
* 定义了包馄炖和包饺子的总体流程,而流程中每个步骤两者不同的细节实现部分则被抽象成了抽象方
* 法。由于制作饺子和馄炖总体流程确定,所以可以将该方法定义为final类型以防止被重写。
*/
public final void makeFood() {
System.out.println("第1步: 和面,准备水和面");
doughMaking();
System.out.println("第2步: 拌馅,准备菜和肉");
prepareStuffing();
System.out.println("第3步: 动手包");
makeIt();
System.out.println("第4步: 下水煮");
cookIt();
}// makeFood()
/**
* 以下方法定义了包饺子和包馄炖不同的细节步骤,这些方法在总体流程方法中被调用。
*/
protected abstract void doughMaking();
protected abstract void prepareStuffing();
protected abstract void makeIt();
protected abstract void cookIt();
}/*MakeFood*/
接着再分别定义包饺子和包馄炖的具体子类,继承MakeFood类并根据各自的特点重写相应的具体实现方法。
/**
* 定义了包饺子的方法,相当于ConcreteClass类,继承MakeFood类并重写各个细节步骤方法以定义包饺
* 子的特有流程。
*/
class MakeDumplings extends MakeFood {
@Override
protected void doughMaking() {
System.out.println("|---和饺子皮的面,不能太软!");
}// doughMaking
@Override
protected void prepareStuffing() {
System.out.println("|---拌饺子馅,菜和肉不同剁得太细!");
}// prepareStuffing
@Override
protected void makeIt() {
System.out.println("|---包饺子,皮厚个大!");
}// makeIt
@Override
protected void cookIt() {
System.out.println("|---煮饺子用清水,汤里不放菜!");
}// cookIt
}/*MakeDumplings*/
/**
* 定义了包馄炖的方法,相当于ConcreteClass类,继承MakeFood类并重写各个细节步骤方法以定义包馄
* 炖的特有流程。
*/
class MakeWonton extends MakeFood {
@Override
protected void doughMaking() {
System.out.println("|---和馄炖皮的面,要嫩一些!");
}// doughMaking
@Override
protected void prepareStuffing() {
System.out.println("|---拌馄炖馅,菜和肉要细细剁!");
}// prepareStuffing
@Override
protected void makeIt() {
System.out.println("|---包馄炖,皮薄个小!");
}// makeIt
@Override
protected void cookIt() {
System.out.println("|---煮馄炖用鸡汤,汤里放紫菜虾皮!");
}// cookIt
}/*MakeWonton*/
最后是客户端程序:
public class TamplateMethodDemo {
public static void main(String[] args) {
MakeFood food = null;
System.out.println("我要吃饺子!");
food = new MakeDumplings();
food.makeFood();
System.out.println();
System.out.println("我要吃馄炖!");
food = new MakeWonton();
food.makeFood();
}// main
}/*TamplateMethodDemo*/
程序运行结果:
三、总结
模版方法模式将流程中的不变行为搬移到超类中,而将可变的行为搬移到了子类中去具体实现。这样,当遇到不变和可变混合在一起的编程时,利用模版方法模式就可以避免在子类中重复对不变行为的编码,在增加新的类似事物时,就会减少编码的工作量。例如上文中的例子中,如果我们要添加一个制作汤圆的类,那么只需要重写MakeFood类中的抽象方法即可,不需要在定义制作汤圆的总体流程。模版方法模式很简单,很多在人在不知不觉中就会多多少少的用到它,它其实就是将类库中的公共行为提取到抽象类中。
模版方法模式可以与工厂模式结合起来,MakeFood就是抽象产品类,各种食物就是具体产品,只需要添加工厂类就能实现工厂模式,从而实现一个方便的点菜系统,有兴趣的读者可以自己实现以下。