用一个实际例子来学习,TaskCat是一个任务管理器,主要聚焦在增删改查这些逻辑!
但它有一些问题:
--无法撤销 — done 或 delete 执行后无法回退
--输出格式死板 — 只能打表格,想加 JSON/Markdown 输出就要改 TaskCat 类
--存储不持久 — 退出即丢失(每次都是新 JVM)
--所有逻辑一个类 — TaskCat 已经开始膨胀,再加功能会更乱
--命令解析和业务逻辑混在一起 — switch 里既有参数校验又有业务操作
1、现在解决第一个问题,无法撤销,使用命令(command)模式!将一个请求封装成一个对象,从而让用户使用不同的请求把客户端参数化;对请求排队或记录日志,以及支持可撤销的操作。
原来:操作直接改数据,无法回退; 现在:每个操作封装成 Command 对象,自带 undo();
原来:加新操作要改 TaskCat 的 switch; 现在:新操作只需新建一个 Command 实现类;
原来:操作历史无法追踪; 现在:UndoManager 天然记录了所有操作;
还有残留的痛点:TaskCat.executeCommand() 里仍在用 switch + if-else 分发命令 → Phase 2 的 Strategy模式会更好;新命令(比如加一个 edit)还是要改 TaskCat → 还没做到开闭原则;输出格式仍然写死 → Phase 2
2、策略strategy模式,将多种不同的实现算法封装,然后客户端根据不同策略分别调用相应的算法!首先定义一个策略接口,规定算法的同一操作;封装各个算法然后使用。
解决输出格式写死的问题
原来问题 | 现在修改 |
|
|
每种格式要 if-else 分支 |
|
格式逻辑混在主类里 | 格式逻辑完全在各自的 Formatter 类里,TaskCat 只调接口 |
还残留痛点:建
3、工厂方法(factory method)模式,解决存储后端耦合的问题。定义一个用于创建对象的接口,让子类决定实例化哪个类!
在重启后数据不会丢失!
Phase 2 的痛 | Factory Method 怎么解 |
|---|---|
|
|
加文件存储要改 TaskCat | 新增 |
Commands 直接依赖具体集合类型 | Commands 只依赖 |
数据退出即丢失 |
|
还残留痛点:任式。
4、观察者(observer)模式,定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都可以得到通知并自动更新。
任务状态变更时会有通知!观察者模式使用十分广泛,类似于发布-订阅。
Phase 3 的痛 | Observer 模式怎么解 |
|---|---|
想知道"什么时候添加了任务"只能轮询 | 注册 Observer,事件自动推送 |
加日志/统计功能要侵入业务代码 | 新功能只需新建 Observer 并注册 |
功能耦合在主流程里 | 功能与核心解耦,可随时增删观察者 |
还残留痛点:任式
5、装饰器(decorator)模式,是在不必改变原类和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
解决"给任务附加能力导致继承爆炸"的问题,每个能力是一个装饰器,可以自由叠加。
继承方式 | Decorator 方式 |
|---|---|
加标签 → TaggedTask extends BasicTask | TaggedTask wraps Task |
加优先级 → PriorityTask extends BasicTask | PriorityTask wraps Task |
两者都要 → TaggedPriorityTask??? |
|
N 个能力 → 2^N 个子类 | N 个装饰器,自由组合 |
还残留痛点:构建式
6、构建者(builder)模式,将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示
解决查询条件参数膨胀的问题
痛点 | Builder 怎么解 |
|---|---|
查询参数越来越多: |
|
不需要的条件也得传 null | 不设的条件默认不筛选 |
加新筛选维度就要加新方法 | Builder 加一个方法,查询方法不用改 |
7、改变了其类。
7、状态(state)模式,当一个对象内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
解决任务状态流转中的 if-else 判断问题,存在一个很长的if else列表,而这些分支都是因为不同状态下执行的操作不一样时考虑使用此模式
8、模版方法(template)模式,在一个方法中定义一个算法的骨架,而将一些步骤的实现延迟到子类中,使得子类可以在不改变一个算法的结构前提下即可重定义该算法的某些特定步骤
场景是数据导入导出的骨架流程,以后要支持 JSON 导入、CSV 导入、Markdown 导出等多种格式,每种格式的"读取→解析→处理→写入"流程一样,但细节不同。用 Template Method 把骨架固定在基类,子类只实现各自的细节步骤。