简单的来说,反模式是指在对经常面对的问题经常使用的低效,不良,或者有待优化的设计模式/方法。甚至,反模式也可以是一种错误的开发思想/理念。在这里我举一个最简单的例子:在面向对象设计/编程中,有一条很重要的原则, 单一责任原则(Single responsibility principle)。其中心思想就是对于一个模块,或者一个类来说,这个模块或者这个类应该只对系统/软件的一个功能负责,而且该责任应该被该类完全封装起来。当开发人员需要修改系统的某个功能,这个模块/类是最主要的修改地方。相对应的一个反模式就是上帝类(God Class),通常来说,这个类里面控制了很多其他的类,同时也依赖其他很多类。整个类不光负责自己的主要单一功能,而且还负责了其他很多功能,包括一些辅助功能。很多维护老程序的开发人员们可能都遇过这种类,一个类里有几千行的代码,有很多功能,但是责任不明确单一。单元测试程序也变复杂无比。维护/修改这个类的时间要远远超出其他类的时间。很多时候,形成这种情况并不是开发人员故意的。很多情况下主要是由于随着系统的年限,需求的变化,项目的资源压力,项目组人员流动,系统结构的变化而导致某些原先小型的,符合单一原则类慢慢的变的臃肿起来。最后当这个类变成了维护的噩梦(特别是原先熟悉的开发人员离职后),重构该类就变成了一个不容易的工程。

上帝类是代码层反模式的代表,但反模式在数据库设计层同样高发且代价更大(数据迁移成本远高于代码重构)。本文按”代码层 → 数据库层 → 架构层”三级盘点高频反模式,每个给出识别信号与解药,并讨论反模式为何总是”非故意地”发生。

代码层反模式(上帝类的同族)

  • 上帝类/上帝对象:信号=千行类、改动必冲突、单测无法写;解药=按职责拆分+提取服务类,重构前先补特征测试(characterization test)锁定现状行为
  • 贫血模型:实体只有 getter/setter,逻辑全在 Service 堆砌;信号=Service 千行、实体无行为;解药=行为回归实体(充血),Service 只做编排
  • 散弹式修改/霰弹枪手术:一个需求改十个类;信号=变更影响面不可预测;解药=职责归位(与上帝类互为镜像:一个功能散在多处 vs 多个功能聚在一处)
  • 复制粘贴编程:第三处复制即应抽象;信号=改 bug 要改 N 处且常漏;解药=提取模板方法/策略
  • 金锤(Golden Hammer):熟悉的技术解决一切问题(万物皆 MQ/万物皆缓存);解药=选型评审问”问题的形状是什么”

数据库层反模式(《SQL 反模式》精华)

  • 逗号分隔列表存多值(tags 列存 “a,b,c”):无法索引、无法约束、LIKE 查询全表扫;解药=关联表
  • EAV(实体-属性-值)万物表:灵活变噩梦,查询需多自连接、类型丧失;解药=明确列或 JSON 列(带生成列索引)+ 仅真动态属性用 EAV
  • 主键用邮箱/自然键:业务值变更即主键变更灾难;解药=无意义自增/UUID surrogate key
  • 外键恐惧症(不用外键靠应用保证):脏数据迟早出现;解药=核心一致性用外键+索引,跨服务边界才放弃外键改事件补偿
  • 索引缺失与索引泛滥:缺索引慢查询、滥索引写放大;解药=按查询负载设计索引,定期审查未使用索引
  • NULL 滥用:NULL 参与比较/聚合的三值逻辑陷阱;解药=NOT NULL + 默认值/哨兵,或显式状态列
  • 分区/分库过早:百万行就喊分库分表;解药=先索引优化+读写分离+归档,分片是最后手段

架构层反模式

  • 分布式单体:服务拆了但强同步长链+共享库表,得到分布式代价而无独立部署收益;信号=每次发布全服务联动
  • 数据库共享:多服务直连同一库同几张表(SOA 时代遗留);解药=database-per-service + API/事件同步
  • 无界上下文混乱:同一概念(”用户”)在多服务含义不同却共用模型;解药=DDD 限界上下文划界
  • 缓存当数据库:核心数据只存缓存无持久化兜底;解药=缓存永远是副本,源在库
  • 日志当消息队列:靠扫日志驱动业务;解药=事件走 MQ,日志只供观测

反模式为何”非故意”发生

原文的观察深刻:上帝类不是有人故意写出来的。反模式的共同成因是压力下的局部最优累积:

  • 期限压力:复制粘贴比抽象快,今天省一小时,未来还一天
  • 需求漂移:类职责随需求一点点渗入,无人在某次评审中决定”让它变上帝”
  • 人员流动:约束知识随人离开,后人只敢加不敢改
  • 度量缺失:没有”变更影响面/圈复杂度/慢查询”的持续度量,恶化不可见

对应防线:代码评审关注职责、CI 卡复杂度与覆盖率阈值、慢查询与未使用索引周期审查、架构决策记录(ADR)留下”为什么”。反模式治理是持续卫生,不是年终大扫除。

识别-解药速查

反模式 识别信号 解药
上帝类 千行、改必冲突 拆职责+特征测试
多值列 LIKE ‘%x%’ 查询 关联表
EAV 滥用 查询多自连接 明确列/JSON
分布式单体 发布全联动 异步化+数据自治
缓存当库 无持久化兜底 缓存只做副本

反模式的价值在于命名:一旦团队共用”上帝类””EAV””分布式单体”这些词,坏味道就能在评审中被一句话指出——命名即防线的第一块砖。