👈️上一篇:结构型设计模式对比
文章目录
- 模板方法模式
- 定义
- 英文原话
- 直译
- 如何理解呢?
- 2个角色
- 类图
- 代码示例
- 应用
- 优点
- 缺点
- 使用场景
- 示例解析:以烹饪过程为例
- 类图
- 代码示例
模板方法模式
模板方法模式(Template Method Pattern)是一种行为型设计模式,它通过一个抽象类定义了一个操作的算法骨架,而将一些步骤延迟到子类中实现。
简而言之,模板方法模式就像是一个烹饪食谱,规定了基本的烹饪流程(算法骨架),但允许厨师根据具体食材(子类)调整某些步骤(如烹饪时间、温度等),从而制作出不同风味的菜肴。
定义
英文原话
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.
直译
定义一个操作中的算法框架,进而推迟一些步骤的执行,将其延迟到子类中。模板方法使得子类在不改变算法的结构的情况下,可以改变算法的某些特定步骤。
如何理解呢?
模板方法模式从字面上理解,可以拆分为“模板”和“方法”两个部分。
- 模板:指的是一种固定的框架或结构,它定义了某个过程或操作的基本流程或步骤。在模板方法模式中,这个“模板”通常由抽象类中的模板方法提供,该方法定义了一个算法的框架,即算法执行的大致步骤和顺序。
- 方法:指的是具体的操作或步骤。在模板方法模式中,这些“方法”通常包括抽象方法(由子类实现)和具体方法(在抽象类中实现)。抽象方法定义了算法中需要子类实现的部分,而具体方法则包含了算法中通用的、不需要子类改变的部分。
我们可以以一个简单的烹饪过程为例。假设我们有一个基本的烹饪流程,其中包含了预热烤箱、烹饪食物和关闭烤箱的步骤。但不同的食物需要不同的烹饪时间和温度,这部分就是可以定制的部分。
假设我们想要烤一个蛋糕和一个披萨。两者都需要预热烤箱,但烹饪时间和温度不同,烹饪完成后都需要关闭烤箱。这里,预热烤箱和关闭烤箱就是模板方法中固定的部分,而烹饪食物则是需要根据不同食物来定制的部分。(见下文示例解析)
2个角色
模板方法模式(Template Method Pattern)中的角色通常包括:
- 抽象类(Abstract Class):这个角色定义了一个或多个抽象操作,以便让子类实现。这些抽象操作是基本操作,还需要定义一个或几个模板方法,这些模板方法一般是具体的方法,定义了一个算法的框架。
- 具体子类(Concrete Subclasses):这是抽象模板角色的子类,它们实现抽象模板角色中的抽象方法,以完成算法中与特定子类相关的步骤。
通过使用模板方法模式,开发者可以在不改变算法结构的情况下,通过子类来重定义算法的某些特定步骤,从而实现算法的灵活性和可复用性。
类图
代码示例
下面是一个Java示例,展示了模板方法模式:
package com.polaris.designpattern.list3.behavioral.pattern01.templatemethod.classicdemo;// 抽象类,定义了模板方法
abstract class AbstractClass {// 模板方法,定义了算法的框架 public final void templateMethod() {specificMethod1(); // 调用第一个抽象方法 // 可能还有其他的通用操作或条件判断 specificMethod2(); // 调用第二个抽象方法 }// 抽象操作,子类必须实现 protected abstract void specificMethod1();// 另一个抽象操作,子类也必须实现 protected abstract void specificMethod2();
}// 具体子类A
class ConcreteClassA extends AbstractClass {// 实现抽象操作 @Overrideprotected void specificMethod1() {System.out.println("ConcreteClassA.specificMethod1()");}// 实现抽象操作 @Overrideprotected void specificMethod2() {System.out.println("ConcreteClassA.specificMethod2()");}
}// 具体子类B
class ConcreteClassB extends AbstractClass {// 实现抽象操作 @Overrideprotected void specificMethod1() {System.out.println("ConcreteClassB.specificMethod1()");}// 实现抽象操作 @Overrideprotected void specificMethod2() {System.out.println("ConcreteClassB.specificMethod2()");}
}// 客户端代码
public class TemplateMethodTest {public static void main(String[] args) {AbstractClass classA = new ConcreteClassA();classA.templateMethod(); // 输出 ConcreteClassA.specificMethod1() 和 ConcreteClassA.specificMethod2() AbstractClass classB = new ConcreteClassB();classB.templateMethod(); // 输出 ConcreteClassB.specificMethod1() 和 ConcreteClassB.specificMethod2() }
}
/* Output:
ConcreteClassA.specificMethod1()
ConcreteClassA.specificMethod2()
ConcreteClassB.specificMethod1()
ConcreteClassB.specificMethod2()
*///~
在这个示例中,
AbstractClass
是抽象类,它定义了一个模板方法templateMethod()
,该方法调用了两个抽象操作specificMethod1()
和specificMethod2()
。ConcreteClassA
和ConcreteClassB
是具体子类,它们分别实现了这两个抽象操作。客户端代码通过调用模板方法来执行算法,而具体的步骤(specificMethod1()
和specificMethod2()
)则由不同的子类实现。
模板方法模式(Template Method Pattern)是一种行为型设计模式,它定义了一个操作中的算法框架,而将一些步骤延迟到子类中。这使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。
应用
模板方法模式在以下场景中特别有用:
- 算法复用:当多个类有相似的行为,但部分行为需要定制时,可以使用模板方法模式。
- 框架设计:在框架设计中,模板方法模式可以帮助实现一些可扩展的、可定制的框架。
- 一次性算法:如果一个算法很少改变,但其中的某些步骤可能需要根据具体情况进行定制时,可以使用模板方法模式。
优点
- 代码复用:模板方法模式通过把不变行为搬到超类,去除了子类中的重复代码。
- 扩展性好:子类可以通过实现或重写抽象方法或钩子方法来改变或扩展算法的部分行为。
- 灵活性高:在模板方法模式中,可以通过定义抽象方法和钩子方法来实现算法的灵活性和可扩展性。
- 符合开闭原则:对扩展开放,对修改关闭。在模板方法模式中,增加新的功能可以通过增加新的子类来实现,而不需要修改现有的代码。
缺点
- 抽象层次提高:模板方法模式会增加类的抽象层次,使得子类之间的关系更加复杂。
- 可能产生过多子类:如果每个不同的行为都使用子类来实现,那么类的个数可能会急剧增加。
- 性能考虑:由于使用了继承,如果子类过多,可能会影响系统的性能。
使用场景
- 创建框架:当我们需要创建一个框架,并希望这个框架具有可扩展性和可定制性时,可以使用模板方法模式。
- 实现回调:在某些情况下,我们可能需要让子类在特定的事件发生时执行一些操作,这时可以使用模板方法模式来实现回调。
- 资源初始化:当资源的初始化或清理具有固定的流程,但某些步骤可能需要根据具体情况进行定制时,可以使用模板方法模式。
- 算法复用:当多个类有相似的行为,但部分行为需要根据具体情况进行定制时,可以使用模板方法模式来复用代码。
模板方法模式是一种非常实用的设计模式,它可以帮助我们更好地组织代码,提高代码的可维护性和可扩展性。
示例解析:以烹饪过程为例
假设我们有一个基本的烹饪流程,其中包含了预热烤箱、烹饪食物和关闭烤箱的步骤。但不同的食物需要不同的烹饪时间和温度,这部分就是可以定制的部分。
假设我们想要烤一个蛋糕和一个披萨。两者都需要预热烤箱,但烹饪时间和温度不同,烹饪完成后都需要关闭烤箱。这里,预热烤箱和关闭烤箱就是模板方法中固定的部分,而烹饪食物则是需要根据不同食物来定制的部分。
类图
代码示例
package com.polaris.designpattern.list3.behavioral.pattern01.templatemethod.cookingdemo;// 抽象类,代表烹饪流程
abstract class CookingProcess {// 模板方法,定义了烹饪的整个过程 public final void cook() {preheatOven(); // 预热烤箱 cookFood(); // 烹饪食物(需要子类实现) turnOffOven(); // 关闭烤箱 }// 预热烤箱的具体方法,不需要子类改变 protected void preheatOven() {System.out.println("Preheating oven to 350°F...");// 假设预热完成需要一些时间,这里省略等待逻辑 }// 烹饪食物的方法,需要子类实现 protected abstract void cookFood();// 关闭烤箱的具体方法,不需要子类改变 protected void turnOffOven() {System.out.println("Turning off the oven...");}
}// 蛋糕烹饪类
class CakeCooking extends CookingProcess {// 实现烹饪食物的方法,这里是烹饪蛋糕 @Overrideprotected void cookFood() {System.out.println("Baking cake for 30 minutes...");// 假设烹饪完成需要一些时间,这里省略等待逻辑 }
}// 披萨烹饪类
class PizzaCooking extends CookingProcess {// 实现烹饪食物的方法,这里是烹饪披萨 @Overrideprotected void cookFood() {System.out.println("Baking pizza for 15 minutes at 450°F...");// 假设烹饪完成需要一些时间,这里省略等待逻辑 }
}// 客户端代码
public class CookingDemo {public static void main(String[] args) {CookingProcess cakeCooking = new CakeCooking();cakeCooking.cook(); // 烹饪蛋糕 System.out.println("--------------------");CookingProcess pizzaCooking = new PizzaCooking();pizzaCooking.cook(); // 烹饪披萨 }
}
/* Output:
Preheating oven to 350°F...
Baking cake for 30 minutes...
Turning off the oven...
--------------------
Preheating oven to 350°F...
Baking pizza for 15 minutes at 450°F...
Turning off the oven...
*///~
这个例子展示了模板方法模式如何帮助我们在保持烹饪流程基本框架不变的情况下,为不同的食物定制不同的烹饪步骤。