代码结构会反复变更的地方——模式最有效
设计模式发挥最大价值的场景有明确特征:这段代码的某个维度(行为、创建方式、功能叠加、模块交互)在可预见的未来会反复变化,而你希望每次变化的改动范围尽量小。
典型的高价值场景:插件系统、多渠道适配、规则引擎、通知系统、报表生成器。这些场景的共同点是"核心逻辑稳定,变体持续增加"。
前提条件:你对变更方向有合理预判。如果完全不知道代码未来会怎么变,提前引入模式更像是在赌博。
看着像但不该用模式的两种情况
变更方向不明确的代码。 一段代码你说不清楚它未来会怎么变,或者根据历史经验它很少被改动。这种代码用最直接的写法最好。引入模式增加了抽象层但没有对应的收益。等变更真的发生了,再根据实际的变更方向引入模式——这时候你有了真实的信号,选择会更准。
问题出在算法或业务逻辑上的代码。 一个排序算法太慢,用 Strategy 模式封装不同的排序策略解决不了性能问题——需要换算法。一个计费规则写错了,用模式重构计费模块的结构也修不了错误——需要改逻辑。设计模式解决的是"结构层面的变更成本",不是"逻辑层面的正确性"。
模式用对了仍然会出问题的三种场景
团队规模和模式复杂度不匹配。 一个三人团队的项目用了 Abstract Factory + Builder + Decorator 的三层结构。理论上优雅,实际上只有写的那个人懂。其他两个人每次改代码都在猜调用链走到哪里了。模式的价值被团队认知成本吃掉了。
模式层级比变体数量还多。 为两种支付方式建了三层工厂,为三种通知渠道设计了四层装饰器。抽象层级超过了它要管理的具体数量,每次添加新变体的操作成本反而比不用模式时更高。
模式跨越了错误的边界。 在微服务架构中,把 Observer 模式用在跨服务的事件通知上——设计模式假设的是进程内调用,延迟可忽略、调用可靠。跨进程之后网络延迟、消息丢失、顺序乱序这些问题都冒出来了,Observer 的简单接口兜不住。这时候需要的是消息队列和事件总线,不是设计模式。
该简化或移除模式的信号
代码里有一层抽象,但这层抽象只有一个实现,而且过去一年没有加过第二个。说明当初预判的变更方向没有发生,这层抽象可以安全移除。
引入模式后,调试和阅读代码的时间明显变长了。每次跟踪一个调用要跳五六个文件。说明抽象层级过度了,应该合并。
新加入团队的人需要超过两天才能理解某个模块的结构。如果这个模块的业务逻辑本身并不复杂,问题大概率出在过度的结构设计上。
这三个信号出现任何一个,考虑做减法:合并抽象层、移除没有被使用的接口、把只有一个实现的工厂替换成直接构造。