0%

设计模式之模板模式(Template)

一、动机

模板方法模式是一种行为设计模式,它在超类中定义了一个算法的框架,允许子类在不修改结构的情况下重写算法的特定步骤。

对于某一项任务,常常有稳定的整体操作结构,但是各个子步骤却有很多改变的需求如何确定稳定操作的前提下,来灵活应对各个子步骤的变化或者晚期实现的需求?

二、解决方案

Template Method是一种非常基础的设计模式,它用最简洁的机制(虚函数的多态性)为很多应用框架提供了灵活的扩展点,是代码复用方法的基本实现结构。

模板方法模式建议将算法分解为一系列步骤,然后将这些步骤改写为方法,最后在 “模板方法” 中依次调用这些方法。步骤可以是抽象的(但一般推荐设置为protected方法,主要是因为这种方法一般不作为接口,而是作为流程中的某一个实现步骤),也可以有一些默认的实现。

为了能够使用算法,客户端需要自行提供子类并实现所有的抽象步骤。如有必要还需重写一些步骤 (但这一步中不包括模板方法自身)。

三、模板模式的结构

structure_of_Template

  1. 抽象类Abstract­Class)会声明作为算法步骤的方法,以及依次调用它们的实际模板方法。算法步骤可以被声明为抽象类型,也可以提供一些默认实现。
  2. 具体类Concrete­Class)可以重写所有步骤,但不能重写模板方法自身。
四、Template代码示例
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <iostream>

// 抽象类,声明作为算法步骤的方法,以及依次调用它们的实际模板方法。
// 其中,算法步骤可以被声明为抽象类型,也可以给出一些默认的实现。
class AbstractClass {
public:
AbstractClass() {}
virtual ~AbstractClass() {}

// 模板方法
void TemplateMethod() {
BaseOperation1();
RequiredOperations1();
BaseOperation2();
Hook1();
RequiredOperation2();
BaseOperation3();
Hook2();
}

protected:
// 默认实现部分步骤方法
void BaseOperation1() {
std::cout << "AbstractClass says: I am doing the bulk of the work\n";
}
void BaseOperation2() {
std::cout << "AbstractClass says: But I let subclasses override some operations\n";
}
void BaseOperation3() {
std::cout << "AbstractClass says: But I am doing the bulk of the work anyway\n";
}

// 将需要由具体类实现的步骤声明为虚函数
// 变化部分
virtual void RequiredOperations1() = 0;
virtual void RequiredOperation2() = 0;

// 这些方法不是强制的要求具体类实现,因为他们已经被定义了(虽然并不做任何动作)
// 它们可以为算法模板提供一些可供具体类修改的扩展点
virtual void Hook1() {}
virtual void Hook2() {}
};

// 具体类需要实现抽象类的所有抽象方法
// 也可以覆盖一些已经默认实现的步骤方法
class ConcreteClass1 : public AbstractClass {
protected:
void RequiredOperations1() override {
std::cout << "ConcreteClass1 says: Implemented Operation1\n";
}
void RequiredOperation2() override {
std::cout << "ConcreteClass1 says: Implemented Operation2\n";
}
};

class ConcreteClass2 : public AbstractClass {
protected:
void RequiredOperations1() override {
std::cout << "ConcreteClass2 says: Implemented Operation1\n";
}
void RequiredOperation2() override {
std::cout << "ConcreteClass2 says: Implemented Operation2\n";
}
void Hook1() override {
std::cout << "ConcreteClass2 says: Overridden Hook1\n";
}
};

void ClientCode(AbstractClass *class_) {
// ...
class_->TemplateMethod();
// ...
}

int main() {
std::cout << "Same client code can work with different subclasses:\n";
ConcreteClass1 *concreteClass1 = new ConcreteClass1;
ClientCode(concreteClass1);
std::cout << "\n";
std::cout << "Same client code can work with different subclasses:\n";
ConcreteClass2 *concreteClass2 = new ConcreteClass2;
ClientCode(concreteClass2);

delete concreteClass1;
delete concreteClass2;
return 0;
}

Execution result:
Same client code can work with different subclasses:
AbstractClass says: I am doing the bulk of the work
ConcreteClass1 says: Implemented Operation1
AbstractClass says: But I let subclasses override some operations
ConcreteClass1 says: Implemented Operation2
AbstractClass says: But I am doing the bulk of the work anyway

Same client code can work with different subclasses:
AbstractClass says: I am doing the bulk of the work
ConcreteClass2 says: Implemented Operation1
AbstractClass says: But I let subclasses override some operations
ConcreteClass2 says: Overridden Hook1
ConcreteClass2 says: Implemented Operation2
AbstractClass says: But I am doing the bulk of the work anyway
五、使用场景
  • 当你只希望客户端扩展某个特定算法步骤,而不是整个算法或其结构时,可使用模板方法模式。

模板方法将整个算法转换为一系列独立的步骤,以便子类能对其进行扩展,同时还可让超类中所定义的结构保持完整。

  • 当多个类的算法除一些细微不同之外几乎完全一样时,你可使用该模式。但其后果就是,只要算法发生变化,你就可能需要修改所有的类。

在将算法转换为模板方法时,你可将相似的实现步骤提取到超类中以去除重复代码。子类间各不同的代码可继续保留在子类中。

六、优缺点
  • 你可仅允许客户端重写一个大型算法中的特定部分,使得算法其他部分修改对其所造成的影响减小。
  • 你可将重复代码提取到一个超类中。
  • 部分客户端可能会受到算法框架的限制。
  • 通过子类抑制默认步骤实现可能会导致违反里氏替换原则。
  • 模板方法中的步骤越多,其维护工作就可能会越困难。
七、与其它模式的关系
  • 工厂方法模式是模板方法模式的一种特殊形式。同时,工厂方法可以作为一个大型模板方法中的一个步骤。
  • 模板方法基于继承机制:它允许你通过扩展子类中的部分内容来改变部分算法。策略模式基于组合机制:你可以通过对相应行为提供不同的策略来改变对象的部分行为。模板方法在类层次上运作,因此它是静态的。策略在对象层次上运作,因此允许在运行时切换行为。