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/

原文地址:https://www.cnblogs.com/LessNull/p/2617048.html