代码结构出问题的五个典型现场

五个工程师日常会撞上的结构困境——每个都对应一组设计模式的适用场景

本页目录

加一种支付方式要改八个文件

电商系统原来只支持微信和支付宝。产品说要加银联。开发评估了一下:要改支付入口、订单处理、退款逻辑、对账模块、通知模板、日志格式、监控告警、测试用例——八个文件,预估三天。

问题不在银联接口复杂。问题在于支付方式被硬编码在了业务逻辑的每一层里。每一层都有 if (payType == "wechat") 这样的分支。

画出来就清楚了:支付行为应该是一个接口,微信、支付宝、银联各自实现。业务逻辑只和接口打交道,不关心具体是哪种支付。Strategy 模式的经典场景。

改完之后加银联:新增一个实现类,注册到配置里。改动范围从八个文件缩到一个文件加一行配置。

日志格式改了三次还是不满意

运维要求日志格式从文本改成 JSON。改完了。两周后安全团队要求敏感字段脱敏。又改了。一个月后 SRE 说日志要按级别分文件写。

每次改都在同一个类里加代码。三次改动之后,这个类既负责格式化、又负责脱敏、又负责路由。职责混在一起,下次只想改格式化时不确定会不会影响脱敏逻辑。

Decorator 模式的使用场景:把每个关注点包成一层装饰器。格式化是一层、脱敏是一层、路由是一层。需要哪些能力就叠加哪些层,顺序可调、可拆。每一层只管自己的事。

改了父类,四个子类全炸了

有一个 BaseProcessor 类,四个业务子类继承它。父类里有个 validate() 方法,最初做的是通用校验。

需求变了:某种业务需要额外校验一个字段。直接在父类 validate() 里加了判断。结果另外三个子类的校验行为也被改了——它们不需要这个字段,但父类的改动让它们也开始校验了。

继承层级过深的典型后果:父类的改动会穿透到所有子类。Template Method 模式的正确用法是:父类定义流程骨架(先校验、再处理、最后通知),把具体步骤留给子类实现。父类只决定"做什么和按什么顺序做",不决定"怎么做"。

所有模块都在直接调所有模块

一个报名系统:用户模块直接调支付模块确认缴费,支付模块直接调通知模块发短信,通知模块直接调用户模块查手机号,用户模块又调报名模块查课程信息。

画出调用关系图:一团互相指向的箭头,没有层次。改任何一个模块的接口,其他模块都可能受影响。

Mediator 模式的适用场景。把模块间的直接调用收到一个协调者手里。用户模块不直接调支付模块,而是告诉协调者"报名完成了",协调者去通知支付模块。每个模块只和协调者打交道,互相之间不知道对方存在。

代价是协调者本身会变复杂。但这种复杂是集中的、可管理的。散落在所有模块里的耦合是分散的、不可管理的。

工厂建了三层但只有两种产品

技术负责人读了设计模式之后很兴奋,给消息推送系统建了 Abstract Factory + Builder + Prototype 三层工厂结构。支持的消息类型:邮件和短信。

两种产品,三层工厂。新来的开发者花了一天读代码才搞明白"发一封邮件"的调用链怎么走。后来加了 App 推送,确实只改了一个工厂类,但前期那三层抽象的认知成本远超收益。

过度设计的典型症状:抽象层数 > 变体数量。模式是工具,不是信仰。当前只有两种变体、未来一年不太可能超过五种时,一个简单的 Factory Method 就够了。等真的需要更复杂的结构时再重构——重构的成本通常比过度设计的维护成本低。

同分类继续看