欢迎光临
我们一直在努力

【C++】《Head First设计模式》读书笔记01: 《Head First设计模式》用C++学,从“鸭子模拟器”到掌握设计精髓

作者: andylin02
学习章节: 《Head First设计模式》C++版概览与学习指南
关键词:设计模式, C++, 策略模式, 观察者模式, 装饰者模式, 面向对象, 设计原则, 多态, 智能指针

《Head First设计模式》用C++学,从“鸭子模拟器”到掌握设计精髓

大家好,我是 andylin02。很多C++开发者在学习设计模式时都会遇到一个尴尬:市面上绝大多数资料都是用Java写的,直接搬过来总感觉“水土不服”——C++没有接口、没有垃圾回收,却有多继承、模板、RAII和强大的智能指针。其实,设计模式的本质是思想,不是语法,《Head First设计模式》恰恰是把思想讲得最通透的入门书。今天,我专门为C++开发者定制了这份学习指南,所有示例都用现代C++重写,让你写出既高效又优雅的C++程序。

无论你是在做游戏引擎、嵌入式系统、还是金融服务端,只要你写的程序需要适应变化,设计模式就能帮你构建出更灵活、更可维护的系统。

一、这本书到底讲了什么?——从“鸭子”的惨案说起

想象你正在用C++开发一个“鸭子模拟器”。你第一反应是写一个基类 Duck,所有鸭子都继承它:

class Duck {
public:
virtual void quack() { std::cout << "Quack!" << std::endl; }
virtual void swim() { std::cout << "I'm swimming." << std::endl; }
virtual void display() = 0; // 每种鸭子外观不同
};

一切都很美好,直到老板说:“我们需要鸭子会飞”。你顺手就在 Duck 里加了 fly():

virtual void fly() { std::cout << "I'm flying!" << std::endl; }

测试时,绿头鸭、红头鸭都飞得挺好——但橡皮鸭(RubberDuck)也扑腾着飞了起来,全场爆笑。你想通过覆盖 fly() 让橡皮鸭什么都不做,但后续老板又要求“诱饵鸭不能叫”、“火箭动力鸭要会飞”……很快你的继承体系就变成了灾难:代码重复、僵化、脆弱,改一处牵动全身。

这正是《Head First设计模式》要解决的核心矛盾:软件唯一不变的就是变化。全书通过一连串类似“鸭子困境”的故事,带你逐一掌握应对变化的 23个经典设计模式中最常用的十几个,并反复灌输背后的 9大设计原则。

全书脉络(C++视角):

  • 第1章 策略模式:把“飞行”和“叫声”这些会变化的行为抽象出来,用组合代替继承。
  • 第2章 观察者模式:实现一个一对多的依赖关系,当主题状态变化时自动通知所有观察者。
  • 第3章 装饰者模式:运行时动态地给对象添加职责,C++里可利用模板或者包装器实现。
  • 第4章 工厂模式:封装对象创建过程,隔离具体类的依赖。用工厂函数、工厂类,甚至模板工厂。
  • 第5章 单例模式:确保类只有一个实例。C++需要特别注意线程安全和内存释放,但现代C++提供了 std::call_once 等工具。
  • 第6章 命令模式:将请求封装成对象,支持撤销、队列、日志。C++中可以用 std::function 作为轻量级命令。
  • 第7章 适配器与外观:适配器转换不兼容的接口;外观提供简化的高层接口。
  • 第8章 模板方法:定义算法骨架,让子类实现具体步骤。C++用虚函数和非虚接口惯用法。
  • 第9章 迭代器与组合:统一遍历容器的方式;处理树形结构。C++的STL迭代器本身就是这一模式的典范。
  • 第10章 状态模式:对象内部状态改变时改变其行为,消灭巨大的 switch-case。
  • 第11章 代理模式:用代理对象控制对真实对象的访问(远程、虚拟、保护代理)。
  • 第12章 复合模式:MVC是多种模式的组合,理解MVC等于把全书串起来。

每个模式的结构都遵循:问题场景 → 错误设计 → 设计原则 → 模式定义 → UML类图 → C++实现 → 实际案例。这种“先痛后悟”的方式,让你真正理解为什么需要这个模式。

二、学好这本书,你能学到哪些关键知识?

认真学完并用C++亲手实现所有模式后,你将获得:

  • 9个改变代码质量的设计原则

    • 封装变化:找出程序中会变化的部分,把它们隔离开来。
    • 针对接口编程,而非针对实现:C++中,接口就是抽象基类(纯虚函数)或者概念(Concepts)。
    • 多用组合,少用继承:运行时绑定行为,比静态继承更灵活。
    • 为松耦合设计:让对象之间的关联降到最低。
    • 开闭原则:对扩展开放,对修改封闭。
    • 依赖倒置:依赖抽象,不要依赖具体类。
    • 最少知识原则:只与直接的朋友通信。
    • 好莱坞原则:“别调用我们,我们会调用你”,高层组件控制流程。
    • 单一职责原则:一个类应该只有一个引起它变化的原因。
  • 十几个核心设计模式的C++实现,理解它们如何利用多态、模板、RAII、智能指针等C++特性。

  • 看清设计模式的分类:创建型、结构型、行为型。遇到问题时,能快速判断该往哪个方向思考。

  • 写出适应变化的C++代码,不再怕需求变更,因为你的架构已经留好了“扩展点”。

  • 深入理解多态和动态绑定的强大,以及虚函数表、抽象基类、dynamic_cast 等在实际设计中的应用。

  • 三、我需要具备什么基础能力?要准备什么?

    这本书对面向对象设计零基础的人也很友好,但你至少需要:

    • C++基础:类、继承、虚函数、纯虚函数、多态、构造函数/析构函数顺序。能区别静态绑定和动态绑定。
    • C++11及以上特性:智能指针(unique_ptr、shared_ptr)、override 关键字、nullptr、auto。本书代码会用到这些现代特性,让内存管理更安全。
    • 基本的编译环境:安装支持C++11的编译器(g++ 4.8+、Clang 3.3+ 或 VS2015+),会用IDE(CLion、VS Code、Visual Studio等)。
    • 一颗愿意深究“为什么”的心:设计模式是手段,理解背后的权衡才是目的。

    准备工作清单:

  • 安装编译器,确认C++版本:g++ –version 和 g++ -std=c++11 测试。
  • 准备《Head First设计模式》(中文版/英文版均可),书中的Java代码我们会在课上全部用C++重写。
  • 准备一本笔记本或电子笔记,每学完一个模式,用自己的话总结:要解决的问题 → 设计原则 → 类图 → C++关键代码 → 现实类比。
  • 所有代码务必亲手敲一遍,并编译运行。观察虚函数调用、对象析构的时机,加深理解。
  • 四、有什么注意事项?

    • 不要用Java思维写C++:Java中有接口、垃圾回收,而C++靠纯虚函数、智能指针管理资源。务必使用 unique_ptr 或 shared_ptr 来表达所有权,避免裸指针内存泄漏。
    • 模式不是银弹:不要为了模式而模式。如果一段简单的 if-else 就能清晰地解决问题,那就用它。设计模式是为了应对未来可能的变化,如果永远不会变,过度设计就是浪费。
    • 优先吃透原则,再记模式结构:原则是心法,模式是招式。掌握了“封装变化”后,你甚至能自己“发明”策略模式。
    • 利用C++的静态多态:除了虚函数,C++的模板(静态多态)有时能提供更优的性能。在合适的场景(如策略模式)可以对比一下虚函数和模板的优缺点。
    • 注意析构顺序和虚析构函数:带有多态行为的基类一定要将析构函数声明为 virtual,否则可能导致资源泄漏。我们代码中会严格遵守这一规则。
    • 第12章 MVC 是试金石:学完全书后,试着分析一个你熟悉的C++框架(如Qt)如何组合使用这些模式,会有豁然开朗的感觉。

    五、详细学习文档与读书笔记

    首先,让我们对全书要学习的模式建立一个分类地图:

    #mermaid-svg-UL1ZXbxMS05umYaZ{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-UL1ZXbxMS05umYaZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UL1ZXbxMS05umYaZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UL1ZXbxMS05umYaZ .error-icon{fill:#552222;}#mermaid-svg-UL1ZXbxMS05umYaZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UL1ZXbxMS05umYaZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UL1ZXbxMS05umYaZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UL1ZXbxMS05umYaZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UL1ZXbxMS05umYaZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UL1ZXbxMS05umYaZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UL1ZXbxMS05umYaZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UL1ZXbxMS05umYaZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UL1ZXbxMS05umYaZ .marker.cross{stroke:#333333;}#mermaid-svg-UL1ZXbxMS05umYaZ svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UL1ZXbxMS05umYaZ p{margin:0;}#mermaid-svg-UL1ZXbxMS05umYaZ .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-UL1ZXbxMS05umYaZ .cluster-label text{fill:#333;}#mermaid-svg-UL1ZXbxMS05umYaZ .cluster-label span{color:#333;}#mermaid-svg-UL1ZXbxMS05umYaZ .cluster-label span p{background-color:transparent;}#mermaid-svg-UL1ZXbxMS05umYaZ .label text,#mermaid-svg-UL1ZXbxMS05umYaZ span{fill:#333;color:#333;}#mermaid-svg-UL1ZXbxMS05umYaZ .node rect,#mermaid-svg-UL1ZXbxMS05umYaZ .node circle,#mermaid-svg-UL1ZXbxMS05umYaZ .node ellipse,#mermaid-svg-UL1ZXbxMS05umYaZ .node polygon,#mermaid-svg-UL1ZXbxMS05umYaZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-UL1ZXbxMS05umYaZ .rough-node .label text,#mermaid-svg-UL1ZXbxMS05umYaZ .node .label text,#mermaid-svg-UL1ZXbxMS05umYaZ .image-shape .label,#mermaid-svg-UL1ZXbxMS05umYaZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-UL1ZXbxMS05umYaZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-UL1ZXbxMS05umYaZ .rough-node .label,#mermaid-svg-UL1ZXbxMS05umYaZ .node .label,#mermaid-svg-UL1ZXbxMS05umYaZ .image-shape .label,#mermaid-svg-UL1ZXbxMS05umYaZ .icon-shape .label{text-align:center;}#mermaid-svg-UL1ZXbxMS05umYaZ .node.clickable{cursor:pointer;}#mermaid-svg-UL1ZXbxMS05umYaZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-UL1ZXbxMS05umYaZ .arrowheadPath{fill:#333333;}#mermaid-svg-UL1ZXbxMS05umYaZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-UL1ZXbxMS05umYaZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-UL1ZXbxMS05umYaZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UL1ZXbxMS05umYaZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-UL1ZXbxMS05umYaZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UL1ZXbxMS05umYaZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-UL1ZXbxMS05umYaZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-UL1ZXbxMS05umYaZ .cluster text{fill:#333;}#mermaid-svg-UL1ZXbxMS05umYaZ .cluster span{color:#333;}#mermaid-svg-UL1ZXbxMS05umYaZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-UL1ZXbxMS05umYaZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-UL1ZXbxMS05umYaZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-UL1ZXbxMS05umYaZ .icon-shape,#mermaid-svg-UL1ZXbxMS05umYaZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UL1ZXbxMS05umYaZ .icon-shape p,#mermaid-svg-UL1ZXbxMS05umYaZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-UL1ZXbxMS05umYaZ .icon-shape .label rect,#mermaid-svg-UL1ZXbxMS05umYaZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UL1ZXbxMS05umYaZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-UL1ZXbxMS05umYaZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-UL1ZXbxMS05umYaZ :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    设计模式

    创建型

    结构型

    行为型

    单例模式

    工厂方法

    抽象工厂

    适配器

    外观

    装饰者

    代理

    策略模式

    观察者

    命令

    模板方法

    迭代器

    组合

    状态

    推荐学习路线(按原书章节):

  • 策略模式(第1章)—— 理解封装变化和组合替代继承。
  • 观察者模式(第2章)—— 松耦合的一对多通知。
  • 装饰者模式(第3章)—— 运行时动态扩展职责。
  • 工厂模式(第4章)—— 解耦对象创建。
  • 单例模式(第5章)—— 全局唯一实例,注意线程安全。
  • 命令模式(第6章)—— 请求对象化,支持撤销。
  • 适配器与外观(第7章)—— 统一接口与简化接口。
  • 模板方法与迭代器/组合(第8-9章)—— 算法骨架和统一遍历。
  • 状态模式(第10章)—— 状态驱动行为。
  • 代理模式(第11章)—— 控制访问。
  • 复合模式(第12章)—— MVC的解剖。
  • 接下来,我们以策略模式为例,给出完整的UML和C++代码,让你提前感受如何用C++实践设计模式。

    策略模式:拯救“鸭子模拟器”的设计原则

    故事回顾:Joe的鸭子模拟器里,每种鸭子都有自己独特的飞行和叫声行为。把这些行为直接实现在 Duck 基类中会导致橡皮鸭也会飞;如果通过接口让子类分别实现,又会造成大量代码重复。怎么破?

    设计原则登场:

    • 找出变化的部分并封装起来:这里变化的是“飞行行为”和“叫声行为”,我们将它们分别抽象成独立的接口。
    • 针对接口编程,而不是针对实现:Duck 类只依赖 FlyBehavior 和 QuackBehavior 两个抽象类,不关心具体的实现。
    • 多用组合,少用继承:每个 Duck 对象拥有飞行和叫声的行为对象,而不是继承这些行为。

    策略模式定义:定义一系列算法,把它们一个个封装起来,并且使它们可以互相替换。策略模式让算法的变化独立于使用它的客户。

    UML类图(C++视角):

    #mermaid-svg-WWVMWDCCDqHgYQh5{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-WWVMWDCCDqHgYQh5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-WWVMWDCCDqHgYQh5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-WWVMWDCCDqHgYQh5 .error-icon{fill:#552222;}#mermaid-svg-WWVMWDCCDqHgYQh5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-WWVMWDCCDqHgYQh5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-WWVMWDCCDqHgYQh5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-WWVMWDCCDqHgYQh5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-WWVMWDCCDqHgYQh5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-WWVMWDCCDqHgYQh5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-WWVMWDCCDqHgYQh5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-WWVMWDCCDqHgYQh5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-WWVMWDCCDqHgYQh5 .marker.cross{stroke:#333333;}#mermaid-svg-WWVMWDCCDqHgYQh5 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-WWVMWDCCDqHgYQh5 p{margin:0;}#mermaid-svg-WWVMWDCCDqHgYQh5 g.classGroup text{fill:#9370DB;stroke:none;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-WWVMWDCCDqHgYQh5 g.classGroup text .title{font-weight:bolder;}#mermaid-svg-WWVMWDCCDqHgYQh5 .cluster-label text{fill:#333;}#mermaid-svg-WWVMWDCCDqHgYQh5 .cluster-label span{color:#333;}#mermaid-svg-WWVMWDCCDqHgYQh5 .cluster-label span p{background-color:transparent;}#mermaid-svg-WWVMWDCCDqHgYQh5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-WWVMWDCCDqHgYQh5 .cluster text{fill:#333;}#mermaid-svg-WWVMWDCCDqHgYQh5 .cluster span{color:#333;}#mermaid-svg-WWVMWDCCDqHgYQh5 .nodeLabel,#mermaid-svg-WWVMWDCCDqHgYQh5 .edgeLabel{color:#131300;}#mermaid-svg-WWVMWDCCDqHgYQh5 .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-WWVMWDCCDqHgYQh5 .label text{fill:#131300;}#mermaid-svg-WWVMWDCCDqHgYQh5 .labelBkg{background:#ECECFF;}#mermaid-svg-WWVMWDCCDqHgYQh5 .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-WWVMWDCCDqHgYQh5 .classTitle{font-weight:bolder;}#mermaid-svg-WWVMWDCCDqHgYQh5 .node rect,#mermaid-svg-WWVMWDCCDqHgYQh5 .node circle,#mermaid-svg-WWVMWDCCDqHgYQh5 .node ellipse,#mermaid-svg-WWVMWDCCDqHgYQh5 .node polygon,#mermaid-svg-WWVMWDCCDqHgYQh5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-WWVMWDCCDqHgYQh5 .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-WWVMWDCCDqHgYQh5 g.clickable{cursor:pointer;}#mermaid-svg-WWVMWDCCDqHgYQh5 g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-WWVMWDCCDqHgYQh5 g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-WWVMWDCCDqHgYQh5 .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-WWVMWDCCDqHgYQh5 .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-WWVMWDCCDqHgYQh5 .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-WWVMWDCCDqHgYQh5 .dashed-line{stroke-dasharray:3;}#mermaid-svg-WWVMWDCCDqHgYQh5 .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-WWVMWDCCDqHgYQh5 #compositionStart,#mermaid-svg-WWVMWDCCDqHgYQh5 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-WWVMWDCCDqHgYQh5 #compositionEnd,#mermaid-svg-WWVMWDCCDqHgYQh5 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-WWVMWDCCDqHgYQh5 #dependencyStart,#mermaid-svg-WWVMWDCCDqHgYQh5 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-WWVMWDCCDqHgYQh5 #dependencyStart,#mermaid-svg-WWVMWDCCDqHgYQh5 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-WWVMWDCCDqHgYQh5 #extensionStart,#mermaid-svg-WWVMWDCCDqHgYQh5 .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-WWVMWDCCDqHgYQh5 #extensionEnd,#mermaid-svg-WWVMWDCCDqHgYQh5 .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-WWVMWDCCDqHgYQh5 #aggregationStart,#mermaid-svg-WWVMWDCCDqHgYQh5 .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-WWVMWDCCDqHgYQh5 #aggregationEnd,#mermaid-svg-WWVMWDCCDqHgYQh5 .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-WWVMWDCCDqHgYQh5 #lollipopStart,#mermaid-svg-WWVMWDCCDqHgYQh5 .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-WWVMWDCCDqHgYQh5 #lollipopEnd,#mermaid-svg-WWVMWDCCDqHgYQh5 .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-WWVMWDCCDqHgYQh5 .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-WWVMWDCCDqHgYQh5 .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-WWVMWDCCDqHgYQh5 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-WWVMWDCCDqHgYQh5 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-WWVMWDCCDqHgYQh5 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    «abstract»

    Duck

    – unique_ptr<FlyBehavior> flyBehavior

    – unique_ptr<QuackBehavior> quackBehavior

    + performFly() : void

    + performQuack() : void

    + swim() : void

    + display() : void

    + setFlyBehavior(unique_ptr<FlyBehavior>) : void

    + setQuackBehavior(unique_ptr<QuackBehavior>) : void

    + virtual ~Duck()

    MallardDuck

    + display() : void

    RubberDuck

    + display() : void

    «abstract»

    FlyBehavior

    + fly() : void

    FlyWithWings

    + fly() : void

    FlyNoWay

    + fly() : void

    «abstract»

    QuackBehavior

    + quack() : void

    Quack

    + quack() : void

    Squeak

    + quack() : void

    MuteQuack

    + quack() : void

    C++完整源码:
    使用智能指针 std::unique_ptr 明确所有权,基类虚析构确保正确释放。

    #include <iostream>
    #include <memory>

    // ========== 飞行行为抽象基类与实现 ==========
    class FlyBehavior {
    public:
    virtual void fly() const = 0;
    virtual ~FlyBehavior() = default;
    };

    class FlyWithWings : public FlyBehavior {
    public:
    void fly() const override {
    std::cout << "I'm flying with wings!" << std::endl;
    }
    };

    class FlyNoWay : public FlyBehavior {
    public:
    void fly() const override {
    std::cout << "I can't fly." << std::endl;
    }
    };

    // ========== 叫声行为抽象基类与实现 ==========
    class QuackBehavior {
    public:
    virtual void quack() const = 0;
    virtual ~QuackBehavior() = default;
    };

    class Quack : public QuackBehavior {
    public:
    void quack() const override {
    std::cout << "Quack! Quack!" << std::endl;
    }
    };

    class Squeak : public QuackBehavior {
    public:
    void quack() const override {
    std::cout << "Squeak! Squeak!" << std::endl;
    }
    };

    class MuteQuack : public QuackBehavior {
    public:
    void quack() const override {
    std::cout << "<< Silence >>" << std::endl;
    }
    };

    // ========== 鸭子抽象基类 ==========
    class Duck {
    protected:
    // 使用unique_ptr管理行为组件,子类构造函数负责初始化
    std::unique_ptr<FlyBehavior> flyBehavior;
    std::unique_ptr<QuackBehavior> quackBehavior;

    public:
    Duck() = default;
    virtual ~Duck() = default; // 重要:虚析构

    void performFly() const {
    if (flyBehavior) {
    flyBehavior->fly();
    }
    }

    void performQuack() const {
    if (quackBehavior) {
    quackBehavior->quack();
    }
    }

    void swim() const {
    std::cout << "All ducks float, even decoys!" << std::endl;
    }

    virtual void display() const = 0;

    // 动态改变行为
    void setFlyBehavior(std::unique_ptr<FlyBehavior> fb) {
    flyBehavior = std::move(fb);
    }

    void setQuackBehavior(std::unique_ptr<QuackBehavior> qb) {
    quackBehavior = std::move(qb);
    }
    };

    // ========== 具体鸭子子类 ==========
    class MallardDuck : public Duck {
    public:
    MallardDuck() {
    // 在构造时组合具体的行为组件
    flyBehavior = std::make_unique<FlyWithWings>();
    quackBehavior = std::make_unique<Quack>();
    }

    void display() const override {
    std::cout << "I'm a real Mallard duck" << std::endl;
    }
    };

    class RubberDuck : public Duck {
    public:
    RubberDuck() {
    flyBehavior = std::make_unique<FlyNoWay>();
    quackBehavior = std::make_unique<Squeak>();
    }

    void display() const override {
    std::cout << "I'm a rubber duckie" << std::endl;
    }
    };

    // ========== 测试 ==========
    int main() {
    MallardDuck mallard;
    mallard.display();
    mallard.performQuack();
    mallard.performFly();

    std::cout << "——————-" << std::endl;

    RubberDuck rubber;
    rubber.display();
    rubber.performQuack();
    rubber.performFly();

    // 动态改变行为——策略模式的威力
    std::cout << "\\n>>> 给橡皮鸭装上火箭引擎!" << std::endl;
    rubber.setFlyBehavior(std::make_unique<FlyWithWings>());
    rubber.performFly(); // 现在橡皮鸭也能飞了!

    return 0;
    }

    运行输出:

    I'm a real Mallard duck
    Quack! Quack!
    I'm flying with wings!
    ——————-
    I'm a rubber duckie
    Squeak! Squeak!
    I can't fly.

    >>> 给橡皮鸭装上火箭引擎!
    I'm flying with wings!

    这段代码清晰展示了策略模式的核心:通过组合行为对象,我们让不同类型的鸭子拥有了灵活可变的行为,并且可以在运行时动态替换。同时,通过使用 unique_ptr,我们做到了明确的所有权管理和自动资源释放,无需手动 delete。

    全书模式速查笔记(C++版)

    模式一句话理解C++实现要点
    策略模式 封装算法,使其可互换 抽象基类 + 组合 + unique_ptr;也可用模板静态多态
    观察者模式 主题状态改变时通知所有观察者 使用 std::vector<std::weak_ptr> 管理观察者
    装饰者模式 动态给对象添加职责 抽象装饰类包装组件,虚函数委托
    工厂方法 定义创建对象的接口,让子类决定实例化哪个类 虚函数工厂;或 std::function 工厂
    抽象工厂 创建一系列相关对象,而不指定具体类 抽象工厂基类,具体工厂返回具体产品
    单例模式 确保一个类只有一个实例 std::call_once + 局部静态变量(Meyers’ Singleton)
    命令模式 把请求封装为对象,支持撤销 std::function 或命令抽象基类
    适配器模式 转换接口,让不兼容的类可以一起工作 类适配器(多继承)或对象适配器(组合)
    外观模式 提供统一的高层接口,简化子系统 封装复杂的子系统调用
    模板方法 定义算法骨架,将部分步骤延迟到子类 非虚接口 + 虚函数(NVI惯用法)
    迭代器模式 顺序访问容器元素,不暴露内部结构 C++ STL迭代器就是典范
    组合模式 将对象组织成树形结构,统一处理单个和组合对象 抽象组件类,叶子和组合分别实现
    状态模式 对象内部状态改变时改变行为 状态抽象类,上下文持有当前状态指针
    代理模式 为另一个对象提供替身,控制访问 代理类实现相同接口,内部持有真实对象指针

    建议把这张表打印出来,每学完一个模式就填写自己的理解和一个具体实例,强化记忆。

    六、总结

    《Head First设计模式》用故事化的方式帮你建立了面向变化的设计思维。当你用C++实现这些模式时,你会发现:内存管理、虚函数开销、模板的静态多态这些语言特性,并不是障碍,而是实现设计原则的强大工具。

    从策略模式开始,你学会了把变化“关进笼子”。继续学下去,你再看自己过去写的代码,会自然地问:“这里什么可能会变?我该如何隔离它?” —— 那个时刻,你就真的把设计模式学活了。

    七、下篇预告:第一章·策略模式(C++深度解析)

    今天我们整体了解了《Head First设计模式》的全貌,并用C++实现了策略模式的示例。下一篇文章,我会带你彻底深入第一章——策略模式:我们将从头构建鸭子模拟器,一步步分析每个设计决策的错误与修正,让你深刻体会组合优于继承。同时,我会展示如何利用C++的模板(静态多态)实现零开销的策略模式,以及在游戏实体、支付系统等真实场景的应用。记得关注,不要错过!


    本文为个人学习笔记,仅用于知识分享。如有错误,欢迎指正。
    👍🏻 点赞 + 收藏 + 分享,让更多开发者看到这篇深度解析!❤️ 如果觉得有用,请给个赞支持一下作者!

    赞(0)
    未经允许不得转载:171主机测评 » 【C++】《Head First设计模式》读书笔记01: 《Head First设计模式》用C++学,从“鸭子模拟器”到掌握设计精髓
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址