模板方法模式C++实现

参考书籍《Head First设计模式》

需求场景

           饮料店制作两种含咖啡因的饮料:茶和咖啡。茶和咖啡都包含以下4个步骤:

            1、把水煮沸

             2、冲泡

             3、把饮料倒进杯子

             4、加调料

           这4个步骤的顺序是固定的,不希望被改变的。其中第1步和第3步对茶和咖啡来说都是同样的处理方式,可以放在基类中处理。第3步和第4步针对两种饮料处理方式不同,其中茶需要用沸水侵泡茶叶,加牛奶(对,就是制作奶茶,哈哈哈),咖啡需要用沸水冲泡咖啡,加糖,这种不同的处理方式需要在子类中实现。

        基于上面的分析,可以得出以下类图结构

       

模式定义

        模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

        类图如下

        

       如上面需求场景中,在咖啡因饮料基类中,定义制作饮料的步骤(prepareRecipe方法中),子类茶和咖啡都不能改变这个步骤,但具体的冲泡方法brew和加调料方法addCondiments在子类中被重新定义。

代码实现

         针对需求场景中的例子代码实现如下

        

// CaffeineBeverage.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>

//Base Class CaffeineBeverage
class CaffeineBeverage {
public:
    virtual void prepareRecipe()final//用final修饰不希望子类覆盖该方法
    {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }
    virtual void brew() = 0;
    virtual void addCondiments() = 0;

    void boilWater()
    {
        std::cout << "Boiling water" << std::endl;
    }

    void pourInCup()
    {
        std::cout << "Pouring into cup" << std::endl;
    }

};

//Concrete Class Tea
class Tea :public CaffeineBeverage {
public:
    virtual void brew()override
    {
        std::cout << "Steeping the tea" << std::endl;
    }
    virtual void addCondiments()override
    {
        std::cout << "Adding milk" << std::endl;
    }
};

//Concrete Class Coffee
class Coffee :public CaffeineBeverage {
public:
    virtual void brew()override
    {
        std::cout << "Dripping COffe through filter" << std::endl;
    }
    virtual void addCondiments()override
    {
        std::cout << "Adding sugar" << std::endl;
    }
};

int main()
{
    Tea myTea;
    Coffee myCoffee;
    myTea.prepareRecipe();

    std::cout <<  std::endl;

    myCoffee.prepareRecipe();
}

   运行测试效果如下

   

        

钩子函数

        钩子是一种被声明在基类中的方法,但只有空的或者默认的实现。钩子的存在可以让子类有能力对算法的不同点进行挂钩,要不要挂钩,由子类自行决定。

        以上面需求场景的例子举例钩子函数,我们加一个条件判断来决定是否要加调料,修改后代码如下

       

    virtual void prepareRecipe()final//用final修饰不希望子类覆盖该方法
    {
        boilWater();
        brew();
        pourInCup();

        if(customerWantsCondiments())
            addCondiments();
    }

        其中 customerWantsCondiments()在基类中的默认实现返回true,子类可以覆盖这个方法,来具体决定是否需要加饮料,这个函数就是钩子函数。

    virtual bool customerWantsCondiments()
    {
        return true;
    }

         在子类茶Tea中具体实现这个钩子函数,决定是否要加调料,代码如下

         

    virtual bool Tea::customerWantsCondiments()override
    {
        char wants = ' ';
        while (wants != 'Y' && wants != 'y'
            && wants != 'N' && wants != 'n')
        {
            std::cout << "Would you like milk with your tea?(Y/N)" << std::endl;
            std::cin >> wants;
        }
        if (wants == 'y' || wants == 'Y')
            return true;
        else if (wants == 'n' || wants == 'N')
            return false;
    }

         测试代码,运行效果如下

        什么时候使用抽象方法,什么时候使用钩子? 

       当子类必须提供算法中的某个方法或步骤的实现时使用抽象方法(纯虚函数),如果算法的函数是可选的就用钩子(虚函数),如果这个步骤不想被改变使用final修饰的虚函数。

      模板方法模式遵循 “别调用我,我会调用你”的设计原则,这防止了依赖腐败。当高层组件依赖底层组件,而底层组件又依赖高层组件,而高层组件又依赖边侧组件,而边侧组件又依赖底层组件时,依赖腐败就发生了。


       

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
模板方法设计模式是一种行为设计模式,它定义了一个算法的骨架,将一些步骤的实现延迟到子类中。这种模式可以确保算法的结构保持不变,但允许子类提供特定的实现细节。在C++中,模板方法设计模式可以通过使用虚函数和继承来实现。 在给出的引用中,我们可以看到一个名为CoffeineBeverage的类,它定义了一个prepare_recipe()方法,并在内部调用了其他几个私有方法。这个类是一个基类,可以被子类继承并重写其中的方法。 具体来说,模板方法设计模式的关键是将算法的骨架定义在基类中,而将可变的实现细节留给子类去实现。在这个例子中,prepare_recipe()方法是算法的骨架,而brew()和add_condiments()方法则是可变的实现细节。 通过将brew()和add_condiments()方法定义为虚函数,基类CoffeineBeverage允许子类去重写这些方法以提供自己的实现。这样,当调用prepare_recipe()方法时,实际执行的是子类中重写后的方法。 使用模板方法设计模式的好处是可以提高代码的复用性和可扩展性。算法的骨架在基类中只需要定义一次,而具体的实现细节可以在不同的子类中灵活变化。 总结起来,C++中的模板方法设计模式通过定义一个算法的骨架并将可变的实现细节留给子类去实现,可以提供代码的复用性和可扩展性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [C++设计模式模板方法模式](https://blog.csdn.net/Long_xu/article/details/127118813)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [C++设计模式-模板方法模式](https://blog.csdn.net/qq78442761/article/details/91990149)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值