模板方法模式
模式动机
模板方法模式是基于继承的代码复用基本技术,模板方法模式的结构和用法也是面向对象设计的核心之一。在模板方法模式中,可以将相同的代码放在父类中,而将不同的方法实现放在不同的子类中。
在模板方法模式中,我们需要准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来让子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现,这就是模板方法模式的用意。模板方法模式体现了面向对象的诸多重要思想,是一种使用频率较高的模式。
模式定义
模板方法模式(Template Method Pattern):定义一个操作中算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。模板方法是一种类行为型模式。(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.)
模式结构
模板方法模式包含如下角色
AbstractClass:抽象类
ConcreteClass:具体子类
模式分析
模板方法模式是一种类的行为型模式,在它的结构图中只有类之间的继承关系,没有对象关联关系。
在模板方法模式的使用过程中,要求开发抽象类和开发具体子类的设计师之间进行协作。一个设计师负责给出一个算法的轮廓和骨架,另一些设计师则负责给出这个算法的各个逻辑步骤 。实现这些具体逻辑步骤的方法称为基本方法(Primitive Method),而将这些基本法方法汇总起来的方法称为模板方法(Template Method),模板方法模式的名字从此而来。
模板方法:一个模板方法是定义在抽象类中的、把基本操作方法组合在一起形成一个总算法或一个总行为的方法。
基本方法:基本方法是实现算法各个步骤的方法,是模板方 的组成部分。
钩子方法
钩子方法是在父类中定义的一个 有默认实现的方法,允许子类通过覆盖它来干预父类算法的流程。钩子方法通常用于控制算法中的某些可选步骤。
核心作用:
控制流程:通过返回值(如 boolean)或方法逻辑,决定父类算法的某个步骤是否执行
扩展灵活性:子类可以通过覆盖钩子方法,修改算法的部分行为,而无需重写整个算法。
案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void template()
{
open();
display();
if(isPrint())
{
print();
}
}
//模板方法
public boolean isPrint()
{
return true;
}
空方法
在父类中定义的 无具体实现的方法(可能为空或抛出异常),目的是子类根据需求实现它。常见于抽象类或接口的默认方法。
空方法通常没有默认逻辑,子类必须实现(如果是抽象方法)或可选覆盖(如果是非抽象空方法)。
- 抽象方法(Abstract Method)
- 具体方法(Concrete Method)
- 钩子方法(Hook Method):“挂钩”方法和空方法
在模板方法模式中,由于面向对象的多态性,子类对象在运行时将覆盖父类对象,子类中定义的方法也将覆盖父类中定义的方法,因此程序在运行时,具体子类的基本方法将覆盖父类中定义的基本方法,子类的钩子方法也将覆盖父类的钩子方法,从而可以通过在子类中实现的钩子方法对父类方法的执行进行约束,实现子类对父类行为的反向控制。
模式优缺点
优点: 模板方法模式在一个类中抽象地定义算法,而由它的子类实现细节的处理
模板方法模式是一种代码复用的基本技术
模板方法模式导致一种反向的控制结构,通过一个父类调用其子类的操作,通过对子类的扩展增加新的行为,符合“开闭原则”。
缺点
每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,但是更加符合“单一职责原则”,使得类的内聚性得以提高。
适用环境
- 一次性实现一个算法不变的部分,并将可变的行为留给子类来实现
- 各子类中公共的行为应该被提取出来并集中感到一个公共父类中以避免代码重复
- 对一些复杂的算法进行分割,将其算法中固定不变的部分设计为模板方法和父类具体方法,而一些可以改变的细节由其子类来实现。
- 控制子类的扩展
案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
abstract class DatabaseExporter {
// 模板方法:定义数据导出流程
public final void exportData() {
connect();
queryData();
formatData(); // 空方法,子类可选实现
save();
disconnect();
}
protected abstract void queryData();
protected abstract void save();
// 空方法:子类可选择性覆盖
protected void formatData() {
// 默认空实现(或抛出 UnsupportedOperationException)
}
private void connect() {
System.out.println("连接数据库");
}
private void disconnect() {
System.out.println("断开连接");
}
}
// 子类:导出 CSV 格式数据
class CsvExporter extends DatabaseExporter {
@Override
protected void queryData() {
System.out.println("执行查询");
}
@Override
protected void save() {
System.out.println("保存为 CSV 文件");
}
}
// 子类:导出 Excel 格式数据(需要格式化)
class ExcelExporter extends DatabaseExporter {
@Override
protected void queryData() {
System.out.println("执行查询");
}
@Override
protected void save() {
System.out.println("保存为 Excel 文件");
}
// 覆盖空方法,添加格式化逻辑
@Override
protected void formatData() {
System.out.println("格式化数据为 Excel 表格");
}
}
