AI Chat 设计模式:1. 模板方法

模板方法模式是一种简单而常见的设计模式,在许多软件开发场景下都得到了广泛应用。在这篇博客中,我们将使用 ChatAI 作为助手详细解析模板方法模式。本文采用问答式的方式展开,问题由我提出,答案由 Chat AI 作出,灰色背景的文字则主要是我的心理活动。

问题列表

  • Q.1 介绍下模板方法设计模式
  • A.1
  • Q.2 给我写一个 c++ 的版本
  • A.2
  • Q.3 举个具体点的场景吧,别搞这么抽象
  • A.3
  • Q.4 很好,再说说这个钩子方法吧
  • A.4

Q.1 介绍下模板方法设计模式

A.1

模板方法设计模式(Template Method Design Pattern)是一种行为型设计模式。它的主要思想是定义一个算法的骨架,将某些步骤推迟到子类实现。它允许子类重写算法的某些步骤,同时保持算法的整体结构不变。

模板方法设计模式由两个角色组成,分别是抽象类具体子类抽象类定义算法的骨架,包括一些抽象方法和模板方法。具体子类实现抽象方法,以便定制算法在特定情况下的行为

使用模板方法设计模式可以避免代码的重复,同时使得程序更加灵活,易于扩展。它常用于框架开发中,例如 Spring 框架的 IOC 容器就是基于模板方法设计模式实现的。总之,模板方法设计模式是一种非常实用的设计模式,它将算法的骨架和具体实现分离开,使得代码更加灵活、易于维护。

这玩意说的还挺清楚啊,主要思想、构成、优点,统统都提到了,确实比我自己猛,让我看看你的代码水平如何。

Q.2 给我写一个 c++ 的版本

A.2

好的,以下是一个基于C++的模板方法设计模式的例子:

#include 

class AbstractClass {
public:
    // 模板方法,定义算法的骨架
    void TemplateMethod() {
        PrimitiveOperation1();
        PrimitiveOperation2();
    }

    virtual ~AbstractClass() {}

protected:
    // 抽象方法,由子类实现,以便定制算法在特定情况下的行为
    virtual void PrimitiveOperation1() = 0;
    virtual void PrimitiveOperation2() = 0;
};

class ConcreteClass1 : public AbstractClass {
protected:
    // 实现抽象方法,定制算法在特定情况下的行为
    virtual void PrimitiveOperation1() override {
        std::cout << "ConcreteClass1::PrimitiveOperation1" << std::endl;
    }
    virtual void PrimitiveOperation2() override {
        std::cout << "ConcreteClass1::PrimitiveOperation2" << std::endl;
    }
};

class ConcreteClass2 : public AbstractClass {
protected:
    // 实现抽象方法,定制算法在特定情况下的行为
    virtual void PrimitiveOperation1() override {
        std::cout << "ConcreteClass2::PrimitiveOperation1" << std::endl;
    }
    virtual void PrimitiveOperation2() override {
        std::cout << "ConcreteClass2::PrimitiveOperation2" << std::endl;
    }
};

int main() {
    AbstractClass* ac = new ConcreteClass1;
    ac->TemplateMethod(); // 输出: ConcreteClass1::PrimitiveOperation1 ConcreteClass1::PrimitiveOperation2
    delete ac;

    ac = new ConcreteClass2;
    ac->TemplateMethod(); // 输出: ConcreteClass2::PrimitiveOperation1 ConcreteClass2::PrimitiveOperation2
    delete ac;

    return 0;
}

在这个例子中,AbstractClass 是抽象类,定义模板方法 TemplateMethod() 和抽象方法 PrimitiveOperation1()PrimitiveOperation2(),具体子类 ConcreteClass1ConcreteClass2 继承 AbstractClass 并实现抽象方法,定制算法在特定情况下的行为。在 main 函数中,我们创建了一个 ConcreteClass1 对象和一个 ConcreteClass2 对象,分别调用了其 TemplateMethod() 方法,输出相应的结果。

该例子展示了如何用模板方法设计模式编写具有高扩展性的程序,子类可以在不影响父类行为的前提下,自由定制自己的算法步骤来满足具体的业务需求。

在抽象类中,PrimitiveOperation1 和 PrimitiveOperation2 被定义为了纯虚函数,要求子类必须重写它们。但这并不是必须的,需视场景而定,一般虚函数即可。
这注释写得还挺到位,就是例子有点抽象。

Q.3 举个具体点的场景吧,别搞这么抽象

A.3

好的,让我们来看一个更具体的场景,比如制作咖啡和茶的过程。这里我们先定义一个抽象类 Beverage,里面包含了制作咖啡和茶的共同步骤子类可以实现不同的步骤以满足定制需求。具体代码如下:

#include 

class Beverage {
public:
    virtual ~Beverage() {}

    // 模板方法,定义制作饮料的过程
    void prepareBeverage() {
        boilWater();
        brew();
        pourInCup();
        /*
         * 这里使用钩子方法,如果 hook() 返回 true,
         * 则执行 addCondiments(),否则跳过
         */
        if (hook()) {
            addCondiments();
        }
    }

protected:
    // 抽象方法,煮水
    virtual void boilWater() {
        std::cout << "Boiling Water" << std::endl;
    }

    // 抽象方法,用沸水冲泡
    virtual void brew() = 0;

    // 抽象方法,倒入杯子
    virtual void pourInCup() {
        std::cout << "Pouring into cup" << std::endl;
    }

    // 钩子方法,子类可以选择性地覆盖
    virtual bool hook() {
        return true;
    }

    // 抽象方法,加入配料
    virtual void addCondiments() = 0;
};

class Coffee : public Beverage {
protected:
    // 实现 brew(),用沸水冲泡咖啡粉
    virtual void brew() override {
        std::cout << "Brewing Coffee" << std::endl;
    }

    // 实现 addCondiments(),加入糖和牛奶
    virtual void addCondiments() override {
        std::cout << "Adding Sugar and Milk" << std::endl;
    }
};

class Tea : public Beverage {
protected:
    // 实现 brew(),用沸水浸泡茶叶
    virtual void brew() override {
        std::cout << "Steeping Tea" << std::endl;
    }

    // 重写钩子方法,茶的制作不需要加配料
    virtual bool hook() override {
        return false;
    }
};

int main() {
    Beverage* beverage = new Coffee;
    beverage->prepareBeverage(); // 输出: Boiling Water Brewing Coffee Pouring into cup Adding Sugar and Milk
    delete beverage;

    beverage = new Tea;
    beverage->prepareBeverage(); // 输出: Boiling Water Steeping Tea Pouring into cup
    delete beverage;

    return 0;
}

在这个例子中,Beverage 是抽象类,定义了制作饮料的模板方法 prepareBeverage(),以及抽象方法 brew()addCondiments() 和钩子方法 hook(),子类 CoffeeTea 继承 Beverage 并分别实现 brew()addCondiments() 方法以定制不同饮料的制作过程。我们在 main 函数中创建了一个 Coffee 对象和一个 Tea 对象,并调用它们的 prepareBeverage() 方法,输出相应的结果。

其中,饮料的制作过程是 prepareBeverage() 方法,包含了 boilWater()brew()pourInCup()addCondiments() 等步骤。其中 hook() 方法是一个钩子方法,结合 if 语句使用,如果返回 true,则执行 addCondiments() 方法进行配料,否则跳过该步骤。

通过使用模板方法设计模式,我们可以将制作饮料的共同步骤抽象成一个模板方法,并在具体子类中实现特定方法,使得程序更加灵活,易于扩展。同时,钩子方法的使用可以让子类选择性地覆盖父类行为,使得程序更加智能化,能够根据不同情况进行不同的操作。

卧槽,这例子比我设想的要好啊。还提到了钩子方法,我当时学的时候就没人提这玩意,不错。

Q.4 很好,再说说这个钩子方法吧

A.4

好的,钩子方法(Hook Method)是模板方法设计模式中一个非常重要的概念。它类似于一个回调函数,在模板方法中被调用,用于判断是否执行一些特定的操作。

钩子方法通常被定义为一个虚函数(Virtual Function)或者纯虚函数(Pure Virtual Function),子类可以选择性地实现或者覆盖这个方法。如果钩子方法返回 true,则执行模板方法中定义的相关操作;反之则跳过这些操作,不会对程序产生影响。

在模板方法设计模式中,钩子方法可以使得算法更加灵活、易于扩展。子类可以通过实现或者覆盖钩子方法,来决定是否执行一些特定的操作,并根据自己的需要自由地定制算法的行为。此外,钩子方法也可以作为一种回调函数,供程序在某些特定的条件下做出相应的响应。

在茶和咖啡的例子中,我们使用了一个 hook() 钩子方法,来控制是否向饮料中加入配料。在 Tea 类中,我们覆盖了 hook() 方法,使得茶不需要加配料,只需要执行基本操作即可;而在 Coffee 类中,我们保留了 hook() 方法的默认实现,让咖啡加入了糖和牛奶。这样,我们就可以根据不同的需求来定制饮料的制作流程,增加了程序的灵活性和可扩展性。

为什么叫钩子呢,我的理解是子类钩住父类的意思,通过子类重写钩子方法,来实现反向控制父类的行为。
本篇分享就到此结束吧,不得不提,借助 AI来写博客还是快速啊。

你可能感兴趣的:(设计模式,设计模式,c++,Chat,AI)