模板模式(Java实现)
模板方法模式让我们能够用一套模板来统一描述步骤相似的操作,这个模式简称为模板模式,它很早就出现在了计算机领域,而且是经典的 GoF 设计模式之一。
我们使用模板模式的主要原因在于,想把多个操作所共用的步骤提取出来。我们用这套基本的步骤定义一个算法骨架,骨架里的某些部分可以交给子类去做具体的实现。于是,我们就能在不改变算法结构的前提下,通过各种子类来完善算法中的特定部分,从而形成各种不同的具体算法。
我们可以在模板方法里把这些部分(或者说,这些步骤)之间的顺序安排好,并交由相应的小方法去执行,这样就无须担心用户会把顺序弄乱了。
代表输入流的 InputStream 类定义了几个重载的 read() 方法,其中有一个三参数版本的 read() 方法用的就是模板模式,该方法本身按照一套通用的步骤来处理输入流中的各个字节,然而它会把读取单个字节这一操作交由无参数版的 read() 方法去做,那个方法是个抽象方法,需要由 InputStream 类的子类实现。
类似的机制也出现在表示输出流的 OutputStream 类及其 write() 方法中。
另外一个用到模板模式的地方出现在 Java 集合框架里,这个框架也位于 java.base 模块的 java.util 包中。集合框架里的 AbstractList 实现了 List 接口所定义的 indexOf() 及 lastIndexOf() 方法,它在实现这两个方法时,用的也是模板模式。
例如,在实现 indexOf() 方法的过程中,它会把获取迭代器的步骤交给名为 listIterator() 方法去完成,这个负责提供迭代器的 listIterator() 方法可以由 AbstractList 的子类去覆写(这种迭代器是专门迭代列表的 ListIterator,它的功能比针对通用集合的 Iterator 更多)。
名为 VehicleSensor 的抽象类表示各类传感器所共有的逻辑,它里面定义了一个修饰成 final 的 activate() 方法,用来描述该操作的通用步骤:

图 1 用UML类图来演示如何将新的传感器类型纳入模板方法模式中
另外,模板方法模式还能让代码更便于维护,并让开发者更容易发现其中的问题。
我们使用模板模式的主要原因在于,想把多个操作所共用的步骤提取出来。我们用这套基本的步骤定义一个算法骨架,骨架里的某些部分可以交给子类去做具体的实现。于是,我们就能在不改变算法结构的前提下,通过各种子类来完善算法中的特定部分,从而形成各种不同的具体算法。
我们可以在模板方法里把这些部分(或者说,这些步骤)之间的顺序安排好,并交由相应的小方法去执行,这样就无须担心用户会把顺序弄乱了。
模板模式在JDK中的运用
Java 的 I/O API 提供了做输入与做输出的字节流,这些字节流位于 java.base 模块的 java.io 包中。代表输入流的 InputStream 类定义了几个重载的 read() 方法,其中有一个三参数版本的 read() 方法用的就是模板模式,该方法本身按照一套通用的步骤来处理输入流中的各个字节,然而它会把读取单个字节这一操作交由无参数版的 read() 方法去做,那个方法是个抽象方法,需要由 InputStream 类的子类实现。
类似的机制也出现在表示输出流的 OutputStream 类及其 write() 方法中。
另外一个用到模板模式的地方出现在 Java 集合框架里,这个框架也位于 java.base 模块的 java.util 包中。集合框架里的 AbstractList 实现了 List 接口所定义的 indexOf() 及 lastIndexOf() 方法,它在实现这两个方法时,用的也是模板模式。
例如,在实现 indexOf() 方法的过程中,它会把获取迭代器的步骤交给名为 listIterator() 方法去完成,这个负责提供迭代器的 listIterator() 方法可以由 AbstractList 的子类去覆写(这种迭代器是专门迭代列表的 ListIterator,它的功能比针对通用集合的 Iterator 更多)。
模板模式实例演示
现在举例说明如何采用模板方法模式来方便地定义各种新的传感器。public static void main(String[] args) { System.out.println("Template method Pattern, changing transport options"); Arrays.asList(new BreaksSensor(), new EngineSensor()) .forEach(VehicleSensor::activate); }程序输出结果如下:
Template method Pattern, changing transport options
BreaksSensor, initiated
BreaksSensor, measurement started
BreaksSensor, data stored
BreaksSensor, measurement stopped
EngineSensor, initiated
EngineSensor, measurement started
EngineSensor, data stored
EngineSensor, measurement stopped
名为 VehicleSensor 的抽象类表示各类传感器所共有的逻辑,它里面定义了一个修饰成 final 的 activate() 方法,用来描述该操作的通用步骤:
abstract sealed class VehicleSensor permits BreaksSensor, EngineSensor { abstract void init(); abstract void startMeasure(); abstract void storeData(); abstract void stopMeasure(); final void activate(){ init(); startMeasure(); storeData(); stopMeasure(); } }这种模板方法让各种具体的传感器都能以各自的方式实现其中的小步骤,与此同时,也让模板的设计者能够自行扩充模板方法本身,而不致影响到其他代码。

图 1 用UML类图来演示如何将新的传感器类型纳入模板方法模式中
总结
模板方法模式很适合用来描述某一套具备通用步骤的操作,它可以将这些操作的内部逻辑与使用这套操作的用户代码相区隔,使得用户能够方便地执行操作,而无须关注该操作的详细步骤。另外,模板方法模式还能让代码更便于维护,并让开发者更容易发现其中的问题。