设计模式
设计模式描述反复出现的对象设计问题:对象怎样创建、怎样组合、怎样协作。它提供一套交流设计的语言,是否采用仍要看变化成本。稳定的两个分支可以直接保留;只有当新增需求反复牵动旧代码时,抽象才有明确收益。
从订单导出认识三类问题
用户点击“导出订单”后,系统需要创建导出任务、接入文件存储、选择 CSV 或 Excel 渲染方式。这三个变化点对应不同模式,不需要把整个流程套成一种模式。
| 变化点 | 直接写法遇到的问题 | 可考虑的结构 |
|---|---|---|
| 任务有筛选条件、列配置、时区等可选项 | 构造函数参数难以辨认,非法组合难检查 | 建造者集中构建与校验 |
| 对象存储 SDK 的上传接口不同 | 每个业务调用方都重复转换参数和错误码 | 适配器提供内部上传端口 |
| CSV 与 Excel 的渲染算法不同 | 新格式不断修改导出主流程 | 策略隔离渲染算法,由请求或配置选择 |
重构是否有效,要再加一种格式来验证:新增渲染策略并注册,旧格式的输出、错误处理和资源释放仍然通过测试。策略只隔离算法,并不会自动解决大文件内存占用、任务重试或下载权限;这些属于项目实践与架构边界。
按问题选择
图中的模式是候选。先确认调用方希望保持什么稳定,再选择隔离方式。例如“包装一个对象”既可能是装饰器,也可能是代理,区别取决于增加职责还是控制访问。
| 容易混淆的模式 | 判断依据 | 订单系统中的例子 |
|---|---|---|
| 策略 / 状态 | 算法选择,还是对象生命周期约束 | 选择优惠算法 / 已发货订单不能再取消 |
| 策略 / 模板方法 | 委托可替换算法,还是继承固定流程骨架 | 替换文件渲染器 / 固定导出步骤并覆盖部分步骤 |
| 适配器 / 外观 | 转换不兼容接口,还是简化子系统入口 | 转换存储 SDK / 封装创建导出任务的协调入口 |
| 装饰器 / 代理 | 叠加职责,还是管理目标访问 | 日志输出加前缀 / 调用前校验下载权限 |
| 工厂方法 / 抽象工厂 / 建造者 | 一个产品的创建、一组相关产品,还是复杂对象的组装 | 创建渲染器 / 成套创建同风格控件 / 组装导出参数 |
分类与详细页
创建型模式把产品选择、初始化或组装从使用方分离;不意味着每个 new 都需要工厂。单例的唯一性也有范围,例如 Java 通常以类加载器为边界,不能保证多个进程共享同一实例。
| 创建型 | 主要变化 |
|---|---|
| 单例 | 约定范围内的实例数量与访问入口 |
| 工厂方法 | 由具体创建者决定产品实现 |
| 抽象工厂 | 一组相关产品的一致创建 |
| 建造者 | 复杂对象的构建步骤与表示 |
| 原型 | 从已有对象复制,明确共享和独立字段 |
结构型模式组织对象之间的关系。包装层应有清楚目的;每加一层都需要解释它如何转发调用、处理异常和管理资源。
| 结构型 | 主要变化 |
|---|---|
| 适配器 / 桥接 | 接口兼容 / 两个独立变化维度 |
| 组合 | 树结构中整体与叶子的统一使用 |
| 装饰器 / 代理 | 职责叠加 / 访问控制 |
| 外观 / 享元 | 简化入口 / 共享可复用状态 |
行为型模式分配协作职责。进程内观察者与消息队列有不同的交付边界;命令封装也不天然具备撤销能力,必须另外定义逆操作或保存恢复信息。
| 行为型 | 主要变化 |
|---|---|
| 策略 / 模板方法 | 算法 / 流程中的步骤 |
| 状态 | 生命周期约束与允许动作 |
| 观察者 / 中介者 | 变化通知 / 多方协调 |
| 责任链 / 命令 | 处理顺序 / 请求对象化 |
| 迭代器 / 访问者 | 遍历 / 在稳定结构上增加操作 |
| 备忘录 / 解释器 | 状态恢复 / 简单语言解释 |
阅读与验收
先读设计原则建立判断依据,再从当前代码中选择一个真实变化点,阅读对应模式。工厂、适配器、策略通常比较容易与业务对应,桥接、访问者、解释器适合在遇到相应结构后再深入。
一次重构至少验证三件事:旧行为不变;新增同类需求的修改范围更集中;异常、并发和资源生命周期没有因新增抽象被隐藏。若只是类变多、调用链变长,却没有减少变更影响,直接设计往往更合适。
