C++模板方法启示录:放羊娃的破局之路

举报
码事漫谈 发表于 2025/08/10 22:47:39 2025/08/10
【摘要】 我一直坚信,优雅的设计模式中,流淌着生活的哲学。模板方法模式是一种行为设计模式,它定义了一个操作中的算法骨架,而将一些步骤延迟到子类中实现。模板方法使得子类可以不改变算法结构的情况下,重新定义算法中的某些特定步骤。我对此最新的理解是:子类在父类规定的框架下,拥有有限的自由,但整体流程是不变的。一如放牛娃走不出放羊的命运。让我们先回顾一下这个经典的故事:有人问放羊娃:"你放羊是为了什么呀?""...

我一直坚信,优雅的设计模式中,流淌着生活的哲学。

模板方法模式是一种行为设计模式,它定义了一个操作中的算法骨架,而将一些步骤延迟到子类中实现。模板方法使得子类可以不改变算法结构的情况下,重新定义算法中的某些特定步骤。

我对此最新的理解是:子类在父类规定的框架下,拥有有限的自由,但整体流程是不变的。一如放牛娃走不出放羊的命运。

让我们先回顾一下这个经典的故事:

有人问放羊娃:"你放羊是为了什么呀?"
"为了卖钱。"
"那有钱了之后呢?"
"娶媳妇。"
"娶媳妇之后呢?"
"生孩子。"
"孩子长大之后呢?"
"接着放羊。"

这个故事展示了一个固定的生命周期循环:放羊→卖钱→娶妻→生子→放羊。这种固定的模式在编程中对应模板方法模式。

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;
}

输出结果

类图

«abstract»
ShepherdBoyLife
+liveLife()
#herdSheep()
#sellSheep()
#childGrowsUp()
#marry()
#haveChild()
TraditionalShepherdBoy
#marry()
#haveChild()
ModernShepherdBoy
#marry()
#haveChild()

代码解析

  1. ShepherdBoyLife 类:这是抽象基类,定义了放羊娃生命周期的模板方法liveLife(),它规定了整个生命周期的流程。

  2. 模板方法liveLife()是一个模板方法,它定义了算法的骨架:

    • 放羊
    • 卖羊
    • 娶妻(抽象方法)
    • 生子(抽象方法)
    • 孩子长大继续放羊
  3. 具体实现类

    • TraditionalShepherdBoy:实现传统的娶妻生子方式
    • ModernShepherdBoy:实现现代的结婚生子方式
  4. 关键点

    • 不变的流程在基类中实现
    • 可变的部分通过虚函数交给子类实现
    • 子类不能改变流程,但可以改变某些步骤的具体实现

特点

  1. 封装不变部分:将不变的行为移到超类,去除子类中的重复代码。
  2. 扩展可变部分:通过子类来扩展可变的行为,符合"开放-封闭原则"。
  3. 控制子类扩展:模板方法只在特定点调用"hook"操作,允许在这些点进行扩展。

应用场景

模板方法模式在C++中常用于:

  1. 框架设计:框架定义流程,用户实现具体步骤
  2. 算法实现:固定算法结构,可变步骤由子类实现
  3. 生命周期管理:如游戏对象的生命周期
  4. 标准化流程:如编译器的编译流程

从循环到突破:模板方法模式的双重哲学

放羊娃的故事本是一个封闭的循环:放羊→卖钱→娶妻→生子→放羊
这种固定的人生轨迹,恰如模板方法模式的经典应用——父类定义不可变的算法骨架,子类只能改变局部细节。

但真正的设计智慧在于:模板方法模式既能守护规则,也能创造突破的可能

想象第三个版本的放羊娃:

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;
    }
};

模式启示:

  1. 常规用法:通过虚函数实现"可替换的步骤"(如娶妻/生子方式)
  2. 高阶用法:通过重写非虚函数打破循环(如改写childGrowsUp()
  3. 设计哲学
    • 模板方法不是命运的枷锁
    • 父类控制最小必要约束
    • 子类拥有超越模板的自由

就像优秀框架的设计:既提供安全护栏,也留有逃生舱口
这才是模板方法模式最深刻的力量——它既能描述"放羊循环"的无奈,也能见证"破局者"改写命运的光芒。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。