重构 API 旨在提升 API 的清晰度、简化其使用方式,并明确函数的功能边界,特别是区分查询和修改操作。
以下是这些技巧的整理和说明:
技巧(中文/英文)
核心目标与动机
做法概览
示例/说明
将查询函数和修改函数分离 (Separate Query from Modifier)
保持 API 清晰地分离 读取数据的函数(查询)和修改程序状态的函数(修改函数)。如果一个函数同时返回一个值并具有副作用(改变可观察状态),应将其拆分。
如果一个函数返回一个值 并且 具有副作用,则应拆分成两个函数:一个 查询函数(返回结果,没有副作用)和一个 修改函数(改变状态,通常返回 void)。
如果函数 alertForMiscreant(people) 既查找了不法分子,又触发了警报,应将其拆分为 findMiscreant(people) (查询) 和 alertForMiscreant(people) (修改函数,调用查询函数并触发警报)。
函数参数化 (Parameterize Function)
消除多个函数之间 相似的重复代码,用一个通用函数取代功能相似、仅有字面量值差异的一组函数。
用 改变函数声明 为函数添加参数,以承载那些变化的字面量值。然后将所有相似的函数调用重定向到这个新的通用函数。
用一个 raise(aPerson, factor) 函数取代功能相似的 tenPercentRaise(aPerson) 和 fivePercentRaise(aPerson)。
移除标记参数 (Remove Flag Argument)
消除用于指示被调函数执行哪一部分逻辑的 标记参数(flag argument),因为它们隐藏了差异性,使 API 难以理解。布尔型标记尤其糟糕。
将包含标记参数的函数替换为 多个明确的函数,每个函数完成标记参数所代表的一项独立任务。
用 setHeight(value) 和 setWidth(value) 取代根据传入字符串 name 决定设置哪个维度的 setDimension(name, value)。
保持对象完整 (Preserve Whole Object)
避免从一个记录结构中取出多个值,并将它们作为独立参数传递给函数。将 整个记录或对象 完整地传给函数,可以缩短参数列表,并更好地应对未来函数对数据需求的变更。
修改函数声明,接受完整的对象作为参数。让函数体内部自行访问所需数据。
函数调用应从 aPlan.withinRange(low, high) 改为 aPlan.withinRange(aRoom.daysTempRange),传入完整的 daysTempRange 对象。
以查询取代参数 (Replace Parameter with Query)
消除冗余参数,如果该参数的值可以通过查询函数(query)从 另一个已传入的参数或对象 中获得。
将函数体内引用该参数的地方改为调用新建的查询函数。全部替换完成后,使用 改变函数声明 将该参数去掉。
如果 discountedPrice(basePrice, discountLevel) 中的 discountLevel 可以通过调用 this.discountLevel 获得,则移除参数,让函数自己查询。
以参数取代查询 (Replace Query with Parameter)
解决函数对 外部元素(如全局变量或需要移除的元素)的依赖关系,增强函数的 引用透明性。
将函数内部的查询操作提炼为参数,迫使函数的调用者提供该值。这通常涉及 提炼变量 和 提炼函数 的组合操作。
如果函数 targetTemperature 内部查询了 thermostat.currentTemperature 的值,则修改函数使其接受 currentTemperature 作为参数传入。
移除设值函数 (Remove Setting Method)
确保对象字段在构造之后不被修改(即保持 不可变性),从而清晰表达设计意图并避免错误。
使用 改变函数声明 确保字段值通过构造函数传入。移除所有在构造函数之外对设值函数的调用,并使用 内联函数 消去设值函数。
如果 Person 类的 name 字段在对象创建后不应改变,则移除 setName 函数。
以工厂函数取代构造函数 (Replace Constructor with Factory Function)
提高对象创建的 灵活性,避免构造函数的限制(如不能返回子类,命名受限)。
新建一个工厂函数,让它在内部调用现有的构造函数。修改所有调用构造函数的代码改为调用工厂函数。
用 createEmployee(name, typeCode) 函数取代直接调用 new Employee(name, typeCode),以便在工厂函数中处理类型选择和子类实例化逻辑。
以命令取代函数 (Replace Function with Command)
当一个函数变得过于复杂,或需要支持 高级功能(如撤销、复杂生命周期管理、继承定制)时,将其封装成一个“命令对象”(command object)。
为目标函数创建一个类(命令对象)。使用 搬移函数 将函数移入新类,参数成为新类的字段。
一个复杂的 score(...) 函数可以被封装进 ScoreCommand 类,其逻辑位于 execute() 方法中。
以函数取代命令 (Replace Command with Function)
如果命令对象过于简单,其带来的 复杂性 大于其提供的灵活性,则应将其变回普通的函数。
将“创建并执行命令对象”的代码提炼到一个函数中。将命令对象的逻辑(包括构造函数参数和支持函数)内联到这个新函数中,并删除命令类。
一个简单的 ChargeCalculator 类,如果其计算逻辑不复杂,应被替换为一个简单的函数 charge(customer, usage, provider)。
 
学习笔记——《重构改善现有代码2》:处理继承关系学习笔记——《重构改善现有代码2》:简化条件逻辑