C++设计模式十--TemplatePattern(模板方法模式)

来源:互联网 发布:穿越火线fps优化器 编辑:程序博客网 时间:2024/06/05 17:15

定义

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

要点

  1.这个方法将算法定义成一组步骤,其中任何步骤都可以是抽象的,由子类负责实现。这样可以确保算法的结果不变,同时由子类提供部分实现。
  2.模板方法为我们提供了一种代码复用的重要技巧。
  3.策略模式模板方法模式都封装算法,策略用组合,模板方法用继承。
  4.工厂方法模式是模板方法的一种特殊版本。

类图

这里写图片描述

AbstractClass:模板方法抽象类。
ConcreteClass1/ConcreteClass2:具体类,可以有许多。每个类都实现了模板方法所需的全部操作。

钩子

  钩子是一种被声明在抽象类中的方法,但只有空的或默认的实现。钩子的用法主要由以下两点:
  1.钩子可以让子类实现算法中可选的部分;
  2.钩子能够让子类有机会对模板方法中某些即将发生的(或刚刚发生的)步骤做出反应。
  
  下面示例中CoffeeVeverage.h文件中实现了钩子,子类重写方法 virtual bool customerWantsCondiments() (改变返回值返回值)就能够控制 void addCondiments() 方法是否会被父类调用。

设计原则

  好莱坞原则:别调用我们,我们会调用你。

  1.好莱坞原则允许低层组件将自己挂钩到系统上,但高层组件会决定什么时候和怎样使用这些低层组件。
  2.决策权应该放在高层模块中,以便决定如何以及何时调用调用低层模块。
  3.低层模块并不是完全不能调用高层的方法,低层组件结束是往往会调用从超类中继承来的方法。应该避免高层和低层组件之间有明显的环状依赖。

示例

这里写图片描述

现实现上图所示算法:

CoffeeVeverage.h

#ifndef COFFEINEBEVERAGE_H#define COFFEINEBEVERAGE_H#include <iostream>using std::cout;using std::cin;using std::endl;// 模板方法类class CoffeineBeverage{public:    CoffeineBeverage(){}    virtual ~CoffeineBeverage(){}    void prepareRecipe()    {        boilWater();        brew();        pourInCup();        if (customerWantsCondiments()) {            addCondiments();        }    }    virtual void brew() = 0;    virtual void addCondiments() = 0;    void boilWater()    {        cout << "Boiling water" << endl;    }    void pourInCup()    {        cout << "Pouring into cup" << endl;    }    virtual bool customerWantsCondiments()  // 钩子    {        return true;    }};#endif

Tea.h

#ifndef TEA_H#define TEA_H#include <string>#include "CaffeineBeverage.h"using std::string;// 茶类(具体类)class Tea : public CoffeineBeverage{public:    Tea(){}    ~Tea(){}    void brew()    {        cout << "Steeping the tea" << endl;    }    void addCondiments()    {        cout << "Adding Lemon" << endl;    }    bool customerWantsCondiments()    {        string ret = gerUserInput();        if ("y" == ret) {            return true;        } else {            return false;        }    }private:    string gerUserInput()    {        string ret = "";        cout << "Would you like lemon with your tea (y/n)?" << endl;        cin >> ret;        if ("" == ret) {            return "n";        }        return ret;    }};#endif

Coffee.h

#ifndef COFFEE_H#define COFFEE_H#include <string>#include "CoffeineBeverage.h"using std::string;// 咖啡类(具体类)class Coffee : public CoffeineBeverage{public:    Coffee(){}    ~Coffee(){}    void brew()    {        cout << "Dripping Coffee through filter" << endl;    }    void addCondiments()    {        cout << "Adding Sugar and Milk" << endl;    }    bool customerWantsCondiments()    {        string ret = gerUserInput();        if ("y" == ret) {            return true;        } else {            return false;        }    }private:    string gerUserInput()    {        string ret = "";        cout << "Would you like mild and sugar with your coffee (y/n)?" << endl;        cin >> ret;        if ("" == ret) {            return "n";        }        return ret;    }};#endif

main.cpp

#include "CoffeineBeverage.h"#include "Coffee.h"#include "Tea.h"int main(){    Tea* tea = new Tea();    Coffee* coffee = new Coffee();    cout << "\nMaking tea ..." << endl;    tea->prepareRecipe();    cout << "\nMaking coffee ..." << endl;    coffee->prepareRecipe();}

Makefile

CXX = g++CFLAGS = -WallLDFLAGS = target = ressrcs = main.cppobjs = $(srcs:.cpp=.o)headers = $(wildcard *.h).PHONY: allall: $(target)$(target): $(objs) $(headers)    $(CXX) $(LDFLAGS) -o $(target) $(objs)$(objs):%.o:%.cpp    $(CXX) $(CFLAGS) -c -o $@ $<clean:    rm -f $(target) *.o

测试

测试结果如下图所示:
这里写图片描述

如上图所示,在茶和咖啡都有一次询问是否加入“调味品”,根据用户输入的不同产生了不同的结果,这就是钩子在起作用。

原创粉丝点击