模板(Template)模式
一、问题的提出
茶和咖啡的冲泡方式非常相似:
很明显上面有重复的代码,需要清理一下设计了。把相似的部分抽取出来,放进一个基类中。//咖啡冲泡法 prepareRecipe() { boilWater();//1、把水煮沸 brewCoffeeGrinds();//2、用沸水冲泡咖啡 pourInCup();//3、把咖啡倒进杯子 addSuarAndMilk();//4、加糖和牛奶 } //茶冲泡法 prepareRecipe() { boilWater();//1、把水煮沸 steepTeaBag();//2、用沸水浸泡茶叶 pourInCup();//3、把茶倒进杯子 addLemon();//4、加柠檬 }
以上的设计中把相同的两个方法抽出来放进了基类当中,但是第二、四步是一样的算法,只是应用在不同的饮料上。于是,可以抽象的将他们换一个更合适的方法,最终的设计便是:prepareRecipe() { boilWater();//1、把水煮沸 brew();//2、用沸水冲泡 pourInCup();//3、把饮料倒进杯子 addCondiments();//4、加调料 }
二、认识模板方法
头文件:
#ifndef TEMPLATE__H #define TEMPLATE__H #include <iostream> using namespace std; //咖啡因饮料超类 class CaffeineBeverage { public: virtual void prepareRecipe() = 0;//这是模板方法,因为:它是一个算法的模板。(在这个例子中,这个算法是用来制作饮料的) //在这个模板中,算法内的每一个步骤都被一个方法代表了,某些方法是由这个超类处理的,某些方法由子类处理,需要子类处理的方法被声明为抽象 void boilWater(); virtual void brew() = 0; void pourInCup(); virtual void addCondiments() = 0; }; //茶 class Tea : public CaffeineBeverage { public: void brew(); void addCondiments(); }; //咖啡 class Coffee : public CaffeineBeverage { public: void brew(); void addCondiments(); }; #endif
cpp文件#include "Template.h" void CaffeineBeverage::boilWater() { cout<<"Boiling water"<<endl; } void CaffeineBeverage::pourInCup() { cout<<"Pouring into cup"<<endl; } //模板方法 void CaffeineBeverage::prepareRecipe() { boilWater(); brew(); pourInCup(); addCondiments(); } void Tea::brew() { cout<<"Steeping the tea"<<endl; } void Tea::addCondiments() { cout<<"Adding Lemon"<<endl; } void Coffee::brew() { cout<<"Dripping Coffee through filter"<<endl; } void Coffee::addCondiments() { cout<<"Adding Sugar and Milk"<<endl; }
定义模板方法模式:模板方法模式在一个方法中定义一个算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中某些实现步骤。
这个模式就是用来创建一个算法的模板。什么是模板?模板就是一个方法。更具体的说,这个方法将算法定义成一组步骤,其中的任何步骤都可以是抽象的,有子类负责。这可以确保算法的结构保持不变,同时由子类提供部分实现。2.1添加钩子
头文件://咖啡因饮料超类,具有钩子 class CaffeineBeverageWithHook { public: virtual void prepareRecipe() = 0;//这是模板方法,因为:它是一个算法的模板。(在这个例子中,这个算法是用来制作饮料的) //在这个模板中,算法内的每一个步骤都被一个方法代表了,某些方法是由这个超类处理的,某些方法由子类处理,需要子类处理的方法被声明为抽象 void boilWater(); virtual void brew() = 0; void pourInCup(); virtual void addCondiments() = 0; //添加钩子函数,由顾客决定是否需要添加调料 bool customerWantsCondiments(); }; class TeaWithHook : public CaffeineBeverageWithHook { public: void prepareRecipe(); void brew(); void addCondiments(); bool customerWantsCondiments(); }; //咖啡 class CoffeeWithHook : public CaffeineBeverageWithHook { public: void prepareRecipe(); void brew(); void addCondiments(); bool customerWantsCondiments(); };
cpp文件void CaffeineBeverageWithHook::boilWater() { cout<<"Boiling water"<<endl; } void CaffeineBeverageWithHook::pourInCup() { cout<<"Pouring into cup"<<endl; } bool CaffeineBeverageWithHook::customerWantsCondiments() { return true; } //模板方法 void CaffeineBeverageWithHook::prepareRecipe() { boilWater(); brew(); pourInCup(); if(customerWantsCondiments()) addCondiments(); } void TeaWithHook::brew() { cout<<"Steeping the tea"<<endl; } void TeaWithHook::addCondiments() { cout<<"Adding Lemon"<<endl; } //钩子,子类覆盖这个钩子。在此默认返回true bool TeaWithHook::customerWantsCondiments() { char t; cout<<"Would you like Lemon with your tea?(y/n)"<<endl; cin>>t; if(t == 'y') return true; return false; } void TeaWithHook::prepareRecipe() { boilWater(); brew(); pourInCup(); if(customerWantsCondiments()) addCondiments(); } void CoffeeWithHook::brew() { cout<<"Dripping Coffee through filter"<<endl; } void CoffeeWithHook::addCondiments() { cout<<"Adding Sugar and Milk"<<endl; } //钩子,子类覆盖这个钩子。在此默认返回true bool CoffeeWithHook::customerWantsCondiments() { char t; cout<<"Would you like milk and sugar with your coffee?(y/n)"<<endl; cin>>t; if(t == 'y') return true; return false; } void CoffeeWithHook::prepareRecipe() { boilWater(); brew(); pourInCup(); if(customerWantsCondiments()) addCondiments(); }
测试代码:#include "Template.h" #include <iostream> using namespace std; int main() { TeaWithHook* teaHook = new TeaWithHook(); CoffeeWithHook* coffeeHook = new CoffeeWithHook(); cout<<"Making tea..."<<endl; teaHook->prepareRecipe(); cout<<endl<<"Making coffee..."<<endl; coffeeHook->prepareRecipe(); delete teaHook; delete coffeeHook; getchar(); getchar(); }
好莱坞原则:别调用我们,我们会调用你。
当我们设计模板方法模式时,我们告诉子类,“不要调用我们,我们会调用你”。
三、模板和策略模式的比较
模板方法模式:1、定义一个算法大纲,由其子类定义其中某些步骤的内容。这么一来,在算法中的个别步骤可以有不同的实现细节,但是算法的结构依然维持不变。2、对算法由更多的控制权,而且不会重复代码。事实上,除了极少的一部分之外,算法的每一个部分都是相同的,所以类的效率高一些。会重复使用到的代码,都被放进了超类当中,好让所有子类继承。策略模式:1、定义一个算法家族,并让这些算法可以互换。正因为每一个散发都被封装起来了,所以客户可以轻易地使用不同的算法。2、使用对象组合,比较有弹性,客户能在运行时改变算法,而客户所要做的,只是改用不同的策略对象。