首页/开发/代码质量与评审速查/手法:Encapsulate Variable(封装变量)
重构手法62 条

手法:Encapsulate Variable(封装变量)

把裸字段变为访问器:先建 getter/setter 再逐步收紧可见性,为后续校验、惰性初始化、变更通知留出插入点;全局变量尤其值得封装。

封装变量是什么

把裸字段变成访问器:先加读取与写入入口把访问收拢,再逐步收紧字段可见性,为后续的校验、惰性初始化与变更通知留出插入点。全局变量与跨模块共享状态尤其值得封装,因为它们是最难追踪的修改来源。

基本信息

步骤封装读、封装写、收紧可见性、添加约束
典型场景全局配置、单例状态、跨模块共享数据
收益变更点集中,便于加校验与通知
常见搭配值对象、不可变数据、依赖注入
判断信号同一字段被多处直接读写

典型使用场景

  • 全局配置对象被多处直接修改
  • 单例里的可变状态没有统一入口
  • 跨模块共享的缓存需要观察何时被清空
  • 给字段加校验(非空、范围)之前先收拢写入口
  • 需要惰性初始化时,用访问器承载初始化逻辑

封装前后

封装前后
// 之前:字段公开,任何地方都能改
public static Config config;

// 之后:入口收拢,写入时校验
private static Config config;

public static Config config() {
    return config;
}

public static void setConfig(Config next) {
    Objects.requireNonNull(next);
    config = next;
}

封装的四个阶段

阶段动作目的
封装读改为通过访问器读取先让读取路径可控
封装写写入走统一入口便于加校验与通知
收紧可见性字段改为私有阻止新的直接访问
添加约束校验、不变式、变更通知把规则写进入口
顺序有讲究:先建立访问入口,再切换调用方,最后隐藏字段。反过来做等于一次性打断所有调用点,迁移期无法编译。
只加写入方法不收敛约束,等于把裸字段换成方法名:校验与不变式仍没有地方落脚,问题只是从字段搬到了方法上。
对外暴露集合时返回只读视图或副本,避免调用方绕开访问器直接改内容;这类隐式修改最难排查,封装要把入口一起管住。

易错点

  • 只加写入方法不收敛约束:校验与不变式依旧缺失,封装成了形式
  • 封装后到处调用写入方法传数据:状态变更点仍然分散,难以追踪
  • 先改私有再切调用方:一次性打断全部调用点,迁移期无法编译
  • 访问器里做重活(远程调用、循环计算):读写变成隐式开销,调用方难以预料

数据来源与参考

  • 《重构》第 2 版 · 第 6 章(Encapsulate Variable)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌