设计模式(类的拓扑结构)(描述总纲)
写这系列的文章,就两个目的
了解什么是设计模式,有哪几种
理解什么时候使用这些设计模式结构
这里系统的讲一下设计模式
围绕下面几个核心的问题
1.什么是设计模式
2.为什么会有设计模式
3.有哪些设计模式
4.什么时候可以去使用设计模式
具体设计模式类型:
(1)创建型模式(5种)
核心:解决对象创建过程的灵活性与耦合问题。
- 单例(Singleton)
- 工厂方法(Factory Method)
- 抽象工厂(Abstract Factory)
- 建造者(Builder)
- 原型(Prototype)
(2)结构型模式(7种)
核心:通过组合类或对象,形成更大的结构。
- 适配器(Adapter)
- 桥接(Bridge)
- 组合(Composite)
- 装饰器(Decorator)
- 外观(Facade)
- 享元(Flyweight)
- 代理(Proxy)
(3)行为型模式(11种)
核心:处理算法与对象间职责的分配、通信。
- 责任链(Chain of Responsibility)
- 命令(Command)
- 解释器(Interpreter)
- 迭代器(Iterator)
- 中介者(Mediator)
- 备忘录(Memento)
- 观察者(Observer)
- 状态(State)
- 策略(Strategy)
- 模板方法(Template Method)
- 访问者(Visitor)
设计模式(类的拓扑结构)—— 描述总纲
写作目的
本系列文章围绕两个核心目标展开:
- 了解什么是设计模式,以及有哪些设计模式
- 理解在什么场景下可以使用这些设计模式结构
核心问题
本系列将系统性地回答以下四个问题:
| 问题 | 简述 |
|---|---|
| 什么是设计模式 | 定义、本质、价值 |
| 为什么会有设计模式 | 产生背景、解决什么痛点 |
| 有哪些设计模式 | 三类23种模式的全景图 |
| 什么时候用 | 每种模式的适用场景与问题特征 |
一、什么是设计模式
设计模式是对软件开发中特定场景下重复出现的类与对象拓扑结构的总结与抽象。
通俗地说,设计模式就是一套被验证过的、可复用的设计经验。它不是可以直接落地的代码库,而是一种解法模板——描述了在什么情况下,类和对象应该如何组织、如何交互、如何承接职责,从而形成一个可复用的、优雅的结构。
从类的拓扑结构这个视角看,设计模式本质上是在回答三个问题:
- 类与类之间如何连接(继承、组合、依赖)
- 对象之间如何通信(调用、通知、回调)
- 职责如何在对象之间分配(谁做什么、谁不做什么)
一句话定义:设计模式 = 特定问题场景下 + 经过验证的 + 类/对象拓扑结构
二、为什么会有设计模式
设计模式的产生源于软件开发中的重复性痛点:
2.1 核心痛点
| 痛点 | 表现 |
|---|---|
| 紧耦合 | 修改一处代码,牵连一片 |
| 重复造轮子 | 类似的问题,类似的解法,每次都重新设计 |
| 可维护性差 | 代码难以理解、难以扩展、难以测试 |
| 变动应对能力弱 | 需求一变化,架构就崩塌 |
2.2 设计模式解决的三个根本问题
- 封装变化—— 找到代码中可能变化的部分,将其隔离
- 面向接口/抽象编程,而非实现—— 依赖抽象而非具体类
- 组合优先于继承—— 运行时动态组合行为,而非静态绑定
2.3 历史背景
- 1994年,GoF(四人组)出版《设计模式:可复用面向对象软件的基础》
- 借鉴了建筑学中 Christopher Alexander 的“模式语言”思想
- 总结了23种面向对象设计模式,成为软件开发领域的经典
三、有哪些设计模式
GoF 将23种设计模式分为三大类:
(1)创建型模式(5种)
核心关注点:对象的创建过程。
| 模式 | 一句话说明 | 关键拓扑特征 |
|---|---|---|
| 单例(Singleton) | 一个类只有一个实例 | 类持有自己的静态实例 |
| 工厂方法(Factory Method) | 子类决定实例化哪个类 | 继承 + 多态 |
| 抽象工厂(Abstract Factory) | 创建一系列相关对象 | 多个工厂接口 + 产品族 |
| 建造者(Builder) | 分步构建复杂对象 | 导向器 + 建造器分离 |
| 原型(Prototype) | 通过拷贝原型创建对象 | 克隆方法 + 原型注册表 |
共性:将“创建什么对象”与“如何使用对象”解耦。
(2)结构型模式(7种)
核心关注点:类与对象的组合方式,形成更大的结构。
| 模式 | 一句话说明 | 关键拓扑特征 |
|---|---|---|
| 适配器(Adapter) | 转换接口,让不兼容的类协同工作 | 包装器 + 接口转换 |
| 桥接(Bridge) | 将抽象部分与实现部分分离 | 抽象类持有实现类引用 |
| 组合(Composite) | 统一对待单个对象和组合对象 | 树形结构 + 统一接口 |
| 装饰器(Decorator) | 动态添加职责 | 包装器链 + 透明接口 |
| 外观(Facade) | 为子系统提供统一入口 | 门面类 + 子系统调用 |
| 享元(Flyweight) | 共享细粒度对象,节省内存 | 对象池 + 内外部状态分离 |
| 代理(Proxy) | 控制对象访问 | 代理类持有真实对象引用 |
共性:通过组合而非继承,实现结构的灵活扩展。
(3)行为型模式(11种)
核心关注点:对象之间的职责分配、算法封装与通信方式。
| 模式 | 一句话说明 | 关键拓扑特征 |
|---|---|---|
| 责任链(Chain of Responsibility) | 请求沿链条传递,直到被处理 | 链表结构 + 处理器链 |
| 命令(Command) | 将请求封装为对象 | 命令对象 + 调用者与接收者解耦 |
| 解释器(Interpreter) | 定义语法规则并解释执行 | 抽象语法树 + 递归解释 |
| 迭代器(Iterator) | 顺序访问聚合对象内部元素 | 迭代器接口 + 聚合接口 |
| 中介者(Mediator) | 对象间通过中介者通信,而非直接引用 | 星型拓扑(中介者为中心) |
| 备忘录(Memento) | 保存和恢复对象状态 | 发起人 + 备忘录 + caretaker |
| 观察者(Observer) | 状态变化时自动通知依赖者 | 发布-订阅结构 |
| 状态(State) | 状态变化时改变对象行为 | 状态对象 + 上下文持有当前状态 |
| 策略(Strategy) | 封装可互换的算法族 | 策略接口 + 上下文持有策略引用 |
| 模板方法(Template Method) | 定义算法骨架,子类实现具体步骤 | 继承 + 钩子方法 |
| 访问者(Visitor) | 在不改变元素类的前提下定义新操作 | 双分派 + 访问者与元素双向依赖 |
共性:将行为变化封装起来,实现算法的可替换和职责的灵活分配。
四、什么时候使用设计模式
使用设计模式需要克制,而非滥用。以下是指南:
4.1 适用场景的判断标准
| 判断依据 | 说明 |
|---|---|
| 是否出现频繁变化 | 某类需求经常变动,需要隔离变化点 |
| 是否存在重复代码 | 相似逻辑出现在多个地方,需要抽象 |
| 耦合度是否过高 | 修改一个类导致多个类需要改动 |
| 扩展性是否需要提升 | 未来可能需要增加新功能,希望不修改现有代码 |
| 复杂度是否需要管理 | 单个类承担了过多职责,需要拆分 |
4.2 不同场景下看哪个类别
| 遇到的问题 | 优先查看的模式类别 |
|---|---|
| 对象创建过程复杂、希望统一创建方式 | 创建型模式 |
| 类与类之间接口不匹配、需要适配 | 结构型模式 |
| 对象数量庞大、需要节省内存 | 结构型模式(享元) |
| 对象行为随状态/策略变化 | 行为型模式(状态/策略) |
| 对象之间需要解耦通信 | 行为型模式(观察者/中介者) |
| 请求需要按顺序被多个处理器处理 | 行为型模式(责任链) |
| 需要记录操作历史以支持撤销 | 行为型模式(命令/备忘录) |
4.3 设计模式不是银弹
不要使用设计模式的场景:
- 功能简单、不会变化 —— 引入模式反而增加复杂度
- 团队对模式不熟悉 —— 模式带来的沟通成本可能大于收益
- 性能敏感的核心路径 —— 某些模式(如装饰器、代理)可能引入额外开销
原则:先写简单直接的代码,当变化和复杂度真正出现时,再用设计模式重构。
五、系列文章结构预告
本系列将按以下顺序展开:
- 总纲(本篇)—— 全景式认知框架
- 创建型模式(5篇,每篇一个模式)
- 结构型模式(7篇)
- 行为型模式(11篇)
- 总结——模式之间的关系、如何组合使用、反模式警示
每篇具体模式文章将遵循统一结构:
- 问题场景(什么时候用)
- 拓扑结构图(类图/对象图)
- 核心代码骨架
- 与其他模式的对比
- 实际案例
总结速查表
| 类别 | 数量 | 核心问题 | 一句话特征 |
|---|---|---|---|
| 创建型 | 5 | 对象怎么创建? | 将创建与使用分离 |
| 结构型 | 7 | 对象怎么组合? | 通过组合形成更大结构 |
| 行为型 | 11 | 对象怎么通信? | 封装行为与交互逻辑 |
最终目的:理解这些类的拓扑结构之后,你不再需要死记硬背23个模式的名字,而是能够在遇到问题时,自然地想到——“这里应该用某种创建型模式来隔离变化”,或者“这里需要某个结构型模式来适配接口”。