读书人

她定义一个操作中的算法的骨骼而将一

发布时间: 2013-07-08 14:13:00 作者: rapoo

她定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
本帖最后由 LCL_data 于 2013-06-28 22:11:28 编辑 概述:
我们最近在开发一个支持多种压缩类型文件的解压缩且制作成pdf的一个应用。对我们的架构来说我们需要支持多种压缩文件类型,但却有固定的操作顺序(先解压缩,在读取里面的文件分析、制作pdf)。我们抽取他们的共同点:这些操作的固定顺序,把他放到我们的父类里;他们的变化点:这些个具体的操作,去留给不同的子类去实现。这个就是模板方法模式,他定义一个操作中的算法的骨架(例子中的固定的操作顺序),而将一些步骤延迟到子类中(例子中的多种压缩文件的解压缩)。
Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。是一种比较简单的设计模式,但却是代码复用的一项基本技术,在类库中尤其重要。使用的也比较普遍。

类图与实例:
请参考帖子末尾链接的博客,里面有相关类图。
这里涉及到两个角色:
抽象模版(AbstractClass)角色:
定义了一个或多个抽象操作,以便让子类实现。这些抽象操作叫做基本操作,它们是一个顶级逻辑的组成步骤。
定义并实现了一个模版方法。这个模版方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。这个就是定义了我们的固定的操作顺序。
具体模版(ConcreteClass)角色:
实现父类所定义的一个或多个抽象方法,它们是一个顶级逻辑的组成步骤。
每一个抽象模版角色都可以有任意多个具体模版角色与之对应,而每一个具体模版角色都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同。就是对我们的多个压缩文件的不同的解压缩的支持。
实例:


#include <iostream>
template <typename T> class CaffeineBeverage //咖啡因饮料
{
public:
void PrepareRecipe() //咖啡因饮料冲泡法
{
BoilWater(); //把水煮沸
Brew(); //冲泡
PourInCup(); //把咖啡因饮料倒进杯子
AddCondiments(); //加调料
}
void BoilWater()
{std::cout << "把水煮沸" << std::endl;}
void Brew()
{static_cast<T *>(this)->Brew();}
void PourInCup()
{std::cout << "把咖啡倒进杯子" << std::endl;}
void AddCondiments()
{static_cast<T *>(this)->AddCondiments();}
};
class Coffee : public CaffeineBeverage<Coffee>
{
public:
void Brew()
{std::cout << "用沸水冲泡咖啡" << std::endl;}
void AddCondiments()
{std::cout << "加糖和牛奶" << std::endl;}
};
class Tea : public CaffeineBeverage<Tea>



{
public:
void Brew()
{std::cout << "用沸水浸泡茶叶" << std::endl;}
void AddCondiments()
{std::cout << "加柠檬" << std::endl;}
};
int main(void)
{
std::cout << "冲杯咖啡:" << std::endl;
Coffee c;
c.PrepareRecipe();
std::cout << std::endl;
std::cout << "冲杯茶:" << std::endl;
Tea t;
t.PrepareRecipe();
return 0;
}


适用性:
1.一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
2.各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。这是Opdyke和Johnson所描述过的“重分解以一般化”的一个很好的例子。首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
3.控制子类扩展。模板方法只在特定点调用“Hook”操作,这样就只允许在这些点进行扩展。
实现要点:
1.Template Method模式是一种非常基础性的设计模式,在面向对象系统中有着大量的应用。它用最简洁的机制(虚函数的多态性)为很多应用程序框架提供了灵活的扩展点,是代码复用方面的基本实现结构。
2.除了可以灵活应对子步骤的变化外,“不用调用我,让我来调用你”的反向控制结构是Template Method的典型应用。
3.在具体实现方面,被Template Method调用的虚方法可以具有实现,也可以没有任何实现(抽象方法,纯虚方法),但一般推荐将它们设置为protected方法。


或者参考博客类图:http://blog.csdn.net/lcl_data/article/details/9199961
或可参考其他设计模式文章:http://blog.csdn.net/column/details/mydesignpattern.html


欢迎讨论学习。
------解决方案--------------------


template method是一种很简单的模式,是多态的最简单使用,很好理解,但也限制了它的应用范围。

template method最大的好处是派生类可以改变某一方法的实现,但又不能完全改变。可以改变那部分在virtual函数中,不能改变那部分在基类的非virtual函数中,基类和派生类各自决定自己能决定的部分,责任分得很清楚。

最极端的GOF模式信徒认为,所有的虚函数都要象数据成员一样,首先考虑为private,如果需要被派生类重用,则protected;需要被外界调用的,就template method。这个观点与我们现在常用的interface类型完全相背,哪种方法更好,实在是难说得很。

记得以前有一系列的文章“跟随大虾学习设计模式”好象是叫这个名字吧?对这个模式讲得很详细。

任何问题都能通过增加一个中间层来解决……
[解决办法]
找到了,这呢:
与大虾对话:领悟设计模式
[解决办法]

引用:
其实我看到这里

一个支持多种压缩类型文件的解压缩且制作成pdf的一个应用。对我们的架构来说我们需要支持多种压缩文件类型,但却有固定的操作顺序(先解压缩,在读取里面的文件分析、制作pdf)

首先想到的是用虚函数, 定义一个解压的接口, 不同的压缩格式来实现这个接口. 用工厂模式来判断文件的格式并创建相应的解压类对象.

模板用在实现算法的时候不依赖于类型, 很好. 要用在整个程序框架的结构上, 持保留态度, 不过貌似 ATL 的整个框架也是模板的. 主要是模板会引入大量的依赖性, 不利于程序的分层分块. 而且还有编译时间和其他人的理解难度问题.

至于是否可以叫做一个模式...
貌似现有的设计模式都是针对面向对象领域的, 是在面向对象的语言里面都能实现的.

这位好像对设计模式不太懂呢,模板方法模式与C++里的模板不是同一个东西。
[解决办法]
和奇异递归模式有些像
[解决办法]
C++ locale::facet和std::streambuf中大量使用template method.
[解决办法]
为什么不用抽象类?
[解决办法]
Mixin Style?
[解决办法]
这个确实是template method的一种实现,在C++中常用的另一种实现是动态绑定. 一动一静,各有所长.
[解决办法]
引用:
模板用在实现算法的时候不依赖于类型, 很好. 要用在整个程序框架的结构上, 持保留态度, 不过貌似 ATL 的整个框架也是模板的. 主要是模板会引入大量的依赖性, 不利于程序的分层分块. 而且还有编译时间和其他人的理解难度问题.


ATL中模板主要是为了简化COM开发,生成高效代码.ATL称为框架,可能不合适,叫library可能更好。 另一方面,com/atl真正提倡针对接口编程.COM已经很复杂了,ATL并没有COM简单不了多少,对使用者的门口过高,我认为这是ATL不能盛行的原因。

[解决办法]
template method的最大优点是简单,但基于继承的重用也另它灵活性很差。事实上Strategy会比模板的适用范围更广。

读书人网 >C++

热点推荐