先问:什么会发生变化?

假设一台移动机器人更换了激光雷达。连接方式从串口变成网口,数据包格式和扫描频率也发生了变化。此时,我们希望修改主要集中在哪些模块?

这个问题比“系统应该分成几层”更具体。先列出预期中的变化,再观察这些变化会穿过哪些边界,往往更容易找到合适的模块划分。

一个可供讨论的分法是:驱动负责设备交互,适配层负责数据转换,算法接收具有明确语义的观测。这样的边界仍然需要在真实需求中验证,而不是画出来就算完成。

稳定的接口,需要稳定的语义

把不同设备的数据装进同一个结构体,只完成了一部分工作。算法真正需要知道的,还包括:

  • 时间戳表示扫描开始、扫描结束,还是驱动收到数据的时刻?
  • 距离和角度采用什么单位,数据属于哪个坐标系?
  • 无效观测如何表达,缺失一帧时应该发生什么?

这些约定才是接口的核心。字段名称没有改变,但时间含义变了,调用方仍然可能得到错误的结果。

稳定的边界,让调用方可以依赖一个明确的承诺。

把抽象控制在当前问题的范围内

并非每个差异都需要立即抽象。只有一个设备时,可以先保留直接、清晰的实现,同时把设备相关逻辑集中在一个可替换的位置。

当第二种设备出现,再检查哪些部分真的一致。如果必须依靠大量类型判断维持“统一接口”,可能意味着这两个对象本来就承担不同职责。

这里可以做一个简单的变化演练:替换一种输入、增加一种错误、改变一个调度条件。逐项写下需要修改的文件,并说明原因。它比单看架构图更容易暴露依赖。

用一次具体改动检验设计

评审时可以保留三个问题:这次修改影响了谁?调用方为什么必须知道?这个新概念应该由哪个模块拥有?

好的设计不保证以后不用修改,而是让每次修改都有清楚的落点。先解决眼前的问题,再为有证据的变化留下空间。