设计模式:模板方法模式

目录

一、引言

二、优化前的代码

三、模板方法模式

四、优化后的代码

五、应用场景

六、结语


一、引言

老样子,先来看一个具体的简单例子。

冲饮料的固定流程:

  1. 把水烧开
  2. 冲泡(咖啡 / 茶不一样)
  3. 倒入杯子
  4. 加配料(糖 / 牛奶 / 柠檬不一样)

其中,1、3步骤是固定的,不管冲什么饮料都一样。2、4步骤是可变的,取决于要冲什么饮料。

整个冲饮料的顺序就是模板方法。现在不理解没关系,看完你就明白了。

二、优化前的代码

用代码实现上面的例子。

cpp 复制代码
#include <iostream>
#include <string>

class Drink {
public:
    Drink(const std::string& name):_name(name) {}
    void makeDrink() {
        boilWater();     // 烧水
        brew();          // 冲泡
        pourInCup();     // 倒入杯子中
        addCondiments(); // 添加配料
    }
private:
    void boilWater() {
        std::cout << "烧水!"<< std::endl;
    }
    void brew() {
        if (_name == "茶") {
            std::cout << "冲茶!" << std::endl;
        }else if (_name == "咖啡") {
            std::cout << "冲咖啡!" << std::endl;
        }
    }
    void pourInCup() {
        std::cout << "倒入杯子中!" << std::endl;
    }
    void addCondiments() {
        if (_name == "茶") {
            std::cout << "加白糖!" << std::endl;
        }else if (_name == "咖啡") {
            std::cout << "加牛奶!" << std::endl;
        }
    }
private:
    std::string _name;
};

int main() {
    Drink* d1 = new Drink("茶");
    d1->makeDrink();

    Drink* d2 = new Drink("咖啡");
    d2->makeDrink();

    delete d1;
    delete d2;
    return 0;
}

这个代码有什么问题?

如果要冲罗汉果茶,那就要修改Drink类中的代码,在brew()函数和addCondiments()函数中增加一个else if。这就违背了开闭原则------对扩展开放,对修改关闭。

仅凭这一点,这段代码就不是一段好代码。

三、模板方法模式

我们来看看它的定义:

定义一个操作中的算法骨架,而将一些步骤延迟到子类中实现。模板方法使得子类可以不改变一个算法的结构,即可重新定义该算法的某些特定步骤。

是不是感觉说了跟没说一样?

这个毕竟是教科书上的标准定义,抽象很正常。

结合结构图,重新解释这个定义。

在抽象类中定义一个模板方法,这个模板方法规定了业务流程的执行顺序与骨架。对应到上面代码中,makeDrink方法就是模板方法,模板方法中调用的所有函数,叫做基本方法。

流程中的有些步骤,可以声明为抽象方法,具体的实现交给子类重写实现。

子类通过继承抽象类、实现抽象方法,来完成流程的个性化。

对应到上面代码中,brew()函数和addCondiments()函数就可以声明为抽象方法。具体要冲什么饮料、加什么配料由子类来具体实现。

总的来说就是,父类的模板方法定流程骨架,子类通过重写填步骤细节,使得同一套流程可以有不同的结果。

四、优化后的代码

下面,应用模板方法模式对代码进行优化。

cpp 复制代码
#include <iostream>
#include <string>

class Drink {
public:
    // 流程是固定死的
    void makeDrink() {
        boilWater();     // 烧水
        brew();          // 冲泡
        pourInCup();     // 倒入杯子中
        addCondiments(); // 添加配料
    }
protected:
    // 这些流程,不管是冲什么,都是不变的,所以放在父类实现
    void boilWater() {
        std::cout << "烧水!"<< std::endl;
    }
    void pourInCup() {
        std::cout << "倒入杯子中!" << std::endl;
    }
    // 声明为纯虚函数,由子类实现
    virtual void brew() = 0;
    virtual void addCondiments() = 0;
};

class DrinkTea : public Drink {
private:
    virtual void brew() override {
        std::cout << "泡茶" << std::endl;
    }
    virtual void addCondiments() override {
        std::cout << "加白糖" << std::endl;
    }
};

class DrinkCoffee : public Drink {
private:
    virtual void brew() override {
        std::cout << "冲咖啡" << std::endl;
    }
    virtual void addCondiments() override {
        std::cout << "加牛奶" << std::endl;
    }
};

int main() {
    std::cout << "=========================制作茶========================:" << std::endl;
    Drink* d1 = new DrinkTea();
    d1->makeDrink();

    std::cout << "=========================制作咖啡========================:" << std::endl;
    Drink* d2 = new DrinkCoffee();
    d2->makeDrink();

    delete d1;
    delete d2;

    return 0;
}

如果要制作罗汉果茶,那么只需要增加一个子类继承父类并实现抽象方法即可。

我们看看优化后的代码都符合哪些设计原则。

  1. 开闭原则:制作新的饮料不改父类的代码。
  2. 单一职责:只负责制作饮料。

这两个设计原则是比较容易看出来的。

五、应用场景

流程固定,但流程的中方法可能不一样,这样的场景下就可以考虑使用模板方法模式。

六、结语

欢迎批评指正!


结束!

相关推荐
如来神掌十八式4 小时前
设计模式之装饰器模式
java·设计模式
qqxhb17 小时前
26|Agent 设计模式:ReAct、Plan-and-Solve 与反射
设计模式·react模式·plan-and-solve·reflection模式
hssfscv18 小时前
软件设计师下午题六——Java的各种设计模式
java·算法·设计模式
zhaoshuzhaoshu20 小时前
设计模式之创建型设计模式详细解析(含示例)
单例模式·设计模式·架构
倚楼盼风雨21 小时前
浅析设计模式-23种设计模式剖析
设计模式
Momentary_SixthSense2 天前
设计模式之工厂模式
java·开发语言·设计模式
Java码农也是农2 天前
Multi-Agent 系统设计模式
设计模式·agent·multi-agent
sg_knight2 天前
设计模式实战:状态模式(State)
python·ui·设计模式·状态模式·state
workflower2 天前
深度学习是通用型人工智能的基础
人工智能·深度学习·设计模式·软件工程·软件构建·制造