首页/开发/代码质量与评审速查/手法:Remove Flag Argument(去标志位参数)
重构手法62 条

手法:Remove Flag Argument(去标志位参数)

布尔参数表示两种行为的函数拆成两个命名函数:调用点自解释、内部分支消失;标志参数是典型的可读性侵蚀点,拆分成本通常很低。

Remove Flag Argument 是什么

用布尔参数表示两种不同行为的函数,拆成两个命名函数:调用点自解释,函数内的分支消失。标志参数的代价体现在阅读端——deliveryDate(order, true) 的 true 是什么含义,只能靠记忆或跳进函数体才知道。拆分成本通常很低,收益直接落在可读性上。

基本信息

场景book(customer, true) 之类的调用
做法拆成 book() / bookExpress() 两个入口
收益调用语义在函数名上自明,内部分支消失
边界参数表示数据而非行为时保留原样

识别与拆分

  • 调用点需要看函数实现才能理解 true 的含义
  • 函数体内最外层就是一个 if (flag) 分成两条路径
  • 同一个布尔值在多个调用点的取值固定不变:调用点其实已经是两种用途
  • 参数在函数体内被反复使用,两条件分支几乎不共享代码
  • 参数是数据而非行为(例如 enableCache 配置项由外部配置决定)时不要拆
  • 拆分时保留一个内部方法承接公共部分,避免代码重复

Java 示例

Java 示例
// 之前:true 是什么含义?只能跳进去看
Date d = deliveryDate(order, true);
Date d2 = deliveryDate(order, false);

// 之后:调用点自解释
Date d  = expeditedDeliveryDate(order);
Date d2 = regularDeliveryDate(order);

// 共同部分留在私有方法里,避免重复
private Date deliveryDate(Order o, DeliveryMode mode) { /* ... */ }

该拆与不该拆

情况处理理由
布尔值决定两套行为拆成两个函数调用语义自明
布尔值来自配置或用户输入保留参数它是数据,不是行为
枚举区分三种以上行为按取值拆成多个入口避免调用方记取值含义
参数仅作为内部透传一层层清理透传的标志位最难追踪
拆出的函数名必须体现差异:expressDeliveryDate 与 deliveryDate 这种命名让人猜不出区别,等于只把问题挪到函数名上。名字里应带上区分行为的词,例如 expedited、draft、dryRun。
过度拆分会带来重复:两个入口如果 90% 逻辑相同,应把公共部分留在私有方法里,由两个入口各自决定差异部分,而不是复制整段实现。

易错点

  • 拆出的函数名不能体现差异:调用点依旧费解,问题只是换了地方
  • 参数语义是数据而非行为:强行拆分导致调用方需要多写一层判断
  • 保留旧的布尔入口不删:两种调用方式并存,新代码继续传 true
  • 拆分后复制整段实现:同一逻辑两份,改一处漏一处

数据来源与参考

  • 《重构》第 2 版 · 第 11 章(Remove Flag Argument)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌