定义:
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.(定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改 变一个算法的结构即可重定义该算法的某些特定步骤。)
模板方法模式通用类图
模板方法设计模式的核心思想是“定义算法骨架,将可变部分延迟到子类实现”。它将算法中不变的部分封装在抽象类的模板方法中,而将可变的部分抽象成抽象方法或钩子方法,由子类具体实现。这样可以避免代码重复,同时允许子类在不改变算法整体结构的情况下,对某些步骤进行定制化实现。
角色:
模板方法模式包含以下几个核心角色:
1、抽象类(Abstract Class)
抽象类定义了一个模板方法,该方法包含了算法的骨架,具体步骤由一系列方法调用组成。这些方法可以是抽象方法、具体方法或钩子方法:
- 抽象方法:由抽象类声明,具体实现由子类完成。
- 具体方法:在抽象类中已经实现,子类可以直接使用或重写。
- 钩子方法:在抽象类中提供默认实现,子类可以选择重写或不重写。钩子方法通常用于控制算法的流程。
2、具体子类(Concrete Class)
具体子类继承自抽象类,实现抽象类中的抽象方法,必要时重写钩子方法,以实现特定的业务逻辑。具体子类不改变算法的结构,只负责实现算法中的某些步骤。
代码示例:
咖啡和茶的制作过程有相似的步骤:烧水、冲泡、倒入杯中、添加调料。使用模板方法设计模式实现这两种饮品的制作过程。
// 抽象类:饮品制作
public abstract class Beverage {
// 模板方法:定义制作饮品的算法骨架
public final void prepareRecipe() {
boilWater();
brew();
pourInCup();
if (customerWantsCondiments()) {
addCondiments();
}
}
// 具体方法:烧水
protected final void boilWater() {
System.out.println("烧开水");
}
// 抽象方法:冲泡
protected abstract void brew();
// 具体方法:倒入杯中
protected final void pourInCup() {
System.out.println("倒入杯中");
}
// 抽象方法:添加调料
protected abstract void addCondiments();
// 钩子方法:是否添加调料,默认添加
protected boolean customerWantsCondiments() {
return true;
}
}
// 具体子类:咖啡
public class Coffee extends Beverage {
@Override
protected void brew() {
System.out.println("冲泡咖啡粉");
}
@Override
protected void addCondiments() {
System.out.println("添加糖和牛奶");
}
}
// 具体子类:茶
public class Tea extends Beverage {
@Override
protected void brew() {
System.out.println("浸泡茶叶");
}
@Override
protected void addCondiments() {
System.out.println("添加柠檬");
}
// 重写钩子方法,自定义是否添加调料
@Override
protected boolean customerWantsCondiments() {
// 模拟用户选择
return Math.random() > 0.5;
}
}
// 客户端代码
public class BeverageClient {
public static void main(String[] args) {
System.out.println("制作咖啡...");
Beverage coffee = new Coffee();
coffee.prepareRecipe();
System.out.println("\n制作茶...");
Beverage tea = new Tea();
tea.prepareRecipe();
}
}
优点 :
1.提高代码复用性:将通用的算法结构放在抽象类中,避免了在多个子类中重复实现相同的代码,提高了代码的复用性。
2.简化子类实现:子类只需实现抽象类中定义的抽象方法和需要重写的钩子方法,而不需要关心算法的整体结构,简化了子类的实现。
3.便于控制算法流程:模板方法模式将算法的控制权集中在抽象类中,子类只能在允许的范围内进行扩展,便于控制算法的流程和行为。
4.符合开闭原则:当需要修改或扩展算法的某些步骤时,只需创建新的子类,而不需要修改抽象类的代码,符合开闭原则。
缺点:
1.限制子类灵活性:模板方法模式通过继承实现代码复用,子类必须遵循抽象类定义的算法结构,可能会限制子类的灵活性。
2.增加类的数量:随着算法步骤的增加,抽象类和子类的数量可能会增多,导致系统的类层次结构变得复杂,增加了代码的理解和维护难度。
3.父类与子类耦合度较高:模板方法模式中,抽象类定义了算法的骨架,子类依赖于抽象类的实现。如果抽象类发生变化,可能会影响到所有的子类,导致父类与子类之间的耦合度较高。
使用场景:
(一)多个算法有相似结构的场景
当多个算法或操作具有相似的结构,但某些步骤的实现方式不同时,可以使用模板方法设计模式。将通用的算法结构放在抽象类中,而将不同的步骤实现延迟到子类中,避免代码重复。
(二)需要控制子类扩展的场景
模板方法模式允许在不改变算法整体结构的情况下,通过子类来扩展或修改算法的某些步骤。抽象类可以定义哪些步骤是必须实现的(抽象方法),哪些步骤是可以选择性实现的(钩子方法),从而控制子类的扩展方式。
(三)希望提高代码复用性的场景
模板方法设计模式通过继承实现代码复用,将通用的代码放在抽象类中,而将特殊的代码放在子类中。这样可以减少代码冗余,提高代码的复用性和可维护性。
(四)框架设计场景
在框架设计中,模板方法模式经常被用来定义框架的核心流程,而将一些具体的实现细节留给开发者。例如,Java 的 Servlet API 中,HttpServlet 类就使用了模板方法模式,定义了处理 HTTP 请求的基本流程,开发者只需重写 doGet()、doPost() 等方法即可。
模板方法设计模式通过定义算法的骨架,将可变部分延迟到子类实现,为软件开发提供了一种优雅的代码复用和扩展方式。它在多个领域都有广泛的应用,如游戏开发中的角色行为模式、软件开发中的框架设计等。合理运用模板方法设计模式,可以使代码更加简洁、灵活和易于维护。然而,在使用时也需要注意其缺点,避免过度使用导致类层次结构复杂。通过权衡利弊,我们可以在适当的场景下使用模板方法模式,发挥其最大的优势。