一、总结
模式类型 | 模式名称 | 核心意图 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|---|
创建型 | 工厂模式 | 定义创建对象的接口,由子类决定实例化哪个类 | 解耦创建与使用,便于扩展 | 新增产品需新增工厂,类数量增多 | 产品种类较少,创建逻辑较复杂时(如日志工厂、数据库连接工厂) |
抽象工厂模式 | 提供创建一系列相关/依赖对象的接口,无需指定具体类 | 保证产品族一致性,隔离具体类 | 新增产品族困难,结构复杂 | 需创建多个相关产品(如UI组件库:Windows/Mac风格的按钮、文本框) | |
单例模式 | 保证类仅有一个实例,并提供全局访问点 | 控制资源访问,避免重复创建 | 破坏单一职责,可能导致全局状态,测试困难 | 全局资源管理(如配置中心、线程池、日志器) | |
原型模式 | 通过复制现有实例创建新对象,避免重复初始化 | 提高创建效率,简化复杂对象创建 | 深拷贝复杂对象时实现困难 | 对象创建成本高(如大对象、数据库查询结果),或需动态生成相似对象时 | |
建造者模式 | 将复杂对象的构建与表示分离,分步构建对象 | 控制构建过程,灵活组合部件 | 产品差异大时,建造者类膨胀 | 复杂对象构建(如文档、报表、汽车组装) | |
结构型 | 适配器模式 | 将一个类的接口转换为客户端期望的另一个接口,解决兼容性问题 | 复用现有类,透明适配,不修改源码 | 过多适配器会增加系统复杂度 | 接口不兼容的类需要协同工作(如老系统对接新接口、第三方库适配) |
桥接模式 | 将抽象与实现分离,使两者可独立变化 | 减少类爆炸,提高扩展性 | 增加系统抽象层次,理解难度加大 | 多维度变化的场景(如形状+颜色、消息类型+发送方式) | |
装饰器模式 | 动态给对象添加额外功能,不改变其结构 | 灵活扩展功能,比继承更灵活 | 多层装饰可能导致调试困难 | 需动态扩展功能且不希望修改原类(如IO流包装、日志增强、权限控制) | |
组合模式 | 将对象组合成树形结构,统一处理单个对象和组合对象 | 简化客户端代码,符合开闭原则 | 设计复杂,限制组合对象类型时较麻烦 | 树形结构场景(如文件系统、组织架构、菜单导航) | |
外观模式 | 为子系统提供统一接口,简化访问复杂度 | 降低耦合,简化调用 | 可能成为“上帝类”,违背单一职责 | 子系统复杂,需对外提供简单接口(如第三方SDK封装、系统入口封装) | |
享元模式 | 复用细粒度对象,减少内存占用 | 减少对象数量,提高性能 | 引入工厂和池,增加系统复杂度 | 大量相似对象场景(如文字处理中的字符、游戏中的粒子效果、缓存) | |
代理模式 | 为对象提供代理,控制对原对象的访问 | 隔离访问,增强功能(如权限、缓存、日志) | 增加代理层,可能降低性能 | 需控制访问(如远程代理、安全代理、延迟加载) | |
行为型 | 职责链模式 | 将请求沿处理链传递,直到被处理 | 解耦请求发送者与接收者,动态调整链 | 可能导致请求未被处理,调试困难 | 多对象可处理同一请求(如日志级别过滤、审批流程、异常处理) |
命令模式 | 将请求封装为对象,使请求可参数化、队列化 | 支持撤销/重做,解耦调用者与执行者 | 类数量增多,命令复杂时理解困难 | 需记录请求、支持事务(如GUI按钮命令、任务调度、命令日志) | |
解释器模式 | 定义语言语法,构建解释器解析句子 | 易于扩展语法,灵活处理特定语言 | 复杂语法会导致类膨胀,效率低 | 简单语法解析(如表达式计算、规则引擎、正则表达式) | |
迭代器模式 | 提供遍历聚合对象的统一接口,不暴露内部结构 | 统一遍历方式,简化客户端 | 增加系统复杂度,对于简单集合可能多余 | 需遍历不同聚合结构(如集合框架、树结构遍历) | |
中介者模式 | 用中介者封装对象间的交互,减少直接耦合 | 简化对象关系,集中控制交互 | 中介者可能成为瓶颈,复杂度集中 | 对象间交互复杂(如聊天室、GUI组件交互、分布式系统协调) | |
备忘录模式 | 捕获对象状态并保存,以便后续恢复 | 实现状态恢复,不破坏封装 | 消耗资源,状态过大时效率低 | 需要撤销/回滚操作(如编辑器撤销、游戏存档、事务回滚) | |
观察者模式 | 定义一对多依赖,一个对象变化时通知所有依赖者 | 解耦主题与观察者,支持动态订阅 | 通知顺序不确定,可能导致循环依赖 | 事件通知场景(如消息订阅、GUI事件响应、状态监控) | |
状态模式 | 允许对象在内部状态变化时改变行为,仿佛修改了类 | 封装状态转换,清晰管理不同状态行为 | 状态多时分支多,类数量增加 | 对象行为依赖于状态(如订单状态流转、电梯状态控制、有限状态机) | |
策略模式 | 定义算法家族,封装并动态切换 | 算法可替换,避免多重条件判断 | 客户端需知道所有策略,增加理解成本 | 多种算法可选(如排序算法、支付方式、折扣策略) | |
模板方法模式 | 定义算法骨架,子类实现具体步骤 | 复用骨架代码,强制算法结构 | 子类可能过多,修改骨架需修改抽象类 | 算法步骤固定但细节可变(如生命周期方法、报表生成步骤、流程模板) | |
访问者模式 | 分离数据结构与操作,在不修改类的情况下添加新操作 | 集中管理操作,易于扩展新操作 | 数据结构变化时需修改所有访问者,违反开闭 | 稳定的数据结构+多变的操作(如文档解析、AST语法树分析、报表统计) |
二、分类框架
23 种模式本质是按解决的核心问题场景划分的,先明确分类能快速缩小选型范围,避免混乱
- 模式分类逻辑:
- 创建型:关注对象创建机制,解决“如何创建对象”的问题,降低耦合。
- 结构型:关注对象/类的组合关系,解决“如何组装对象”的问题,优化结构。
- 行为型:关注对象间的交互,解决“如何协作”的问题,优化行为。
- 常见模式组合:
- 抽象工厂 + 桥接模式:处理多维度产品变化(如跨平台组件库)。
- 装饰器 + 策略模式:动态扩展功能并切换算法(如带缓存的支付系统)。
- 观察者 + 中介者模式:处理复杂事件通知(如分布式系统中的事件总线)。
- 反模式警惕:
- 单例模式滥用导致全局状态污染。
- 装饰器过度嵌套导致“装饰地狱”。
- 中介者模式膨胀为“万能类”。
三、易混淆模式的核心区分
很多模式看似相似,实则解决的问题场景完全不同
易混淆组 | 核心区分点 |
---|---|
工厂方法 VS 抽象工厂 | 工厂方法:单产品族(如只造“手机”,不同工厂造“小米手机”“华为手机”); 抽象工厂:多产品族(如造“手机+平板”,一个工厂造“小米手机+小米平板”,另一个造“华为手机+华为平板”) |
策略模式 VS 状态模式 | 策略模式:主动选择算法(客户端决定用哪个策略,如“导航选步行/开车”); 状态模式:状态驱动行为(对象自身状态变化触发行为变化,如“订单从待支付→已支付→已发货”,状态变了行为自动变) |
装饰器模式 VS 代理模式 | 装饰器模式:增强功能(不改变原对象核心逻辑,只叠加新功能,如“给手机贴钢化膜(防刮)+戴壳(防摔)”); 代理模式:控制访问(替原对象做“权限校验、延迟加载、日志”等非功能逻辑,如“代理对象先校验用户权限,再调用原对象接口”) |
适配器模式 VS 外观模式 | 适配器模式:解决“不兼容”(让两个已有、不兼容的接口能一起工作,如“USB-C转HDMI适配器”); 外观模式:简化接口(给复杂子系统封装一个统一入口,如“智能家居中控,一键控制灯光+空调+窗帘”,子系统本身是兼容的) |
四、实践中的核心注意事项(避免踩坑)
- 设计模式不是“银弹”,拒绝过度设计
不要为了用模式而用模式!比如:简单的对象创建(如只new一个对象,无变化),没必要用工厂模式;只有一个类的系统,没必要用外观模式。过度使用会增加代码复杂度(如单例模式会增加测试难度,装饰器多层嵌套会让调试变难)。 - 优先遵循设计原则,再匹配模式
所有模式都是“设计原则的落地实现”:- 开闭原则(对扩展开放、对修改关闭)→ 装饰器、策略、观察者等;
- 单一职责(一个类只做一件事)→ 策略、职责链等;
- 依赖倒置(依赖抽象,不依赖具体)→ 工厂方法、抽象工厂等。
先明确“我要解决的问题违反了哪个原则”,再找对应的模式,比死记模式更高效。
- 模式可以组合使用(实际项目更常见)
单一模式难以解决复杂问题,常见组合场景:- 建造者模式 + 模板方法:建造者的“构建步骤”用模板方法固定,具体实现交给子类(如“造房子的步骤(打地基→砌墙→封顶)固定,子类实现‘盖别墅’‘盖公寓’的具体细节”);
- 观察者模式 + 中介者模式:多个观察者之间不直接通信,通过中介者转发消息(如“微信群聊,群成员(观察者)发消息,由群(中介者)转发给其他人,避免成员间耦合”);
- 单例模式 + 工厂模式:工厂本身做成单例(如“全局只有一个数据库连接工厂”)。
- 特殊模式的使用限制
- 单例模式:注意线程安全(C++需加锁,Java用枚举或静态内部类),且避免用于“有状态”的对象(如存储用户会话,会导致多用户数据混乱);
- 享元模式:仅适用于“大量重复、细粒度对象”(如围棋棋子、字符串常量池),否则缓存的开销可能大于收益;
- 解释器模式:只用于简单语法(如表达式解析),复杂语法(如SQL解析)建议用专业解析工具(ANTLR),避免手写解释器导致代码臃肿。
五、学习建议
- 先“理解场景”,再“记结构”:不要死记UML类图,先想“这个模式解决了什么痛点”(比如代理模式解决“不想让客户端直接访问原对象”的痛点),再看类图如何支撑这个痛点。
- 结合业务练手:比如“电商订单状态流转”用状态模式,“支付方式选择”用策略模式,“商品SKU创建(多属性组合)”用建造者模式,将模式和实际业务绑定记忆。
- 警惕“设计模式教条化”:小项目或简单场景,“能用if-else解决的,就不用策略模式”,过度追求设计优雅反而会降低开发效率。设计模式的核心是“平衡灵活性和复杂度”。