定义:
定义一个算法的骨架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
实现方案:将算法/逻辑框架放在抽象基类中,并定义好实现接口,在子类中实现细节接口。
注:策略模式,解决的是相同的问题,只是其方案是将各个接口封装为类,通过委托/组合方式解决问题
UML图:
AbstractClass是抽象类,其实也就是一个抽象模板,定义并实现了一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶层逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到之类实现。顶层逻辑也有可能调用一些具体方法。
ConcreteClass,实现父类所定义的一个或多个抽象方法。每一个AbstractClass都可以有任意多个ConcreteClass与之对应,而每一ConcreteClass都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同。
特点:
模板方法模式是通过把不变行为搬移到基类,去除子类中的重复代码来体现它的优势。
当不变的和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现。通过模板方法模式把这些行为搬移到单一的地方,这样就帮助子类摆脱重复的不变行为的纠缠。
#include <iostream>
#include <string>
using namespace std;
//抽象模板,并定义了一个模板方法。
class AbstractClass
{
public:
~AbstractClass(){}
//具体的模板方法,给出了逻辑的骨架,而逻辑的组成是一些相应的抽象操作,它们都推迟到子类中实现
void TemplateMethod()
{
this->PrimitiveOperation1();
this->PrimitiveOperation2();
}
//一些抽象行为,放到子类中去实现
virtual void PrimitiveOperation1()=0;
virtual void PrimitiveOperation2()=0;
protected:
AbstractClass(){}
private:
};
//实现基类所定义的抽象方法
class ConcreteClassA : public AbstractClass
{
public:
ConcreteClassA(){}
~ConcreteClassA(){}
//实现基类定义的抽象行为
virtual void PrimitiveOperation1()
{
cout << "ConcreteClassA::PrimitiveOperation1" << endl;
}
virtual void PrimitiveOperation2()
{
cout << "ConcreteClassA::PrimitiveOperation2" << endl;
}
private:
};
//实现基类所定义的抽象方法
class ConcreteClassB : public AbstractClass
{
public:
ConcreteClassB(){}
~ConcreteClassB(){}
//实现基类定义的抽象行为
virtual void PrimitiveOperation1()
{
cout << "ConcreteClassB::PrimitiveOperation1" << endl;
}
virtual void PrimitiveOperation2()
{
cout << "ConcreteClassB::PrimitiveOperation2" << endl;
}
private:
};
int main()
{
//ConcreteClassA与ConcreteClassB可相互替换
AbstractClass* pAbstract = new ConcreteClassA();
pAbstract->TemplateMethod();
pAbstract = new ConcreteClassB();
pAbstract->TemplateMethod();
return 0;
}
优点
- 提高代码复用性
将相同部分的代码放在抽象的父类中 - 提高了拓展性
将不同的代码放入不同的子类中,通过对子类的扩展增加新的行为 - 实现了反向控制
通过一个父类调用其子类的操作,通过对子类的扩展增加新的行为,实现了反向控制 & 符合“开闭原则”
缺点
引入了抽象类,每一个不同的实现都需要一个子类来实现,导致类的个数增加,从而增加了系统实现的复杂度。
应用场景
- 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现;
- 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复;
- 控制子类的扩展。
例:
假如你是一个老师,现在你要给你的学生出一份期末考试试卷。你班上有几十个学生,你将考虑如何为设计考试卷。
经分析显然学生的试卷大部分类容都是一致的,唯一不一致的是姓名和答案。老师设计好试卷,只需要把试卷交个学生填写答案即可。学生不需要把题目照抄一份。
所以我们需要把试卷抽象成基类,并且给学生留下填写答案以及姓名的地方。
#include <iostream>
#include <string>
using namespace std;
class TestPaper
{
public:
void DoTestPaper()//模板方法
{
StudentName();
TestTitleOne();
TestTitleTwo();
};
void TestTitleOne(){
cout<<"题目一:X国的房价会降下来么?"<<endl;
AnswerOne();
}
void TestTitleTwo(){
cout<<"题目二:说说你的新闻联播的看法?"<<endl;
AnswerTwo();
}
virtual void AnswerOne() = 0;//抽象行为,放到子类中实现
virtual void AnswerTwo() = 0;
virtual void StudentName() = 0;
};
//小红的试卷
class XiaoHongTestPaper : public TestPaper
{
public:
void StudentName(){
cout<<"姓名:小红"<<endl;
}
void AnswerOne(){
cout<<"答:相信X,相信国家,明年一定降下来。"<<endl<<endl;
}
void AnswerTwo(){
cout<<"答:新闻联播是我最喜欢的节目啊。"<<endl<<endl;
}
};
//xiaozhang的试卷
class XiaoZhangTestPaper : public TestPaper
{
public:
void StudentName(){
cout<<"姓名:小张"<<endl;
}
void AnswerOne(){
cout<<"答:呵呵,还是去做你的X国梦吧。"<<endl<<endl;
}
void AnswerTwo(){
cout<<"答:我很幸福"<<endl<<endl;
}
};
int main()
{
TestPaper *paper;
paper=new XiaoHongTestPaper();
paper->DoTestPaper();
paper=new XiaoZhangTestPaper();
paper->DoTestPaper();
return 0;
}