2018-09-21 18:42:07
模板模式
模板(Template)模式,定义一个操作中算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
模板模式UML类图
AbstractClass:一个抽象模板,定义抽象的原语操作(PrimitiveOperation),具体的子类将重定义它们,以便有针对性的实现算法的各个步骤。另外,它还将实现一个模板方法(TemplateMethod),这是一个具体方法,它将给出一个顶级的逻辑框架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现,顶级逻辑也有可能调用定义在AbstractClass类或者其它地方的具体方法。
ConcreteClass:实现父类所定义的一个或多个抽象方法,每一个AbstractClass都可以有任意多个ConcreteClass与之对应,而每一个ConcreteClass都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同。
模板方法的特点
模板模式通过把整个继承体系中不变的地方移动到基类,变化的部分以虚函数的形式给出,将实现延迟到子类,这样就达到了在子类中去除重复代码(那些所有子类都一样的代码)的目的。通过模板方法能很好的达到代码复用的目的,那么客户代码在使用这个模板的时候,只需要通过ConcreteClass的实例来调用TemplateMethod就可以实现多样化的计算结果了。
模板方法适用场景
1.当遇到一个由一系列步骤构成的过程需要执行,但这个过程从高层上看是相同的,但有些步骤的实现可能不同,而有一些是相同的(把这些相同的方法设计为模板方法或者其它父类的具体方法)。这个时候就可以考虑使用模板模式了。
2.通过子类来决定算法中的某个步骤是否需要执行,达到通过子类对父类进行反向控制的目的。
优点
1.在父类中形式化的定义一个算法(TemplateMethod),而由其子类实现具体的细节,在子类中实现算法的具体处理细节时不会影响算法的操作次序(这是次序是在TemplateMethod中定义好的)
2.这是一种代码复用技术,在设计类库时,可以把整个类库的公共部分抽离到父类中,通过子类来实现差异化的行为。
3.可以实现子类对父类的反向控制(不太懂),通过覆盖父类的钩子方法来决定某一特定步骤是否需要执行。
4.在模板方法模式中,可以通过子类来覆盖父类的基本方法,不同的子类可以提供这些基本方法的不同实现。更换和增加新的子类是符合开放封闭原则的。
缺点:
每个不同的实现都需要提供一个子类,如果变化太多会造成子类数量过多。看下代码示例的客户代码就很明了这句话的意思了
代码示例
人早晨起床的流程:要先睁眼(这个每个人都是固定的方式,至于有人微睁,有人半睁就不要纠结了。。。)、起床、洗漱、(睁眼、起床、洗漱每个人都一样)吃饭(但吃饭,每个人的早餐都是不同的)。
1.AbstractClass
#ifndef ABSTRACTCLASS_H_ #define ABSTRACTCLASS_H_ #include <iostream> #include <string> class AbstractClass { public: void openEyes() { std::cout << "I opened my eyes!" << std::endl; } void getUp() { std::cout << "I got up!" << std::endl; } virtual void wash() { std::cout << "I had washed." << std::endl; } virtual void eatBreakfast() = 0; virtual void templateMethod() { openEyes(); getUp(); wash(); eatBreakfast(); } AbstractClass() = default; virtual ~AbstractClass() = default; }; #endif
2.ConcreteClass
#ifndef CONCRETECLASS_H_ #define CONCRETECLASS_H_ #include "AbstractClass.h" #include <string> class ConcreteClass:public AbstractClass { public: void eatBreakfast(); ConcreteClass() = default; ~ConcreteClass() = default; private: }; #endif #include "ConcreteClass.h" void ConcreteClass::eatBreakfast() { std::cout << "default eat eggs" << std::endl; } #ifndef CONCRETECLASSYANGYANG_H_ #define CONCRETECLASSYANGYANG_H_ #include "AbstractClass.h" class ConcreteClassYangyang : public AbstractClass { public: void eatBreakfast() override; ConcreteClassYangyang() = default; ~ConcreteClassYangyang() = default; }; #endif #include "ConcreteClassYangYang.h" void ConcreteClassYangyang::eatBreakfast() { std::cout << "Yang yang ate noodles." <<std::endl; }
3.Client
#include "ConcreteClass.h" #include "ConcreteClassYangYang.h" using namespace std; int main(int argc,char *argv[]) { AbstractClass *obj1 = new ConcreteClass; obj1->templateMethod(); if(nullptr != obj1) { delete obj1; obj1 = nullptr; } AbstractClass *obj2 = new ConcreteClassYangyang; obj2->templateMethod(); if(nullptr != obj2) { delete obj2; obj2 = nullptr; } return (1); }