文章目录
- 意图
- 什么时候使用模板方法
- 真实世界类比
- 常说的钩子
- 模板方法的实现
- 模板方法模式的优缺点
亦称: Template Method
意图
它的主要思想是,定义一个操作的一系列步骤,对于某些暂时确定不下来的步骤,就留给子类去实现好了,这样不同的子类就可以定义出不同的步骤。因此,模板方法的核心在于定义一个“骨架”。模板方法模式很好的体现了好莱坞原则(Hollywood Principle): Don’t call us, we’ll call you。使得父类来控制程序流程,父类根据程序执行需要将子类的功能勾到父类中,程序流程倒置这个点需要花一点时间适应。
什么时候使用模板方法
- 算法的整体步骤很固定,但是其中一些方法需要根据场景不同,进行不同的实现,我们就可以将易变的抽象出来,交由子类实现。
- 当多个子类存在公共的行为时,可以将其提取出来并集中到一个公共父类中以避免代码重复。
- 当需要控制子类的扩展部分是否执行时,可以在特定的方法前调用钩子操作,这样就增强了模板方法的灵活性。
真实世界类比
可对典型的建筑方案进行微调以更好地满足客户需求。
模板方法可用于建造大量房屋。 标准房屋建造方案中可提供几个扩展点, 允许潜在房屋业主调整成品房屋的部分细节。
每个建造步骤 (例如打地基、 建造框架、 建造墙壁和安装水电管线等) 都能进行微调, 这使得成品房屋会略有不同。
常说的钩子
钩子 相信大家曾经有听到过,但是可能一直不太理解,它是一种被声明在抽象类中的方法,但只有空的或者默认的实现。 钩子的存在,可以让子类有能力对算法的不同点进行挂钩。要不要挂钩由子类自行决定。
它和模板方法是搭配着使用的,假设现在要加入一个新的方法调用,改变了模板方法。但是这个方法又不是所有的子类都需要实现,希望将这个选择的能力交给子类选择,就可以将这个方法在超类中定义为空方法或者默认实现,子类可以选择覆盖,但不一定非要这么做,这就是钩子的作用。
模板方法的实现
1、抽象类 (AbstractClass) 会声明作为算法步骤的方法, 以及依次调用它们的实际模板方法。 算法步骤可以被声明为 抽象
类型, 也可以提供一些默认实现。
2、具体类 (ConcreteClass) 可以重写所有步骤, 但不能重写模板方法自身。
class AbstractClass
{
/**
* The template method defines the skeleton of an algorithm.
*/
public:
void TemplateMethod() const {
this->BaseOperation1();
this->RequiredOperations1();
this->BaseOperation2();
this->Hook1();
this->RequiredOperation2();
this->BaseOperation3();
this->Hook2();
}
/**
* These operations already have implementations.
*/
protected:
void BaseOperation1() const {
std::cout << "AbstractClass says: I am doing the bulk of the work\n";
}
void BaseOperation2() const {
std::cout << "AbstractClass says: But I let subclasses override some operations\n";
}
void BaseOperation3() const {
std::cout << "AbstractClass says: But I am doing the bulk of the work anyway\n";
}
/**
* These operations have to be implemented in subclasses.
*/
virtual void RequiredOperations1() const = 0;
virtual void RequiredOperation2() const = 0;
/**
* These are "hooks." Subclasses may override them, but it's not mandatory
* since the hooks already have default (but empty) implementation. Hooks
* provide additional extension points in some crucial places of the
* algorithm.
*/
virtual void Hook1() const {}
virtual void Hook2() const {}
};
/**
* Concrete classes have to implement all abstract operations of the base class.
* They can also override some operations with a default implementation.
*/
class ConcreteClass1 : public AbstractClass
{
protected:
void RequiredOperations1() const override {
std::cout << "ConcreteClass1 says: Implemented Operation1\n";
}
void RequiredOperation2() const override {
std::cout << "ConcreteClass1 says: Implemented Operation2\n";
}
};
/**
* Usually, concrete classes override only a fraction of base class' operations.
*/
class ConcreteClass2 : public AbstractClass
{
protected:
void RequiredOperations1() const override {
std::cout << "ConcreteClass2 says: Implemented Operation1\n";
}
void RequiredOperation2() const override {
std::cout << "ConcreteClass2 says: Implemented Operation2\n";
}
void Hook1() const override {
std::cout << "ConcreteClass2 says: Overridden Hook1\n";
}
};
模板方法模式的优缺点
优点 | 缺点 |
---|---|
很好的符合的“开闭原则” | 部分客户端可能会受到算法框架的限制 |
提高了代码的复用度 | 通过子类抑制默认步骤实现可能会导致违反_里氏替换原则 |
模板方法中的步骤越多, 其维护工作就可能会越困难 |