作者: 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等)。
- 一颗愿意深究“为什么”的心:设计模式是手段,理解背后的权衡才是目的。
准备工作清单:
四、有什么注意事项?
- 不要用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;}
设计模式
创建型
结构型
行为型
单例模式
工厂方法
抽象工厂
适配器
外观
装饰者
代理
策略模式
观察者
命令
模板方法
迭代器
组合
状态
推荐学习路线(按原书章节):
接下来,我们以策略模式为例,给出完整的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++版)
| 策略模式 | 封装算法,使其可互换 | 抽象基类 + 组合 + 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++的模板(静态多态)实现零开销的策略模式,以及在游戏实体、支付系统等真实场景的应用。记得关注,不要错过!
本文为个人学习笔记,仅用于知识分享。如有错误,欢迎指正。
👍🏻 点赞 + 收藏 + 分享,让更多开发者看到这篇深度解析!❤️ 如果觉得有用,请给个赞支持一下作者!




