首页/开发/系统分析与架构设计速查/信息隐藏(Information Hiding)
架构风格与模式58 条

信息隐藏(Information Hiding)

Parnas 提出:模块划分应隐藏“可能变化的决策”,接口只暴露稳定信息;与按处理步骤划分相比,更利于应对变化与独立开发。

信息隐藏的划分依据

Parnas 在 1972 年提出:模块划分的依据不该是处理步骤,而应是“哪些决策可能发生变化”。每个模块把一类易变决策藏起来,只对外暴露稳定的抽象与契约。这样当存储格式、算法实现或外部接口发生变化时,改动被限制在模块内部。它与“隐藏数据”不是一回事——被隐藏的对象是易变的设计决策。

基本信息

划分依据哪些决策可能变化(存储格式、算法、外部系统)
暴露内容稳定的抽象与契约,不暴露实现细节
收益局部修改不影响其他模块,支持并行开发
与封装的区别封装关注访问控制,信息隐藏关注隔离变化点

典型使用场景

  • 替换存储实现:仓储接口隔离数据库细节,业务逻辑不感知 SQL 方言
  • 接入第三方系统:防腐层与适配器隔离外部模型与内部领域模型
  • 算法可替换:策略接口按配置选择实现,切换不需要改调用方
  • 部署拓扑变化:配置注入替代硬编码,单机与集群部署共用代码
  • 多端接入:BFF 与适配层隔离端侧差异,核心逻辑保持单一实现

变化点识别示例

可能变化的决策隐藏方式
存储格式仓储接口后替换实现
第三方接口防腐层与适配器
算法实现策略接口按配置选择
部署拓扑配置注入而非硬编码

接口设计对照

接口设计对照
暴露实现细节:
  getOrders(sqlWhere: string, orderBy: string)
  # 调用方必须了解表结构与字段名,索引调整即影响调用方

隐藏变化点:
  findOrdersByStatus(status, page): Order[]
  findOrdersByCustomer(customerId, timeRange): Order[]
  # 调用方表达业务意图,存储方案在实现里演进

使用要点

  • 模块接口暴露内部数据结构,实现变化即破坏调用方,属于典型的隐藏对象选错
  • 按变化点而不是按处理步骤划分模块,否则变化时仍要跨模块改
  • 接口按调用方需求设计:只为实际用到的能力开方法,避免万能查询接口
  • 留一条“变化发生时的验证路径”:接口契约要有测试,替换实现时能立刻发现破坏
信息隐藏与“隐藏数据”不同:隐藏的对象是易变的设计决策;把数据藏起来而暴露了变化点,等于没做信息隐藏。
过度抽象同样是成本:只有真的存在变化预期时才引入抽象层,为想象中的变化预留接口会带来无价值的间接层与维护负担。

易错点

  • 模块接口暴露内部数据结构,实现变化即破坏调用方,升级被迫全链路改造
  • 按流程步骤而非变化点划分模块,变化时仍要跨模块修改,划分收益落空
  • 设计“万能查询接口”把数据库能力暴露给调用方,实现无法演进也无法优化索引
  • 抽象边界与团队边界错位:跨团队的接口变更缺少契约与版本管理,改动变成协调成本
  • 为想象中的变化过度抽象,留下一堆只有一个实现的接口,可读性与调试成本上升

数据来源与参考

  • David Parnas《On the Criteria To Be Used in Decomposing Systems into Modules》(CACM, 1972)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌