您现在的位置是:首页 >技术教程 >AI Chat 设计模式:1. 模板方法网站首页技术教程
AI Chat 设计模式:1. 模板方法
模板方法模式是一种简单而常见的设计模式,在许多软件开发场景下都得到了广泛应用。在这篇博客中,我们将使用 ChatAI 作为助手详细解析模板方法模式。本文采用问答式的方式展开,问题由我提出,答案由 Chat AI 作出,灰色背景的文字则主要是我的心理活动。
Q.1 介绍下模板方法设计模式
A.1
模板方法设计模式(Template Method Design Pattern)是一种行为型设计模式。它的主要思想是定义一个算法的骨架,将某些步骤推迟到子类实现。它允许子类重写算法的某些步骤,同时保持算法的整体结构不变。
模板方法设计模式由两个角色组成,分别是抽象类
和具体子类
。抽象类定义算法的骨架,包括一些抽象方法和模板方法。具体子类实现抽象方法,以便定制算法在特定情况下的行为。
使用模板方法设计模式可以避免代码的重复,同时使得程序更加灵活,易于扩展。它常用于框架开发中,例如 Spring 框架的 IOC 容器就是基于模板方法设计模式实现的。总之,模板方法设计模式是一种非常实用的设计模式,它将算法的骨架和具体实现分离开,使得代码更加灵活、易于维护。
这玩意说的还挺清楚啊,主要思想、构成、优点,统统都提到了,确实比我自己猛,让我看看你的代码水平如何。
Q.2 给我写一个 c++ 的版本
A.2
好的,以下是一个基于C++的模板方法设计模式的例子:
#include <iostream>
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()
,具体子类 ConcreteClass1
和 ConcreteClass2
继承 AbstractClass
并实现抽象方法,定制算法在特定情况下的行为。在 main 函数中,我们创建了一个 ConcreteClass1
对象和一个 ConcreteClass2
对象,分别调用了其 TemplateMethod()
方法,输出相应的结果。
该例子展示了如何用模板方法设计模式编写具有高扩展性的程序,子类可以在不影响父类行为的前提下,自由定制自己的算法步骤来满足具体的业务需求。
在抽象类中,PrimitiveOperation1 和 PrimitiveOperation2 被定义为了纯虚函数,要求子类必须重写它们。但这并不是必须的,需视场景而定,一般虚函数即可。
这注释写得还挺到位,就是例子有点抽象。
Q.3 举个具体点的场景吧,别搞这么抽象
A.3
好的,让我们来看一个更具体的场景,比如制作咖啡和茶的过程。这里我们先定义一个抽象类 Beverage
,里面包含了制作咖啡和茶的共同步骤,子类可以实现不同的步骤以满足定制需求。具体代码如下:
#include <iostream>
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()
,子类 Coffee
和 Tea
继承 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来写博客还是快速啊。