C++模板方法启示录:放羊娃的破局之路
【摘要】 我一直坚信,优雅的设计模式中,流淌着生活的哲学。模板方法模式是一种行为设计模式,它定义了一个操作中的算法骨架,而将一些步骤延迟到子类中实现。模板方法使得子类可以不改变算法结构的情况下,重新定义算法中的某些特定步骤。我对此最新的理解是:子类在父类规定的框架下,拥有有限的自由,但整体流程是不变的。一如放牛娃走不出放羊的命运。让我们先回顾一下这个经典的故事:有人问放羊娃:"你放羊是为了什么呀?""...
我一直坚信,优雅的设计模式中,流淌着生活的哲学。
模板方法模式是一种行为设计模式,它定义了一个操作中的算法骨架,而将一些步骤延迟到子类中实现。模板方法使得子类可以不改变算法结构的情况下,重新定义算法中的某些特定步骤。
我对此最新的理解是:子类在父类规定的框架下,拥有有限的自由,但整体流程是不变的。一如放牛娃走不出放羊的命运。
让我们先回顾一下这个经典的故事:
有人问放羊娃:"你放羊是为了什么呀?"
"为了卖钱。"
"那有钱了之后呢?"
"娶媳妇。"
"娶媳妇之后呢?"
"生孩子。"
"孩子长大之后呢?"
"接着放羊。"
这个故事展示了一个固定的生命周期循环:放羊→卖钱→娶妻→生子→放羊。这种固定的模式在编程中对应模板方法模式。
C++实现放羊娃的故事
让我们用C++来实现这个放羊娃的故事,展示模板方法模式的应用:
#include <iostream>
#include <string>
// 抽象基类 - 定义放羊娃的生命周期模板
class ShepherdBoyLife {
public:
// 模板方法 - 定义生命周期流程
void liveLife() {
herdSheep();
sellSheep();
marry();
haveChild();
childGrowsUp();
}
virtual ~ShepherdBoyLife() = default;
protected:
// 基本方法 - 具体步骤
void herdSheep() {
std::cout << "每天放羊..." << std::endl;
}
void sellSheep() {
std::cout << "卖羊赚钱..." << std::endl;
}
virtual void marry() = 0; // 抽象方法 - 由子类实现
virtual void haveChild() = 0; // 抽象方法 - 由子类实现
void childGrowsUp() {
std::cout << "孩子长大了..." << std::endl;
std::cout << "孩子开始: ";
herdSheep(); // 循环又开始了
}
};
// 具体实现 - 传统放羊娃
class TraditionalShepherdBoy : public ShepherdBoyLife {
protected:
void marry() override {
std::cout << "用钱娶媳妇..." << std::endl;
}
void haveChild() override {
std::cout << "生儿育女..." << std::endl;
}
};
// 具体实现 - 现代放羊娃
class ModernShepherdBoy : public ShepherdBoyLife {
protected:
void marry() override {
std::cout << "用钱买房结婚..." << std::endl;
}
void haveChild() override {
std::cout << "生二胎..." << std::endl;
}
};
int main() {
std::cout << "=== 传统放羊娃的一生 ===" << std::endl;
TraditionalShepherdBoy traditionalBoy;
traditionalBoy.liveLife();
std::cout << "\n=== 现代放羊娃的一生 ===" << std::endl;
ModernShepherdBoy modernBoy;
modernBoy.liveLife();
return 0;
}
输出结果
类图
代码解析
-
ShepherdBoyLife 类:这是抽象基类,定义了放羊娃生命周期的模板方法
liveLife()
,它规定了整个生命周期的流程。 -
模板方法:
liveLife()
是一个模板方法,它定义了算法的骨架:- 放羊
- 卖羊
- 娶妻(抽象方法)
- 生子(抽象方法)
- 孩子长大继续放羊
-
具体实现类:
TraditionalShepherdBoy
:实现传统的娶妻生子方式ModernShepherdBoy
:实现现代的结婚生子方式
-
关键点:
- 不变的流程在基类中实现
- 可变的部分通过虚函数交给子类实现
- 子类不能改变流程,但可以改变某些步骤的具体实现
特点
- 封装不变部分:将不变的行为移到超类,去除子类中的重复代码。
- 扩展可变部分:通过子类来扩展可变的行为,符合"开放-封闭原则"。
- 控制子类扩展:模板方法只在特定点调用"hook"操作,允许在这些点进行扩展。
应用场景
模板方法模式在C++中常用于:
- 框架设计:框架定义流程,用户实现具体步骤
- 算法实现:固定算法结构,可变步骤由子类实现
- 生命周期管理:如游戏对象的生命周期
- 标准化流程:如编译器的编译流程
从循环到突破:模板方法模式的双重哲学
放羊娃的故事本是一个封闭的循环:放羊→卖钱→娶妻→生子→放羊
这种固定的人生轨迹,恰如模板方法模式的经典应用——父类定义不可变的算法骨架,子类只能改变局部细节。
但真正的设计智慧在于:模板方法模式既能守护规则,也能创造突破的可能
想象第三个版本的放羊娃:
class RevolutionaryShepherdBoy : public ShepherdBoyLife {
protected:
void marry() override {
std::cout << "用钱读书深造..." << std::endl;
}
void haveChild() override {
std::cout << "创造新事业..." << std::endl;
}
// 关键突破:重写最终步骤
void childGrowsUp() override {
std::cout << "下一代成为科学家,彻底改变家族命运!" << std::endl;
}
};
模式启示:
- 常规用法:通过虚函数实现"可替换的步骤"(如娶妻/生子方式)
- 高阶用法:通过重写非虚函数打破循环(如改写
childGrowsUp()
) - 设计哲学:
- 模板方法不是命运的枷锁
- 父类控制最小必要约束
- 子类拥有超越模板的自由
就像优秀框架的设计:既提供安全护栏,也留有逃生舱口
这才是模板方法模式最深刻的力量——它既能描述"放羊循环"的无奈,也能见证"破局者"改写命运的光芒。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)