绝大多数开发者对设计原则的误用,并不发生在核心业务逻辑中,而是发生在那些连接不同系统、转换数据格式、处理消息路由的胶水代码里。胶水代码不需要设计原则,或者说,它需要的是一套完全不同的准则。把面向对象设计原则、函数式编程范式硬套在胶水代码上,最终得到的是一个过度抽象、难以调试、对新人极度不友好的怪物。
胶水代码的核心任务是连接——把A系统的数据格式转换成B系统能理解的结构,把C服务的调用结果转发给D队列。这种“连接”属性意味着它天然没有自己的核心领域模型。据《Software Architecture: The Hard Parts》一书中的分析,系统间的集成层代码,其变更频率远高于核心业务逻辑,但变更原因几乎全部来自外部——上游接口换了字段、下游服务改了认证方式、中间件升级了协议版本。把策略模式、工厂模式、依赖注入这些武器用在胶水代码上,本质上是在为一个没有内聚特性的模块强行制造内聚。
一个典型的反例:用策略模式封装不同支付渠道的调用逻辑。表面上看,支付宝、微信支付、银联各有各的接口,正好对应策略模式的不同实现。但问题在于,支付渠道的真正差异不是业务逻辑的差异,而是协议格式和异常处理方式的差异。当支付宝把返回字段从trade_no改成transaction_id,你不得不修改策略实现类,同时还要修改调用方的映射逻辑——策略模式承诺的“新增而不修改”在这里彻底失效。
设计原则之所以成立,是因为它们解决的是软件中“变化”的问题。开闭原则告诉你对扩展开放、对修改关闭,前提是你能识别出系统中哪些维度会变化。胶水代码的悲哀在于,它无法预测变化的方向。
一个数据同步任务,从MySQL读数据写到Elasticsearch。你用模板方法模式抽象出“读取-转换-写入”的骨架,预留了扩展点。三个月后,需求变成了“从MySQL读数据,调用一个HTTP接口做数据清洗,再写到Elasticsearch,同时在Redis里缓存一份热点数据”。你精心设计的骨架瞬间崩塌——模板方法只能处理固定步骤的扩展,无法应对步骤本身的增减。这种情况下,一个简单的脚本式编排代码,反而比精心设计的模式更容易修改。
DRY原则——Don’t Repeat Yourself,是驱动开发者抽象胶水代码的最强动力。看到两个地方都在做类似的JSON字段映射,立刻抽取出一个通用的转换器。但这个动作忽略了一个关键事实:胶水代码的相似性往往是巧合,而非本质。
两个服务都返回user_id字段,不等于它们代表同一个概念。A服务是内部用户系统,user_id是自增主键;B服务是第三方OAuth,user_id是平台分配的UUID。把它们合并到一个通用处理逻辑里,当B服务因为安全审计要求,user_id改用加密后的哈希值时,你的通用逻辑立刻需要引入一个if分支。这个分支一旦出现,DRY的价值就打了折扣——你只是把重复代码换了个地方存储,还额外增加了理解成本。
更危险的是,为了追求可重用性而对胶水代码进行抽象,往往导致测试成本急剧上升。一个独立的胶水脚本,可以直接用真实数据进行端到端测试。抽象成可配置的框架后,你得先测试框架本身的正确性,再测试配置文件的正确性,最后还要测试两者组合在一起的行為——测试矩阵的膨胀速度是指数级的。
既然常规设计原则失效,胶水代码需要什么样的指南?以下六条原则来自多次项目重构的经验总结,它们的共同点是放弃“设计感”,拥抱“实用性”。
| 常规原则 | 为何失效 | 胶水代码替代原则 |
|---|---|---|
| 开闭原则 | 无法预测扩展维度 | 优先直写,重复三次再考虑抽象 |
| 单一职责 | 职责边界由外部系统定义 | 一个文件对应一个数据流 |
| 依赖倒置 | 依赖方向由协议决定 | 依赖具体实现,测试时再mock |
| 接口隔离 | 接口由上游系统强加 | 不需要抽象层,直接适配 |
| DRY | 相似性可能是巧合 | 允许重复,直到模式稳定 |
| 迪米特法则 | 胶水代码天然需要了解多系统 | 集中处理转换逻辑,避免分散 |
这些替代原则的核心思想是:胶水代码的价值不在于内部结构有多优雅,而在于修改时有多直接。一个新人打开代码,看到的是一个清晰的管道——数据从A到B,中间经过哪些转换步骤,错误在哪里处理,他不需要理解任何抽象概念就能下手修改。这才是胶水代码真正该追求的品质。
说胶水代码不需要设计原则,不等于说整个系统不需要设计原则。关键在于把原则用对地方。核心业务逻辑——那些承载着企业真正价值的领域模型、计算规则、状态机——是设计原则大显身手的地方。领域驱动设计中的聚合、实体、值对象、领域服务,这些概念存在的意义就是让业务逻辑形成清晰的边界,抵抗变化带来的侵蚀。
胶水代码应该被隔离在独立的模块中,用最简单的脚本式代码实现,接受它“丑陋”的本质。而核心业务逻辑则被保护在一个干净的、应用了设计原则的内核中。这种“内核干净、外壳简陋”的分层策略,比试图在整个代码库中统一应用设计原则,要务实得多。它让团队把精力集中在真正产生价值的代码上,而不是在注定要频繁修改的胶水层浪费设计心力。
当你在代码评审中看到有人用一个巧妙的工厂模式封装了三个消息队列的发送逻辑,问一个问题:这个抽象,是不是在为一个没有内聚性的东西强行制造内聚?如果答案是肯定的,删掉它,换成三个直白的函数调用。代码行数可能会增加,但维护成本一定会下降。
评论
加载中...