zoukankan      html  css  js  c++  java
  • 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/

  • 相关阅读:
    MSSQL的基础应用
    SQLLite的使用
    C#反射调用其它DLL的委托事件 传值
    SQLServer 取 字段名称 类型 字段描述 等
    WCF跨域 客户端无法访问
    不同的数据库数据类型映射
    用人四策
    思考集体跳槽
    C/C++/Qt 统计运行时间
    CUDA项目属性设置
  • 原文地址:https://www.cnblogs.com/LessNull/p/2617048.html
Copyright © 2011-2022 走看看