一、定义
定义一个操作中的框架,而将一些步骤延迟到子类中。使得子类可以不改变逻辑的情况下即可重定义该算法的某些特定步骤。
二、栗子
1、模板方法模式(通用版)
(1)UML图
(2)UML说明
AbstractClass 叫抽象模板,其方法分为两类:
基本方法:即基本操作,是由子类实现的方法,并且在模板方法被调用。
模板方法:可以有一个或几个,一般是一个具体方法,即一个框架,实现对基本方法的调度,完成固定的逻辑
注:为了防止被恶意操作,C++中可以使用两种方式
- 模板方法为虚函数,但是需要加上 final 关键字,使之在子类中不被覆写。
- 模板方法为非虚函数。
类图中的 ConcreteClass1 和 ConcreteClass2 属于具体模板,实现父类所定义的一个或多个抽象方法,也就是父类定义的基本方法在子类中得以实现。
(3)代码
注:抽象模板中的基本方法尽量设计为 protected 类型,符合迪米特法则,不需要暴露的属性或方法尽量不要设置为 protected 类型。实现类若非必要,尽量不要扩大父类中的访问权限。
#include <iostream>
class AbstractClass
{
protected:
virtual void doSomething() = 0; //基本方法
virtual void doAngthing() = 0;
public:
virtual void templateMethod() final //模板方法
{
this->doSomething();
this->doAngthing();
}
};
class ConcreteClass1 : public AbstractClass
{
protected:
virtual void doSomething() { std::cout << "ConcreteClass1 : doSomething" << std::endl; }
virtual void doAngthing() { std::cout << "ConcreteClass1 : doAngthing" << std::endl; }
};
class ConcreteClass2 : public AbstractClass
{
protected:
virtual void doSomething() { std::cout << "ConcreteClass2 : doSomething" << std::endl; }
virtual void doAngthing() { std::cout << "ConcreteClass2 : doAngthing" << std::endl; }
};
int main()
{
AbstractClass *class1 = new ConcreteClass1();
AbstractClass *class2 = new ConcreteClass2();
class1->templateMethod();
class2->templateMethod();
delete class1;
delete class2;
return 0;
}
2、汽车模型
(1)UML图
(2)UML说明
悍马车有两个型号,H1 和 H2。按需求,只需要给出悍马车模型。此处有一个抽象类,然后两个不同型号的模型实现类,通过简单继承就可以实现业务需求。
(3)结构说明
HummerModel 抽象类,在类中定义了发动、停止、鸣笛、引擎、跑几个方法。根据不同型号来进行不同的实现。
(4)代码
#include <iostream>
class HummerModel
{
protected:
virtual void start() = 0;
virtual void stop() = 0;
virtual void alarm() = 0;
virtual void enginBoom() = 0;
public:
void run()
{
this->start();
this->enginBoom();
this->alarm();
this->stop();
}
};
class HummerH1Model : public HummerModel
{
protected:
virtual void start() { std::cout << "HummerH1 : start" << std::endl; }
virtual void stop()
{
std::cout << "HummerH1 : stop" << std::endl;
}
virtual void alarm()
{
std::cout << "HummerH1 : alarm" << std::endl;
}
virtual void enginBoom()
{
std::cout << "HummerH1 : enginBoom" << std::endl;
}
};
class HummerH2Model : public HummerModel
{
protected:
virtual void start()
{
std::cout << "HummerH2 : start" << std::endl;
}
virtual void stop()
{
std::cout << "HummerH2 : stop" << std::endl;
}
virtual void alarm()
{
std::cout << "HummerH2 : alarm" << std::endl;
}
virtual void enginBoom()
{
std::cout << "HummerH2 : enginBoom" << std::endl;
}
};
int main()
{
HummerModel *h1 = new HummerH1Model();
HummerModel *h2 = new HummerH2Model();
h1->run();
h2->run();
return 0;
}
三、模板方法模式的应用
- 封装不变部分,扩展可变部分。例如,汽车模型中,增加一个H3型号的模型,增加一个子类,实现父类的基本方法就可以。
- 提取公共部分代码,便于维护。
- 行为由父类控制,子类实现。
- 多个子类有共有的方法,并且逻辑基本相同时。
- 重要、复杂的算法,可以把核心算法设计为模板方法,周边相关细节功能由各个子类实现。
- 重构时,把相同代码抽取到父类中,通过钩子函数(见“模板方法模式的扩展”)约束其行为。
四、模板方法模式的扩展
1、汽车模型
增加需求 H1的喇叭是否响可控,H2的喇叭不响。
(1)UML图
(2)UML说明
在抽象类 HummerModel 中增加了一个实现方法 isAlarm,确定各个型号的车是否需要声音,由各个实现类覆写该方法,同时其他基本方法由于不需要对外提供访问,因此也设计为 protected 类型。
#include <iostream>
class HummerModel
{
protected:
virtual void start() = 0;
virtual void stop() = 0;
virtual void alarm() = 0;
virtual void enginBoom() = 0;
virtual bool isAlarm() { return true; }
public:
void run()
{
this->start();
this->enginBoom();
if (this->isAlarm())
{
this->alarm();
}
this->stop();
}
};
class HummerH1Model : public HummerModel
{
public:
void setAlarm(bool OK) { this->m_alarmFlag = OK; }
protected:
virtual void start() { std::cout << "HummerH1 : start" << std::endl; }
virtual void stop() { std::cout << "HummerH1 : stop" << std::endl; }
virtual void alarm() { std::cout << "HummerH1 : alarm" << std::endl; }
virtual void enginBoom() { std::cout << "HummerH1 : enginBoom" << std::endl; }
virtual bool isAlarm() { return this->m_alarmFlag; }
private:
bool m_alarmFlag;
};
class HummerH2Model : public HummerModel
{
protected:
virtual void start() { std::cout << "HummerH2 : start" << std::endl; }
virtual void stop() { std::cout << "HummerH2 : stop" << std::endl; }
virtual void alarm() { std::cout << "HummerH2 : alarm" << std::endl; }
virtual void enginBoom() { std::cout << "HummerH2 : enginBoom" << std::endl; }
virtual bool isAlarm() { return false; }
};
int main()
{
HummerH1Model h1;
HummerH2Model h2;
h1.setAlarm(true);
h1.run();
h2.run();
return 0;
}
五、最佳实践
1、合适的方法
父类建立框架,子类重写了父类部分方法后,再调用从父类继承的方法,产生不同的结果,即模板方式。
2、开源框架中的使用
开源框架提供了一个抽象类,然后有一堆子类,如果需要扩展功能,可以继承这个抽象类,然后覆写 protected 方法,然后调用一个类似 execute方法,即可扩展。
(完)