C++Template(模板方法)模式-行为型模式

模板方法属于行为型设计模式,行为型设计模式主要关注对象之间职责分配和算法的问题。类行为型模式使用继承来分配类之间的职责,模板方法就是个类行为型模式。对象行为型模式使用组合来分配职责。在我们构建软件的过程中大部分时候我们都是在思考实体之间的职责,怎样的职责分配最合理,不至于过重,又不至于过轻,而且又不越权。在面向对象系统的分析与设计过程中经常会遇到这样一种情况:对于某一个业务逻辑 (算法实现)在不同的对象中有不同的细节实现,但是逻辑(算法)的框架(或通用的应用 算法)是相同的。Template 提供了这种情况的一个实现框架。 Template 模式是采用继承的方式实现这一点:将逻辑(算法)框架放在抽象基类中,并 定义好细节的接口,子类中实现细节。【注释 1】

        【注释 1】:Strategy 模式解决的是和 Template 模式类似的问题,但是 Strategy 模式是将逻辑 算法)封装到一个类中,并采取组合(委托)的方式解决这个问题。

一、什么是模板方法模式

模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤的实现延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中某些步骤的具体实现。

看到“设计模式”这四个字我们往往会觉得高深莫测,但是模板方法模式却是一个例外,你要关注的就是一个方法而已

模板方法模式确实非常简单,仅仅使用继承机制,但是它是一个应用非常广泛的模式。

二、模板方法模式的使用场景

当系统中算法的骨架是固定的时候,而算法的实现可能有很多种的时候,就需要使用模板方法模式。

  • 多个子类有共有的方法,并且逻辑基本相同
  • 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现
  • 重构时,模板方法是一个经常使用的方法,把相同的代码抽取到父类中,然后通过构造函数约束其行为。

举例:需要做一个报表打印程序,用户规定需要表头,正文,表尾。但是客户的需求会变化,一会希望这样显示表头,一会希望那样显示。 
这时候采用模板方式就合适。

三、模板方法模式的优缺点

优点:

  • 封装不变部分,扩展可变部分。把认为不变部分的算法封装到父类中实现,而可变部分的则可以通过继承来继续扩展。
  • 提取公共部分代码,便于维护。
  • 行为由父类控制,子类实现

缺点:

算法骨架需要改变时需要修改抽象类。

按设照计习惯,抽象类负责声明最抽象、最一般的事物属性和方法,实现类负责完成具体的事务属性和方法,但是模板方式正好相反,子类执行的结果影响了父类的结果,会增加代码阅读的难度。

四、模板方法模式的实现

AbstractClass类---抽象模板类,定义并实现了一个模板方法。
这个模板一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。
顶级逻辑也有可以调用具体的方法

#pragma once
//Template.h 
class AbstractClass 
{ 
public: 
	virtual ~AbstractClass(); 
	void TemplateMethod(); 
protected: 
	virtual void PrimitiveOperation1() = 0; 
	virtual void PrimitiveOperation2() = 0; 
	AbstractClass(); 
private: 
}; 
class ConcreteClass1:public AbstractClass 
{ 
public:
	ConcreteClass1(); 
	~ConcreteClass1(); 
protected: 
	void PrimitiveOperation1(); 
	void PrimitiveOperation2(); 
private: 
}; 
class ConcreteClass2:public AbstractClass 
{ 
public: 
	ConcreteClass2(); 
	~ConcreteClass2(); 
protected: 
	void PrimitiveOperation1(); 
	void PrimitiveOperation2(); 
private: 
};
 
#include "stdafx.h"
#include "Template.h" 
#include <iostream>
 
using namespace std; 
AbstractClass::AbstractClass() 
{ 
} 
AbstractClass::~AbstractClass() 
{ 
} 
void AbstractClass::TemplateMethod() 
{ 
	this->PrimitiveOperation1(); 
	this->PrimitiveOperation2(); 
} 
ConcreteClass1::ConcreteClass1() 
{ 
} 
ConcreteClass1::~ConcreteClass1() 
{ 
} 
void ConcreteClass1::PrimitiveOperation1() 
{
	cout<<"ConcreteClass1...PrimitiveOperat ion1"<<endl; 
} 
void ConcreteClass1::PrimitiveOperation2() 
{ 
	cout<<"ConcreteClass1...PrimitiveOperat ion2"<<endl; 
} 
ConcreteClass2::ConcreteClass2() 
{ 
} 
ConcreteClass2::~ConcreteClass2() 
{ 
} 
void ConcreteClass2::PrimitiveOperation1() 
{ 
	cout<<"ConcreteClass2...PrimitiveOperat ion1"<<endl; 
} 
void ConcreteClass2::PrimitiveOperation2()
{ 
	cout<<"ConcreteClass2...PrimitiveOperat ion2"<<endl; 
}
 
int main(int argc, _TCHAR* argv[])
{
	AbstractClass* p1 = new ConcreteClass1(); 
	AbstractClass* p2 = new ConcreteClass2(); 
	p1->TemplateMethod(); 
	p2->TemplateMethod(); 
	return 0;
}

 由于 Template 模式的实现代码很简单,因此解释是多余的。其关键是将通用算法(逻 辑)封装起来,而将算法细节让子类实现(多态)。

        唯一注意的是我们将原语操作(细节算法)定义未保护(Protected)成员,只供模板方 法调用(子类可以)。

      Template 模式是很简单模式,但是也应用很广的模式。如上面的分析和实现中阐明的 Template 是采用继承的方式实现算法的异构,其关键点就是将通用算法封装在抽象基类中,并将不同的算法细节放到子类中实现。

      Template 模式获得一种反向控制结构效果,这也是面向对象系统的分析和设计中一个原 则 DIP(依赖倒置:Dependency Inversion Principles)。其含义就是父类调用子类的操作(高 层模块调用低层模块的操作),低层模块实现高层模块声明的接口。这样控制权在父类(高 层模块),低层模块反而要依赖高层模块。

       继承的强制性约束关系也让 Template 模式有不足的地方,我们可以看到对于 ConcreteClass 类中的实现的原语方法 Primitive1(),是不能被别的类复用。假设我们要创建 一个 AbstractClass 的变体 AnotherAbstractClass,并且两者只是通用算法不一样,其原语操 作想复用 AbstractClass 的子类的实现。但是这是不可能实现的,因为 ConcreteClass 继承自 AbstractClass,也就继承了 AbstractClass 的通用算法,AnotherAbstractClass 是复用不了 ConcreteClass 的实现,因为后者不是继承自前者。

        Template 模式暴露的问题也正是继承所固有的问题,Strategy 模式则通过组合(委托) 来达到和 Template 模式类似的效果,其代价就是空间和时间上的代价,关于 Strategy 模式的 详细讨论请参考 Strategy 模式解析。

猜你喜欢

转载自blog.csdn.net/weixin_41882459/article/details/112687938