设计模式(类的拓扑结构)(为什么会产生设计模式,以及什么是设计模式)
设计模式(类的拓扑结构)(为什么会产生设计模式,以及什么是设计模式)
这篇文章就是为了解决:
1.什么是设计模式
2.为什么会有设计模式
1.什么是设计模式
其实很多人不懂设计模式,本质上是被这个名字误导了
他压根不是什么模式
他是解决需求的类的拓扑结构
他是类和类组成的一种结构,可以用来解决需求。
2.为什么需要设计模式
其实了解设计模式
本质上,就是熟练使用类的设计
多个类之间怎么组合设计
来实现需求
对于写复杂需求,重构代码
在特定的场景有画龙点睛的作用
3.掌握设计模式实际上掌握的事什么东西
掌握的是多个类之间可以怎么连接设计
所以我们看,掌握设计模式
就是掌握多个类之间怎么设计
通过继承,组合,实现这些
设计模式不是什么模式,而是类的拓扑结构
这篇文章就想说清楚两件事:
- 什么是设计模式
- 为什么会有设计模式
一、什么是设计模式
其实很多人觉得设计模式难懂,本质上是被“模式”这个名字误导了。
它压根不是什么玄妙的模式,也不是一段可以直接复制粘贴的代码库。
设计模式 = 解决特定需求的类的拓扑结构。
什么叫“类的拓扑结构”?
简单说,就是若干个类通过继承、实现、组合这三种基本关系,连接成的一种特定的结构图。这个结构图是用来解决某一类重复出现的软件设计问题的。
所有 GoF 的 23 种设计模式,本质上都是这三种关系原语搭建出来的:
| 关系原语 | 代码表现 | 在模式中的典型角色 |
|---|---|---|
| 继承 | class B extends A | 模板方法模式(定义算法骨架),抽象工厂(定义产品族) |
| 实现 | class B implements I | 策略模式的策略接口,观察者模式的通知接口 |
| 组合 | class A { B b; } | 装饰器模式中持有被装饰对象,适配器中持有被适配者 |
拿策略模式举例,它的拓扑结构非常清晰:
Context (上下文) Strategy (接口) │ ▲ │ 组合一个 ────────────────────── 实现 │ │ ├─────────> 具体策略A 具体策略A ├─────────> 具体策略B 具体策略B └─────────> 具体策略C 具体策略C这个拓扑的特点是:星型 + 组合。上下文只依赖接口,不依赖具体策略类。要增加新策略,只需要再增加一个实现类,不改动上下文。你看,结构决定了能力。
所以,别再被“模式”两个字唬住了。
设计模式就是一张张类与类如何连接的蓝图。
二、为什么需要设计模式
2.1 历史原因:大规模面向对象编程后的必然产物
1994 年,四位作者(Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides,简称 GoF)出版了《设计模式:可复用面向对象软件的基础》。
他们并没有“发明”这些模式,而是总结了 23 个已经在大规模系统中被反复验证有效的类的组织结构。
为什么是 1994 年?
因为 80–90 年代,C++ 和 Smalltalk 等面向对象语言开始普及,大家都在用“类”编程。但很多人只是把类当成“带方法的 C 结构体”,不知道怎么组织多个类之间的协作。
大型软件频繁出现崩溃、维护困难,业界急需一套类与类协作的标准走法。
设计模式的本质不是什么学院派空想,而是大规模面向对象编程产生的避坑指南。
2.2 根本原因:管理变化,降低修改成本
更深一层的原因是软件世界的一个根本矛盾:
需求总是在变化,但代码的修改成本很高。
一开始写代码,你可能只用两三个类,直接互相new、直接调用。这就像一个临时搭起来的棚屋,很快、很随意。
但当需求不断增加(比如要支持新的支付方式、新的导出格式、新的UI风格),直接修改原有类会越来越危险:改一处,炸一片。
于是前辈程序员们发现:如果提前让类之间形成某种固定的、松耦合的连接关系(比如不直接new,而是通过一个工厂;不直接调用,而是通过一个观察者列表),就能让未来的修改变得局部化、可插拔。
设计模式不是追求结构美,而是管理变化、降低修改成本的工具。
2.3 从“代码坏味道”到“模式重构”:一个对比表
理解“为什么需要”,最好的方式是看一个反例和一个正例的对比。下面这个表格,展示了常见坏味道与对应模式拓扑的关系:
| 代码坏味道 | 拓扑问题 | 使用的模式 | 拓扑变化 |
|---|---|---|---|
大量if-else根据类型做不同事情 | 逻辑与调用者强耦合 | 策略模式 | 从过程式分支 → 星型拓扑(接口+多个实现) |
| 需要给一个类动态增加职责(缓存、日志等) | 继承会导致类爆炸 | 装饰器模式 | 链式拓扑:装饰器与被装饰者实现同一接口,且装饰器组合另一个该接口对象 |
| 多个模块需要监听某个对象的状态变化 | 直接调用导致硬编码依赖 | 观察者模式 | 星型 + 单向依赖:主题维护观察者列表,观察者实现统一接口 |
| 需要适配一个第三方库的接口 | 接口不匹配,改不动三方库 | 适配器模式 | 菱形拓扑:客户端依赖目标接口,适配器实现目标接口并持有被适配者 |
设计模式不是炫技,而是对特定坏味道的标准手术方案。
三、掌握设计模式,实际上掌握的是什么
很多人学完设计模式后,只会背名字和类图,到了实际写代码还是不会用。
因为真正的掌握,不是记住了名词,而是下面三层能力:
| 层次 | 名称 | 具体内容 | 例子 |
|---|---|---|---|
| L1 | 识别与匹配 | 看到代码/需求,能认出该用哪个模式 | “这里有多个算法需要切换 → 策略模式” |
| L2 | 拓扑实现 | 能画出并写出该模式的类结构(接口、继承、组合) | 用 Java/Python 实现一个观察者模式 |
| L3 | 权衡与变体 | 知道模式带来的代价(增加类数量、降低可读性)以及何时不用 | 观察者模式容易造成通知顺序混乱,有时用事件总线更好 |
所以,掌握设计模式 = 脑子里储存了足够多的、针对不同变化场景的类拓扑结构模型,并且能在合适的场景下调用、改造它们。
它不神秘,它是一项非常实在的技能:知道多个类之间可以怎么连接,并且知道每种连接的代价和收益。
四、再补充几个澄清,帮你避免被误导
很多人在网上看设计模式文章,容易产生误解。这里澄清三点:
设计模式不是代码库,不是复制粘贴就能用的。
它是结构思想,需要你根据当前场景调整类名、关系、粒度。设计模式不是只能用于“大项目”。
小项目中过度使用会增加复杂度,但理解它在复杂场景的价值,能让你在遇到复杂需求时做出更合理的决策。设计模式 ≠ 类图上的那些箭头。
真正的模式包含问题、上下文、权衡、已知用途。拓扑结构只是它的骨架,不是全部。
五、一个彩蛋:设计模式与软件架构模式的关系
如果你还想再拔高一下视角,可以知道这个区别:
- 设计模式:解决局部(几个类)的拓扑结构问题。
- 架构模式:解决整个系统(多个模块/子系统)的组织结构,比如 MVC、微服务、分层架构。
两者是“建材”与“建筑”的关系。举个例子:一个 MVC 架构里,可能大量使用观察者模式(视图观察模型的变化),以及策略模式(控制器选择不同的行为策略)。
理解这个关系,能帮你把设计模式放到更大的软件设计图景中。
写在最后
设计模式不是什么高不可攀的秘籍。它就是在面向对象编程发展了几十年后,人们不断踩坑、总结出来的一系列类的拓扑结构方案。
- 它是为了解决“需求总在变化,但代码不能总推倒重来”这个现实问题而生的。
- 它的本质是若干个类通过继承、实现、组合三种关系连接成的特定形状。
- 掌握它,就是掌握了一套在不同场景下如何组织类间关系的经验库。
下次当你再看到if-else满天飞,或者新加一个功能就要改一堆老代码时,不妨停下来想一想:
我是不是可以换一种类的拓扑结构,让代码变得更友好?
那样,你就真正开始理解设计模式了。
如果你觉得这篇文章对你有用,欢迎收藏、转发。也可以留言告诉我你想看哪个具体模式的“拓扑结构拆解”,我会继续写。
