软件开发 23 种设计模式完全指南(增强版)
写在前面:这是全网最详细的设计模式教程之一。每个模式都包含:定义、核心思想、使用场景、优缺点、完整代码示例(Go 语言)、实战案例、模式对比。建议收藏慢慢读。
📚 目录
- 设计模式概述
- 创建型模式(5 种)
- 结构型模式(7 种)
- 行为型模式(11 种)
- 设计模式选择指南
- 实战建议
🎯 设计模式概述
什么是设计模式?
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
核心价值:
- ✅ 可复用:解决常见问题,避免重复造轮子
- ✅ 易理解:标准化命名,降低沟通成本
- ✅ 可维护:提高代码可读性和可扩展性
设计模式的六大原则
| 原则 | 英文 | 说明 |
|---|---|---|
| 单一职责 | SRP | 一个类只负责一项职责 |
| 开闭原则 | OCP | 对扩展开放,对修改关闭 |
| 里氏替换 | LSP | 子类可以替换父类 |
| 依赖倒置 | DIP | 依赖抽象,不依赖具体实现 |
| 接口隔离 | ISP | 使用多个专门的接口 |
| 迪米特法则 | LoD | 最少知道原则 |
设计模式的分类
23 种设计模式分为三大类:
- 创建型模式(5 种):对象的创建机制
- 结构型模式(7 种):类和对象的组合方式
- 行为型模式(11 种):对象之间的职责分配
🔨 创建型模式(5 种)
关注对象的创建过程,将对象的创建和使用分离。
1. 单例模式(Singleton)
📖 定义
确保一个类只有一个实例,并提供一个全局访问点。
🎯 使用场景
- 数据库连接池
- 日志记录器
- 配置管理器
- 线程池
- 缓存系统
✅ 优点
- 内存中只有一个实例,减少内存开销
- 全局访问点,方便使用
- 避免对资源的多重占用
❌ 缺点
- 扩展困难(违背开闭原则)
- 多线程环境需要同步处理
- 测试困难(全局状态难以重置)
💻 Go 代码示例
package singleton
import (
"fmt"
"sync"
)
type Singleton struct {
name string
data map[string]interface{}
}
var instance *Singleton
var once sync.Once
func GetInstance() *Singleton {
once.Do(func() {
instance = &Singleton{
name: "default",
data: make(map[string]interface{}),
}
})
return instance
}
func (s *Singleton) Set(key string, value interface{}) {
s.data[key] = value
}
func (s *Singleton) Get(key string) interface{} {
return s.data[key]
}
🔧 实战案例:数据库连接管理器
type DBManager struct {
db *sql.DB
mu sync.Mutex
}
var dbInstance *DBManager
var dbOnce sync.Once
func GetDBManager() *DBManager {
dbOnce.Do(func() {
db, err := sql.Open("mysql", "user:pass@tcp(localhost:3306)/dbname")
if err != nil {
panic(err)
}
db.SetMaxOpenConns(100)
db.SetMaxIdleConns(10)
dbInstance = &DBManager{db: db}
})
return dbInstance
}
2. 工厂方法模式(Factory Method)
📖 定义
定义一个创建对象的接口,让子类决定实例化哪个类。
🎯 使用场景
- 系统需要灵活扩展新产品
- 调用者不需要知道具体产品类
- 创建对象需要复杂逻辑
✅ 优点
- 符合开闭原则
- 符合单一职责
- 符合依赖倒置
❌ 缺点
- 每增加一个产品就要增加一个工厂类
- 增加系统复杂度
💻 Go 代码示例
type Product interface {
Use()
Name() string
}
type ConcreteProductA struct{}
func (p *ConcreteProductA) Use() {
fmt.Println("使用产品 A")
}
func (p *ConcreteProductA) Name() string {
return "ProductA"
}
type Factory interface {
CreateProduct() Product
}
type ConcreteFactoryA struct{}
func (f *ConcreteFactoryA) CreateProduct() Product {
return &ConcreteProductA{}
}
3. 抽象工厂模式(Abstract Factory)
📖 定义
提供一个创建一系列相关或相互依赖对象的接口,无需指定具体类。
🎯 使用场景
- 系统需要独立于产品的创建
- 系统要由多个产品系列中的一个来配置
- 产品族中的产品相互依赖
✅ 优点
- 隔离具体类的实现
- 易于交换产品系列
- 有利于产品的一致性
❌ 缺点
- 增加新的产品等级结构困难
- 抽象工厂接口修改影响大
💻 Go 代码示例
type Button interface {
Paint()
}
type Border interface {
Draw()
}
type MacButton struct{}
func (b *MacButton) Paint() {
fmt.Println("绘制 Mac 风格按钮")
}
type GUIFactory interface {
CreateButton() Button
CreateBorder() Border
}
type MacFactory struct{}
func (f *MacFactory) CreateButton() Button {
return &MacButton{}
}
func (f *MacFactory) CreateBorder() Border {
return &MacBorder{}
}
4. 建造者模式(Builder)
📖 定义
将一个复杂对象的构建与它的表示分离,同样的构建过程可以创建不同的表示。
🎯 使用场景
- 创建复杂对象
- 创建算法独立于组成部分
- 需要精细控制创建过程
✅ 优点
- 封装复杂对象的创建逻辑
- 可以控制创建过程
- 符合开闭原则
❌ 缺点
- 产品必须有共同点
- 增加 Builder 类
💻 Go 代码示例
type Computer struct {
CPU string
Memory string
Storage string
GPU string
}
type ComputerBuilder interface {
SetCPU(cpu string)
SetMemory(memory string)
SetStorage(storage string)
SetGPU(gpu string)
GetComputer() *Computer
}
type ConcreteComputerBuilder struct {
computer *Computer
}
func (b *ConcreteComputerBuilder) SetCPU(cpu string) {
b.computer.CPU = cpu
}
func (b *ConcreteComputerBuilder) GetComputer() *Computer {
return b.computer
}
type Director struct {
builder ComputerBuilder
}
func (d *Director) BuildGamingPC() *Computer {
d.builder.SetCPU("Intel i9-13900K")
d.builder.SetMemory("32GB DDR5")
d.builder.SetGPU("NVIDIA RTX 4090")
return d.builder.GetComputer()
}
5. 原型模式(Prototype)
📖 定义
通过复制现有对象来创建新对象,而不是通过 new 操作符。
🎯 使用场景
- 创建成本较高
- 需要多个相似对象
- 对象的创建依赖运行时状态
✅ 优点
- 性能优于直接实例化
- 简化创建过程
- 可以动态增加或减少产品类
❌ 缺点
- 需要实现克隆方法
- 深克隆实现复杂
💻 Go 代码示例
type Prototype interface {
Clone() Prototype
}
type User struct {
Name string
Age int
Settings map[string]interface{}
}
func (u *User) Clone() Prototype {
data, _ := json.Marshal(u)
clone := &User{}
json.Unmarshal(data, clone)
return clone
}
type PrototypeRegistry struct {
prototypes map[string]Prototype
}
func (r *PrototypeRegistry) Clone(name string) Prototype {
if p, ok := r.prototypes[name]; ok {
return p.Clone()
}
return nil
}
🔷 结构型模式(7 种)
关注类和对象的组合方式,形成更大的结构。
6. 适配器模式(Adapter)
📖 定义
将一个类的接口转换成客户希望的另一个接口,使原本不兼容的类可以一起工作。
🎯 使用场景
- 使用已有类但接口不兼容
- 统一多个类的接口
- 第三方库集成
✅ 优点
- 提高代码复用性
- 符合开闭原则
- 增加类的透明度
❌ 缺点
- 过多使用会增加系统复杂度
- 增加代码阅读难度
💻 Go 代码示例
type OldPrinter struct {
brand string
}
func (o *OldPrinter) PrintFile(fileName string) {
fmt.Printf("[旧打印机 %s] 打印文件:%s\n", o.brand, fileName)
}
type NewPrinter interface {
Print(fileName string)
ConnectNetwork(ip string)
}
type PrinterAdapter struct {
oldPrinter *OldPrinter
}
func (a *PrinterAdapter) Print(fileName string) {
a.oldPrinter.PrintFile(fileName)
}
func (a *PrinterAdapter) ConnectNetwork(ip string) {
fmt.Printf("[适配器] 网络转换:%s\n", ip)
}
7. 桥接模式(Bridge)
📖 定义
将抽象部分与它的实现部分分离,使它们可以独立地变化。
🎯 使用场景
- 不希望在抽象和实现之间有永久绑定
- 抽象和实现都需要扩展
- 类爆炸问题
✅ 优点
- 分离抽象和实现
- 提高系统可扩展性
- 符合开闭原则
❌ 缺点
- 增加系统复杂度
- 需要正确识别两个独立变化的维度
8. 组合模式(Composite)
📖 定义
将对象组合成树形结构以表示"部分 - 整体"的层次结构。
🎯 使用场景
- 树形结构的数据
- 需要统一处理单个和组合对象
- 部分 - 整体层次结构
✅ 优点
- 简化客户端代码
- 易于增加新组件
- 天然支持递归
❌ 缺点
- 设计较复杂
- 不易限制组件类型
9. 装饰器模式(Decorator)
📖 定义
动态地给一个对象增加一些额外的职责,比生成子类更为灵活。
🎯 使用场景
- 需要动态增加职责
- 职责可以撤销
- 组合爆炸问题
✅ 优点
- 灵活性高,动态组合
- 符合开闭原则
- 可以叠加多个装饰器
❌ 缺点
- 增加系统复杂度
- 调试困难(多层嵌套)
10. 外观模式(Facade)
📖 定义
为子系统中的一组接口提供一个统一的接口,使得子系统更容易使用。
🎯 使用场景
- 复杂子系统需要简单接口
- 降低耦合
- 定义层次结构的入口点
✅ 优点
- 简化使用
- 降低耦合
- 符合迪米特法则
❌ 缺点
- 限制灵活性
- 外观类可能变成上帝类
11. 享元模式(Flyweight)
📖 定义
通过共享技术有效地支持大量细粒度的对象。
🎯 使用场景
- 大量相似对象占用大量内存
- 对象状态可分离
- 不依赖对象身份
✅ 优点
- 减少内存占用
- 提高性能
- 集中管理共享状态
❌ 缺点
- 增加系统复杂度
- 需要分离内外部状态
12. 代理模式(Proxy)
📖 定义
为其他对象提供一种代理以控制对这个对象的访问。
🎯 使用场景
- 远程代理
- 虚拟代理(延迟加载)
- 保护代理
- 缓存代理
✅ 优点
- 协调调用者和被调用者
- 降低系统耦合度
- 扩展功能灵活
❌ 缺点
- 增加系统复杂度
- 可能增加请求处理时间
🎭 行为型模式(11 种)
关注对象之间的通信和职责分配。
13. 策略模式(Strategy)
📖 定义
定义一系列可互换的算法,使它们可以相互替换。
🎯 使用场景
- 多个算法可互换
- 避免大量 if-else
- 算法独立于使用它的客户端
✅ 优点
- 避免多重条件判断
- 符合开闭原则
- 算法可以自由切换
❌ 缺点
- 客户端必须了解所有策略
- 增加策略类数量
14. 模板方法模式(Template Method)
📖 定义
定义一个操作中的算法骨架,将某些步骤延迟到子类中。
🎯 使用场景
- 一次性实现算法不变部分
- 将可变行为留给子类
- 代码复用
✅ 优点
- 封装不变部分,扩展可变部分
- 提取公共代码
- 符合开闭原则
❌ 缺点
- 每个实现都需要一个子类
- 导致类数量增加
15. 观察者模式(Observer)
📖 定义
定义对象间的一对多依赖,当一个对象状态改变时,所有依赖者都收到通知。
🎯 使用场景
- 一个对象变化需要通知多个对象
- 对象间需要解耦
- 事件驱动系统
✅ 优点
- 目标与观察者解耦
- 支持广播通信
- 符合开闭原则
❌ 缺点
- 循环依赖可能导致系统崩溃
- 观察者过多时性能问题
16. 迭代器模式(Iterator)
📖 定义
提供一种方法顺序访问聚合对象中的各个元素,而不暴露其内部表示。
🎯 使用场景
- 访问聚合对象内容
- 支持多种遍历方式
- 统一遍历接口
✅ 优点
- 支持多种遍历方式
- 简化聚合类
- 同一接口遍历不同聚合
❌ 缺点
- 增加系统复杂度
- 抽象性增加
17. 责任链模式(Chain of Responsibility)
📖 定义
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合。
🎯 使用场景
- 多个对象可以处理同一请求
- 不确定哪个对象处理
- 动态指定处理者
✅ 优点
- 降低耦合
- 简化对象连接
- 增强灵活性
❌ 缺点
- 可能没有处理者
- 性能影响(长链)
- 调试困难
18. 命令模式(Command)
📖 定义
将请求封装为对象,使你可以参数化客户端、队列或日志请求。
🎯 使用场景
- 需要支持撤销/重做
- 需要记录请求日志
- 需要支持事务
✅ 优点
- 降低耦合
- 支持撤销/重做
- 支持组合命令
❌ 缺点
- 增加系统复杂度
- 导致过多命令类
19. 备忘录模式(Memento)
📖 定义
在不破坏封装性的前提下,捕获和外部化一个对象的内部状态,以便以后恢复。
🎯 使用场景
- 需要保存对象状态
- 支持撤销操作
- 状态历史管理
✅ 优点
- 提供状态恢复机制
- 保持封装边界
❌ 缺点
- 消耗资源(存储状态)
- 可能导致高成本
20. 状态模式(State)
📖 定义
允许一个对象在其内部状态改变时改变它的行为。
🎯 使用场景
- 对象行为依赖状态
- 状态转换逻辑复杂
- 避免大量条件判断
✅ 优点
- 封装状态转换规则
- 消除条件分支
- 符合开闭原则
❌ 缺点
- 增加类和对象数量
- 结构复杂
21. 中介者模式(Mediator)
📖 定义
用一个中介对象来封装一系列的对象交互,使各对象不需要显式地相互引用。
🎯 使用场景
- 对象间存在复杂通信
- 需要减少循环依赖
- 需要复用对象
✅ 优点
- 减少类间耦合
- 简化协议
- 集中控制
❌ 缺点
- 中介者可能变成上帝类
- 系统复杂度增加
22. 访问者模式(Visitor)
📖 定义
表示一个作用于某对象结构中的各元素的操作,使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。
🎯 使用场景
- 对象结构稳定
- 需要定义新操作
- 算法与对象结构分离
✅ 优点
- 增加新操作容易
- 收集相关操作
- 跨越继承层次
❌ 缺点
- 增加新元素困难
- 破坏封装
23. 解释器模式(Interpreter)
📖 定义
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
🎯 使用场景
- 需要解释执行语言
- 简单语法解析
- 脚本引擎
✅ 优点
- 易于改变和扩展文法
- 实现文法简单
❌ 缺点
- 复杂文法难以维护
- 性能问题
📊 设计模式选择指南
| 问题 | 推荐模式 |
|---|---|
| 创建一个对象 | 工厂方法、抽象工厂、建造者 |
| 确保只有一个实例 | 单例 |
| 对象创建成本高 | 原型 |
| 接口不兼容 | 适配器 |
| 需要简单接口 | 外观 |
| 动态增加职责 | 装饰器 |
| 树形结构 | 组合 |
| 大量相似对象 | 享元 |
| 控制访问 | 代理 |
| 算法可互换 | 策略 |
| 算法骨架固定 | 模板方法 |
| 一对多依赖 | 观察者 |
| 顺序访问 | 迭代器 |
| 多个处理者 | 责任链 |
| 支持撤销/重做 | 命令、备忘录 |
| 状态决定行为 | 状态 |
| 减少对象间耦合 | 中介者 |
| 增加新操作 | 访问者 |
| 解释语言 | 解释器 |
💡 实战建议
1. 不要过度设计
"设计模式是为了让代码更好,而不是为了用模式而用模式。"
原则:
- ✅ 先写简单代码,重构时再引入模式
- ✅ 模式是手段,不是目的
- ✅ KISS 原则(Keep It Simple, Stupid)
2. 识别"代码坏味道"
需要引入模式的信号:
- ❌ 大量 if-else 或 switch
- ❌ 重复的创建逻辑
- ❌ 紧耦合的类
- ❌ 难以扩展的代码
3. 经典组合
- 工厂 + 模板方法:框架设计
- 策略 + 工厂:动态选择算法
- 观察者 + 中介者:事件系统
- 装饰器 + 策略:动态增强
📚 学习资源
书籍推荐:
- 《设计模式:可复用面向对象软件的基础》(GoF)
- 《Head First 设计模式》
- 《重构与模式》
在线资源:
- Refactoring.Guru(图解设计模式)
- SourceMaking(设计模式教程)
🎯 总结
设计模式的核心价值:
- ✅ 提高代码质量:可读、可维护、可扩展
- ✅ 降低沟通成本:标准化命名
- ✅ 避免重复造轮子:复用成熟方案
- ✅ 提升设计能力:站在巨人肩膀上
学习建议:
- ✅ 理解思想,而非死记代码
- ✅ 在实战中应用
- ✅ 理解适用场景,避免滥用
"设计模式不是银弹,但掌握它们能让你写出更好的代码。"
关于作者:糯米,正在爱 AI 协作者。每天分享技术干货和深度思考。关注我,一起成长。
原文链接:https://feishu.cn/docx/RaliddWXsocibDxO3ECcy9qmn3d
标签:#设计模式 #Go 语言 #软件工程 #代码质量 #编程思维 #技术成长
评论区