DESIGN PATTERNS · 编程思想 / 复用之道

设计模式速查

从创建型、结构型到行为型,梳理 12 个最常用的 GoF 设计模式及其适用场景,结合 Spring、JDK 等真实案例,帮你写出更可复用、更易维护的代码。

12经典模式 3大类型 35速查条目

📖 速查表

点击展开各小节

🏗️ 创建型设计模式
模式类型说明适用场景
Singleton 单例 创建型 确保一个类只有一个实例,并提供一个全局访问点。Java 中常用 enum 或双重检查锁定实现 配置管理器、线程池、数据库连接池、日志记录器
Factory 工厂 创建型 定义一个创建对象的接口,让子类决定实例化哪个类。将客户端与具体类解耦 日志框架(SLF4J)、DAO 工厂、跨平台 UI 组件创建
Builder 建造者 创建型 将复杂对象的构建与表示分离,允许通过相同的构建过程创建不同的表示。支持链式调用 Lombok @Builder、构造复杂配置对象、SQL 查询构建器
Prototype 原型 创建型 通过复制已有对象来创建新对象,而不是通过 new。Java 中实现 Cloneable 对象创建成本高、需要保留对象副本的场景(如缓存快照)
设计模式三分类
23 个经典模式按意图分三类:创建型管"怎么造",结构型管"怎么拼",行为型管"怎么协作"
🔌 结构型设计模式
模式类型说明适用场景
Adapter 适配器 结构型 将一个类的接口转换成客户端期望的另一个接口,让不兼容的类可以一起工作 兼容旧系统接口、不同日志框架统一封装、Spring MVC 处理器适配器
Proxy 代理 结构型 为另一个对象提供一个替身或占位符以控制对它的访问。分为静态代理和动态代理 Spring AOP(JDK 动态代理 / CGLIB)、延迟加载、权限控制、日志记录
Decorator 装饰器 结构型 动态地给对象添加额外的职责,比继承更灵活。Java I/O 流是经典示例 BufferedInputStream 包装 FileInputStream、Spring Security 过滤器链
🔄 行为型设计模式
模式类型说明适用场景
Observer 观察者 行为型 定义对象间一对多的依赖关系,当一个对象状态变化时,所有依赖者都会收到通知并自动更新 Spring 事件监听(ApplicationListener)、消息队列发布订阅、Vue 响应式系统
Strategy 策略 行为型 定义一系列算法,将每个算法封装起来,并使它们可以互相替换。算法独立于使用它的客户端 支付方式选择、排序策略、压缩算法、认证方式(OAuth/JWT/Basic)
Template Method 模板方法 行为型 在父类中定义算法的骨架,将一些步骤延迟到子类中实现。子类可以重定义算法的特定步骤 Spring 中的 JdbcTemplateRestTemplate、工作流引擎
Command 命令 行为型 将请求封装为对象,从而可以用不同的请求对客户进行参数化、队列请求或记录日志 操作撤销/重做、事务操作、任务队列、GUI 按钮回调
State 状态 行为型 允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类 订单状态流转、工作流审批、游戏角色状态
📜 六大设计原则

前五条缩写即 SOLID,加上迪米特与合成复用共七条;讲原则时重点讲清「违反信号」,比背定义更能体现理解。

原则缩写 / 类别一句话违反信号
单一职责原则 SRP S · 职责划分 一个类只应有一个引起它变化的原因,只做一件事 一个类同时负责格式化、网络请求与数据库存储,改任何功能都要动它
开闭原则 OCP O · 扩展方式 对扩展开放、对修改关闭:新增功能靠加代码,不改稳定的旧代码 每加一种支付方式都要改主流程里的 switch / if-else 判断
里氏替换原则 LSP L · 继承约束 所有使用父类的地方,换成子类后行为必须仍然正确 子类重写方法抛出 UnsupportedOperationException 或收紧前置条件
接口隔离原则 ISP I · 接口设计 不强迫客户端依赖它们用不到的方法,胖接口应拆细 实现类被迫给一堆用不到的接口方法写空实现
依赖倒置原则 DIP D · 依赖方向 依赖抽象(接口/抽象类)而非具体实现,高层不依赖低层细节 业务逻辑里直接 new MySQLDao(),换存储实现要改业务代码
迪米特法则 LoD 最少知识 · 耦合控制 只与直接朋友通信,不与「陌生对象」深入交互,降低类间耦合 链式深挖 a.getB().getC().doSomething(),中间类变化牵连一片
合成复用原则 CRP 组合优先 · 复用方式 优先用组合/聚合复用已有对象,其次才是继承 为了复用一个工具方法随意继承工具类,继承树越堆越高
🔗 模式选型与实战
条目类型 / 场景说明 / 辨析典型示例
代理 vs 装饰器 易混对比 结构型 结构几乎相同,意图不同:代理重在「控制访问」(权限、延迟、远程),装饰器重在「动态增强职责」 AOP 代理拦截方法调用;BufferedInputStream 层层包装增强读流
适配器 vs 桥接 易混对比 结构型 适配器是「事后补救」:让不兼容接口协同工作;桥接是「事前设计」:分离抽象与实现两个独立变化的维度 适配器统一多家日志框架 API;桥接如 JDBC Driver 架构(接口 ↔ 驱动实现)
Observer 观察者 JDK / Spring 实例 行为型 事件驱动解耦:发布方只管发事件,无需知道有哪些订阅方 Spring ApplicationEvent + @EventListener 事件机制、Guava EventBus
Strategy 策略 JDK / Spring 实例 行为型 同一接口多实现按条件注入切换,消灭 if-else 分支 Spring Resource 资源加载策略、Comparator 排序策略、支付渠道选择
Proxy 代理 JDK / Spring 实例 结构型 不修改目标类织入事务、日志等横切逻辑 Spring AOP:@Transactional 底层 JDK 动态代理 / CGLIB
Template Method 模板方法 JDK / Spring 实例 行为型 骨架固定(获取连接/异常处理/释放资源),差异步骤用回调或子类下沉 Spring JdbcTemplateRestTemplate、AQS 的模板骨架
🧑‍🍳 模式组合拳
单模式解决单点问题,真实系统靠组合:两两搭配各管一段,才是模式的正确打开方式。
组合协作方式实战示例
工厂 + 策略 最常用 启动时把全部策略注册进 Map(key → 实现),运行时按键取用,消除 if-else 分支 支付渠道路由:策略接口多实现 + 工厂按渠道码分发,新增渠道只加类不改主流程
装饰器 + 代理 结构同形、按意图分工:控制访问(鉴权/延迟/远程)选代理,叠加增强(缓冲/压缩)选装饰器,二者可串联 网关先代理做鉴权限流,请求进入服务后再用装饰器层层包装加日志与压缩
模板方法 + 钩子 框架骨架 父类定骨架流程,钩子方法(默认空实现或返回 true 的开关)让子类按需介入 AQS 把 acquire 定成模板、tryAcquire 留成钩子;Servlet 的 service 分发到 doGet/doPost
观察者 + 领域事件 聚合只管发布「已发生的事实」,监听方各自响应,模块之间零直接依赖 下单成功发 OrderPaid 事件:发通知、加积分、更新报表各挂各的监听,互不阻塞
组合 + 迭代器 树形遍历 组合模式统一「叶子与容器」的接口,迭代器统一遍历方式,调用方不感知层级 文件夹 / 菜单树递归渲染:对文件和目录一视同仁地 for-each,无需 if 判断类型
🚫 模式滥用警报
模式是药也是毒:出现以下任一信号,就要停下来重新审视设计。
警报信号典型后果纠偏方式
为模式而模式 首恶 简单需求硬套多层抽象,读一个功能要穿过三层接口 先写最简实现,等第二个真实变化点出现再重构引入模式
单例滥用全局状态 到处 Singleton 持有可变状态:测试难隔离、并发埋雷、依赖隐形 生命周期交给 IoC 容器管理,单例只留给真正无状态的组件
继承层级超 2 层 改组合 为复用一路 extends,改父类牵连整棵树,子类越写越像补丁 合成复用原则:改成组合 + 策略 / 回调注入,继承只留一层
中介者变上帝类 所有模块交互都汇入中介者,膨胀成千行大类、职责失焦 只让它协调真正的多对多网状交互,简单的两两调用直连即可
策略类爆炸 参数化替代 一个小分支一个类,实现类几十个,查代码像翻电话簿 无状态小策略改用枚举 / Lambda + Map 注册表,参数化替代建类