首页/开发/代码质量与评审速查/封装与信息隐藏(Information Hiding)
设计原则与模式62 条

封装与信息隐藏(Information Hiding)

模块通过接口承诺能力,把实现选择藏起来(Parnas 1972):隐藏的是「可能变化的设计决策」,不是单纯的私有字段;封装让替换实现不惊动调用方。

封装与信息隐藏是什么

Parnas 在 1972 年提出:模块划分的依据应当是「隐藏可能变化的设计决策」,而不是按处理步骤切分。封装的核心是模块通过接口承诺能力,把实现选择藏起来——存储方式、算法、协议、数据格式,这些都是将来可能替换的东西。只把字段改成 private 但协作方式照旧,并不算封装。

基本信息

经典文献Parnas(1972)《On the Criteria To Be Used in Decomposing Systems into Modules》
隐藏对象易变的实现决策:存储、算法、协议、格式
手段模块接口、私有可见性、不提供后门访问器
检验替换实现时,有多少调用方需要改

落地要点

  • 对外只暴露稳定视图:快照、查询方法与意图明确的操作
  • 不提供为测试而设的后门访问器,改用行为断言的测试
  • 集合与结构不直接外泄:返回只读视图或副本
  • 实现细节不进接口签名:参数与返回值里不出现存储层类型
  • 隐藏变化点:第三方协议、缓存策略、并发模型都应内聚在实现内
  • 模块划分依据变化原因,而不是「功能看起来相近」

隐藏点示例

隐藏点示例
// 暴露实现细节:外部能直接改结构,校验与通知全部被绕过
public List<Row> rows;

// 信息隐藏:只给出稳定视图与带语义的操作
private final List<Row> rows = new ArrayList<>();

public Snapshot snapshot() {
    return Snapshot.of(rows);        // 稳定视图,调用方读不到内部结构
}

public void apply(Edit edit) {
    validate(edit);                  // 校验点收在模块内
    edit.applyTo(rows);
    events.publish(new RowsChanged());
}

隐藏什么与暴露什么

对象处理理由
存储结构隐藏可能换成另一种容器或外部存储
算法与策略隐藏实现可替换且无需通知调用方
业务不变量暴露为行为方法调用方需要以受控方式触发
只读数据视图暴露读取是稳定需求
配置开关收敛到实现内外泄会让调用方依赖实现选择
封装的判据可以量化:假设要把实现换掉,有多少调用方需要修改。答案是「几乎不用改」才算封装成功;如果换个存储要改几十处读取代码,那么字段虽然私有,信息依然是公开的。
为测试暴露内部状态是一种隐性侵蚀:测试从此绑定实现细节,重构会因测试而受阻。用行为断言替代状态断言,测试反而更稳定,也更能反映真实需求。

易错点

  • 只做字段私有化不改协作方式:结构仍然暴露,调用方照样依赖实现
  • 为测试暴露内部状态的后门:封装被侵蚀,实现细节被测试绑定
  • 把实现细节写进接口签名:换存储就要改签名,所有实现类与调用方一起改
  • 隐藏过度导致无法观测:出问题时缺少快照与诊断接口,只能加临时日志排查

数据来源与参考

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