1. 概念
模板方法模式是一种行为设计模式,它在一个方法中定义算法的骨架,将一些步骤延迟到子类中实现。
2. 原理结构图
2.1 图
2.2 角色
抽象类(Abstract Class)
定义抽象的基本操作(Primitive Operations),这些操作构成了模板方法的骨架。 实现模板方法(Template Method),模板方法是定义算法结构的方法,它调用基本操作来执行这个算法的步骤。 可以定义一些钩子(Hook),这些是抽象方法或具体方法,提供了默认实现,子类可以选择性地覆盖它们。
具体方法(Concrete Methods)
在抽象类中实现一些不变化的方法,即那些构成算法骨架的共同步骤。 通常是私有(Private)或者最终(Final)的,以确保子类不会改变这些方法。 抽象操作(Abstract Operations)
在抽象类中声明的一些抽象方法,它们代表了算法中需要由子类实现的可变部分。 通常这些抽象操作对应于算法的某些特定步骤,子类将根据具体需求来实现这些步骤。 具体子类(Concrete Class)
继承自抽象类的具体子类。 实现或覆盖抽象操作以提供具体的实现细节。 如果有需要,也可以覆盖钩子方法来影响算法的某些部分。 客户端(Client)
调用抽象类中的模板方法,通常会通过一个具体子类的实例来进行调用。
3. 代码示例
3.1 示例1–在线购物系统
假设正在开发一个在线购物系统,其中有不同的支付方式(如信用卡支付、支付宝支付等)。每种支付方式可能有一些共通的步骤,如验证支付信息、处理支付等,但也有一些特定的步骤,如与支付网关交互。
abstract class Payment {
public final void processPayment ( ) {
validatePaymentInfo ( ) ;
performPrePaymentOperations ( ) ;
handlePayment ( ) ;
performPostPaymentOperations ( ) ;
}
protected abstract void validatePaymentInfo ( ) ;
protected abstract void handlePayment ( ) ;
protected void performPrePaymentOperations ( ) {
System . out. println ( "Performing pre-payment operations (default implementation)." ) ;
}
protected void performPostPaymentOperations ( ) {
System . out. println ( "Performing post-payment operations (default implementation)." ) ;
}
}
class CreditCardPayment extends Payment {
@Override
protected void validatePaymentInfo ( ) {
System . out. println ( "Validating credit card payment info." ) ;
}
@Override
protected void handlePayment ( ) {
System . out. println ( "Handling credit card payment with payment gateway." ) ;
}
@Override
protected void performPostPaymentOperations ( ) {
System . out. println ( "Updating order status after credit card payment." ) ;
}
}
class AlipayPayment extends Payment {
@Override
protected void validatePaymentInfo ( ) {
System . out. println ( "Validating Alipay payment info." ) ;
}
@Override
protected void handlePayment ( ) {
System . out. println ( "Handling Alipay payment with Alipay gateway." ) ;
}
}
public class PaymentProcessor {
public static void main ( String [ ] args) {
Payment creditCardPayment = new CreditCardPayment ( ) ;
Payment alipayPayment = new AlipayPayment ( ) ;
creditCardPayment. processPayment ( ) ;
alipayPayment. processPayment ( ) ;
}
}
Validating credit card payment info.
Performing pre- payment operations ( default implementation) .
Handling credit card payment with payment gateway.
Updating order status after credit card payment.
Validating Alipay payment info.
Performing pre- payment operations ( default implementation) .
Handling Alipay payment with Alipay gateway.
Performing post- payment operations ( default implementation) .
在这个例子中,Payment 类定义了支付处理的算法骨架,CreditCardPayment 和 AlipayPayment 子类则提供了具体实现的细节。processPayment 方法作为模板方法,确保每种支付方式都遵循相同的处理流程,而子类则负责实现各自特有的验证和支付逻辑。同时,钩子方法 performPrePaymentOperations 和 performPostPaymentOperations 提供了额外的扩展点,允许子类在需要时插入自定义的行为。
3.2 示例2–文件处理系统
定义一个处理文件的通用流程,但具体的文件读取和写入操作可以由不同的子类来实现。
abstract class FileProcessor {
public final void processFile ( String filePath) {
System . out. println ( "Starting file processing..." ) ;
String content = readFile ( filePath) ;
String processedContent = processContent ( content) ;
writeFile ( processedContent, filePath) ;
System . out. println ( "File processing completed." ) ;
}
protected abstract String readFile ( String filePath) ;
protected abstract String processContent ( String content) ;
protected abstract void writeFile ( String content, String filePath) ;
protected void beforeProcessing ( ) {
System . out. println ( "Before processing hook method." ) ;
}
protected void afterProcessing ( ) {
System . out. println ( "After processing hook method." ) ;
}
}
class TextFileProcessor extends FileProcessor {
@Override
protected String readFile ( String filePath) {
System . out. println ( "Reading text file: " + filePath) ;
return "Content from text file" ;
}
@Override
protected String processContent ( String content) {
System . out. println ( "Processing text content..." ) ;
return "Processed text content" ;
}
@Override
protected void writeFile ( String content, String filePath) {
System . out. println ( "Writing to text file: " + filePath) ;
}
@Override
protected void beforeProcessing ( ) {
System . out. println ( "Before processing text file." ) ;
}
}
class BinaryFileProcessor extends FileProcessor {
@Override
protected String readFile ( String filePath) {
System . out. println ( "Reading binary file: " + filePath) ;
return "Content from binary file" ;
}
@Override
protected String processContent ( String content) {
System . out. println ( "Processing binary content..." ) ;
return "Processed binary content" ;
}
@Override
protected void writeFile ( String content, String filePath) {
System . out. println ( "Writing to binary file: " + filePath) ;
}
}
public class FileProcessorDemo {
public static void main ( String [ ] args) {
FileProcessor textFileProcessor = new TextFileProcessor ( ) ;
textFileProcessor. processFile ( "path/to/text/file.txt" ) ;
FileProcessor binaryFileProcessor = new BinaryFileProcessor ( ) ;
binaryFileProcessor. processFile ( "path/to/binary/file.bin" ) ;
}
}
Starting file processing. . .
Reading text file: path/ to / text/ file. txt
Processing text content. . .
Writing to text file: path/ to / text/ file. txt
File processing completed.
Starting file processing. . .
Reading binary file: path/ to / binary/ file. bin
Processing binary content. . .
Writing to binary file: path/ to / binary/ file. bin
File processing completed.
在这个例子中,FileProcessor 是一个抽象类,它定义了一个 processFile 模板方法,该方法按照预定的顺序调用了三个抽象方法:readFile、processContent 和 writeFile。这些抽象方法必须由子类提供具体的实现。 TextFileProcessor 和 BinaryFileProcessor 是 FileProcessor 的具体子类,分别用于处理文本文件和二进制文件。每个子类都提供了 readFile、processContent 和 writeFile 方法的具体实现,以符合它们各自处理文件类型的特定需求。 FileProcessorDemo 类是主类,用于演示如何使用这两个具体的文件处理器。通过创建 TextFileProcessor 和 BinaryFileProcessor 的实例,并调用它们的 processFile 方法,可以执行文件处理流程。
4. 优缺点
主要作用
定义一个操作中的算法骨架,并将一些步骤延迟到子类中实现。 优点
代码复用 :通过定义稳定的操作序列,减少了重复代码的编写,提高了代码复用性。结构清晰 :将不变的部分和可变的部分分离,使得操作流程更加清晰和易于理解。扩展性好 :子类可以通过实现抽象方法来定制特定步骤,从而在不修改原有结构的情况下进行扩展。易于维护 :由于操作流程被统一管理,当需要修改流程时,只需在抽象类或模板方法中修改,减少了维护工作量。 缺点
抽象类的数量增加 :为了使用模板方法,可能需要创建新的抽象类。代码的复杂性 :对于初学者而言,理解模板方法的继承和多态特性可能较为复杂。子类的依赖性增加 :子类需要依赖父类的具体实现,这可能导致子类与父类之间的耦合度增加。
5. 应用场景
5.1 主要包括以下几个方面
有固定处理步骤的算法 :当一个复杂的算法或流程中的某些步骤在各个子类中实现相同,而某些步骤需要根据具体子类实现不同功能时,可以使用模板方法模式。代码复用和扩展性要求高的场景 :如果希望在不改变算法结构的前提下,通过继承机制来扩展程序的功能,模板方法模式可以实现这一点。有多个子类共享相似处理过程的情况 :当多个子类具有类似的处理流程,但部分处理步骤在各子类间有所差异时,模板方法可以将这些步骤抽象化,并在抽象类中定义共同的处理过程。插件式系统 :当希望系统能够动态地加载和卸载功能模块时,模板方法模式允许将新功能的实现作为插件插入到系统中,而不需要修改现有代码。框架开发 :在设计软件框架时,模板方法模式可以用来定义框架的基础结构和默认行为,同时允许用户通过继承机制定制特定行为。
5.2 实际应用
文件读取和处理 :当需要从不同类型的文件中读取数据并进行处理时,可以使用模板方法模式。首先定义一个抽象类,其中包含读取文件、解析数据和处理数据的步骤。然后,为每种文件类型创建一个子类,实现具体的读取和解析方法。这样,可以确保处理流程的一致性,同时根据不同的文件类型提供特定的实现。图形绘制 :在图形绘制系统中,可以使用模板方法模式来定义绘制不同形状的算法骨架。首先定义一个抽象类,其中包含绘制形状的步骤,如设置画笔颜色、绘制边框等。然后,为每种形状创建一个子类,实现具体的绘制方法。这样,可以确保绘制流程的一致性,同时根据不同的形状提供特定的实现。网络请求处理 :在网络应用程序中,可以使用模板方法模式来处理不同类型的网络请求。首先定义一个抽象类,其中包含发送请求、接收响应和处理结果的步骤。然后,为每种请求类型创建一个子类,实现具体的发送和接收方法。这样,可以确保请求处理流程的一致性,同时根据不同的请求类型提供特定的实现。游戏开发 :在游戏开发中,可以使用模板方法模式来定义游戏的主循环。首先定义一个抽象类,其中包含更新游戏状态、渲染画面和处理用户输入的步骤。然后,为每种游戏类型创建一个子类,实现具体的更新和渲染方法。这样,可以确保游戏循环的一致性,同时根据不同的游戏类型提供特定的实现。
6. JDK中的使用
集合框架 :在java.util.Collections类中,例如sort()方法就是一个模板方法。它定义了排序的算法骨架,具体的比较逻辑则由实现Comparator接口的子类来完成。I/O流 :java.io.InputStream和java.io.OutputStream等抽象类中使用了模板方法模式。它们定义了读取和写入数据的基本步骤,而具体如何读取和写入则由子类如FileInputStream和FileOutputStream来实现。JUnit测试框架 :在JUnit中,TestCase类使用了模板方法模式。setUp()和tearDown()方法定义了测试用例执行前后的准备工作和清理工作,而具体的测试逻辑则在继承自TestCase的子类中实现。
7. 注意事项
合理使用 :模板方法模式适用于有固定算法结构,但某些步骤需要在不同子类中具体实现的情况。不是所有的场景都需要使用模板方法模式,只有在多个子类中需要重复实现一些通用方法或算法时,才考虑使用它。区分钩子方法 :模板方法模式中的钩子方法是一种可选的扩展点,它们可以为子类提供覆盖超类中某些步骤的机会。正确理解和使用钩子方法与模板方法的关系,可以帮助更好地实现算法的可定制性。避免滥用 :尽管模板方法模式可以提高系统的扩展性,但过度使用可能导致代码复杂性和维护难度增加。因此,应当在确实需要时才使用模板方法模式。关注子类实现 :在使用模板方法模式时,需要确保子类正确地实现了所有抽象方法,否则可能导致运行时错误或逻辑不一致。
8. 模板方法模式 VS 建造者模式
模式 类型 目的 模式架构主要角色 应用场景 模板方法模式 行为型 定义一个操作中的算法骨架,并将一些步骤延迟到子类中实现。 抽象类和具体方法类 适用于有固定处理步骤的算法,但某些步骤需要在不同的子类中实现时使用。 建造者模式 创建型 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 Builder、ConcreteBuilder、Director 和 Product 适用于创建复杂对象