C++高效实现模板方法模式
模板方法模式——在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
下面,是大家很熟悉的一段代码——《Head First 设计模式》中第8章模板方法模式中星巴兹咖啡因饮料代码的C++版。
#include <iostream>
class Coffee
{
public:
void PrepareRecipe() //星巴兹咖啡冲泡法
{
BoilWater(); //把水煮沸
BrewCoffeeGrinds(); //用沸水冲泡咖啡
PourInCup(); //把咖啡倒进杯子
AddSugarAndMilk(); //加糖和牛奶
}
void BoilWater()
{std::cout << "把水煮沸" << std::endl;}
void BrewCoffeeGrinds()
{std::cout << "用沸水冲泡咖啡" << std::endl;}
void PourInCup()
{std::cout << "把咖啡倒进杯子" << std::endl;}
void AddSugarAndMilk()
{std::cout << "加糖和牛奶" << std::endl;}
};
class Tea
{
public:
void PrepareRecipe() //星巴兹茶冲泡法
{
BoilWater(); //把水煮沸
SteepTeaBag(); //用沸水浸泡茶叶
PourInCup(); //把茶倒进杯子
AddLemon(); //加柠檬
}
void BoilWater()
{std::cout << "把水煮沸" << std::endl;}
void SteepTeaBag()
{std::cout << "用沸水浸泡茶叶" << std::endl;}
void PourInCup()
{std::cout << "把茶倒进杯子" << std::endl;}
void AddLemon()
{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;
}
但是,这段代码有大量的重复代码,不利于星巴兹提高自己的竞争力。于是,我们对代码进行了重构。
#include <iostream>
class CaffeineBeverage //咖啡因饮料
{
public:
void PrepareRecipe() //咖啡因饮料冲泡法
{
BoilWater(); //把水煮沸
Brew(); //冲泡
PourInCup(); //把咖啡因饮料倒进杯子
AddCondiments(); //加调料
}
void BoilWater()
{std::cout << "把水煮沸" << std::endl;}
virtual void Brew() = 0;
void PourInCup()
{std::cout << "把咖啡倒进杯子" << std::endl;}
virtual void AddCondiments() = 0;
};
class Coffee : public CaffeineBeverage
{
public:
void Brew()
{std::cout << "用沸水冲泡咖啡" << std::endl;}
void AddCondiments()
{std::cout << "加糖和牛奶" << std::endl;}
};
class Tea : public CaffeineBeverage
{
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;
}
星巴兹咖啡公司使用模板方法模式,在不改变客户端代码的情况下,清除了重复的代码。使得星巴兹咖啡因饮料声名大振。
但是,随着顾客的增多,星巴兹咖啡公司里的顾客排起了长龙。
测试表明:
Brew(); //冲泡
AddCondiments(); //加调料
两个函数调用存在严重的效率问题——虚函数需要一个虚函数表,而且不能被内联,且比普通的函数调用(非内联)多一个间接寻址。
很多面向对象的爱好者会问:“难道有其他的解决方案?”
“有。模板。”
#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;
}
高效稳定的C++模板方法模式的实现,使得星巴兹咖啡公司一跃成为咖啡因饮料中的佼佼者。
请读者自己对比一下,这段代码与用虚函数实现模板方法模式的版本有什么改变。
参考文献:《Head First 设计模式》 转自:http://blog.163.com/zhanglibin_1222/blog/static/11195704720102179121470/