CH06_第一组重构(下)

封装变量(Encapsulate Variable | 132)

曾用名:自封装字段(Self-Encapsulate Field)

曾用名:封装字段(Encapsulate Field)

js 复制代码
let defaultOwner = {firstName: "Martin", lastName: "Fowler"};
js 复制代码
let defaultOwnerData = {firstName: "Martin", lastName: "Fowler"};
export function defaultOwner() {return defaultOwnerData;}
export function setDefaultOwner(arg) {defaultOwnerData = arg;}

动机

重构的作用就是调整程序中的元素。函数相对容易调整一些,因为函数只有一种用法,就是调用。在改名或搬移函数的过程中,总是可以比较容易地保留旧函数作为转发函数(即旧代码调用旧函数,旧函数再调用新函数)。这样的转发函数通常不会存在太久,但的确能够简化重构过程。

如果数据的可访问范围变大,重构的难度就会随之增大,这也是说全局数据是大麻烦的原因。

如果想要搬移一处被广泛使用的数据,最好的办法往往是先以函数形式封装所有对该数据的访问。

封装能提供一个清晰的观测点,可以由此监控数据的变化和使用情况,还可以轻松地添加数据被修改时的验证或后续逻辑。数据的作用域越大,封装就越重要。处理遗留代码时,一旦需要修改或增加使用可变数据的代码,可以借机把这份数据封装起来,从而避免继续加重耦合一份已经广泛使用的数据。

面向对象方法如此强调对象的数据应该保持私有(private),背后也是同样的原理。每当看见一个公开(public)的字段时,考虑使用封装变量来缩小其可见范围。

封装数据很重要,不过,不可变数据更重要。如果数据不能修改,就根本不需要数据更新前的验证或者其他逻辑钩子。

做法

  • 创建封装函数,在其中访问和更新变量值。
  • 执行静态检查。
  • 逐一修改使用该变量的代码,将其改为调用合适的封装函数。每次替换之后,执行测试。
  • 限制变量的可见性。
  • 测试。
  • 如果变量的值是一个记录,考虑使用封装记录(162)。

封装值

对数据结构的引用做封装,能控制对该数据结构的访问和重新赋值;但并不能控制对结构内部数据项的修改。

控制结构内部数据的修改方法:

  • 取值函数中返回数据的一份副本(保护源数据,防止源数据变化导致的意外事故)

  • 阻止对数据的修改(禁止对数据结构内部的数值做任何修改)

数据封装很有价值,但往往并不简单。到底应该封装什么,以及如何封装,取决于数据被使用的方式,以及想要修改数据的方式。

变量改名(Rename Variable | 137)

js 复制代码
let a = height * width;
js 复制代码
let area = height * width; 

动机

好的命名是整洁编程的核心。变量名起得好,变量可以很好地解释一段程序在干什么。

变量使用范围越广,名字的好坏就越重要。

机制

  • 如果变量被广泛使用,考虑运用封装变量(132)将其封装起来。
  • 找出所有使用该变量的代码,逐一修改。(已发布变量,不能进行这个重构)
  • 测试。

引入参数对象(Introduce Parameter Object | 140)

js 复制代码
function amountInvoiced(startDate, endDate) {...}
function amountReceived(startDate, endDate) {...}
function amountOverdue(startDate, endDate) {...}
js 复制代码
function amountInvoiced(aDateRange) {...}
function amountReceived(aDateRange) {...}
function amountOverdue(aDateRange) {...}

动机

一组数据项(作为函数参数)总是同时出现(这样一组数据就是所谓的数据泥团)。

将数据组织成结构是一件有价值的事,因为这让数据项之间的关系变得明晰。使用新的数据结构,参数的参数列表也能缩短。

做法

  • 如果暂时还没有一个合适的数据结构,就创建一个
  • 测试
  • 使用改变函数声明(124)给原来的函数新增一个参数,类型是新建的数据结构
  • 测试
  • 调整所以调用者,传入新数据结构的适当实列。修改每一处,执行测试
  • 用新数据结构中的每一项元素,逐一取代参数列表中与之对应的参数项,然后删除原来的参数。测试

函数组合成类(Combine Functions into class | 144)

js 复制代码
function base(aReading) {...}
function taxableCharge(aReading) {...}
function calculateBaseCharge(aReading) {...}
js 复制代码
class Reading {
    base() {...}
    taxableCharge() {...}
    calculateBaseCharge() {...}
}

动机

类,在大多数现代编程语言中都是基本的构造。它们把数据与函数捆绑到同一个环境中,将一部分数据与函数暴露给其他程序元素以便协作。

如果发现一组函数相互操作同一块数据(通常是将这块数据作为参数传递给函数),可以将这几个函数足见2一个类。类能明确地给这些函数提供一个共用的环境,在对象内部调用这些函数可以少传许多参数,从而简化函数调用。

做法

  • 运用封装记录(162)对多个函数共用的数据记录加以封装。
  • 对于使用该记录结构的每个函数,运用搬移函数(198)将其移入新类。(如果函数调用时传入的参数已经是新类的成员,则从参数列表中去除之。)
  • 用以处理该数据记录的逻辑可以用提炼函数(106)提炼出来,并移入新类。

函数组合成变换(Combine Functions into Transfrom | 149)

动机

在软件中经常有这类操作:一个数据项在一个函数中处理加工后,有进入到其他函数中继续处理加工,经过几番处理,才得到最终需要的结果(可能我们的初衷不这样,但由于后期新需求、功能的添加才形成这种现象)。

一个方式是采用数据变换(transform)函数:这种函数接受源数据作为输入,计算出所有的派生数据,将派生数据以字段形式填入输出数据。

函数组合成变换的替代方案是函数组合成类(144),后者的做法是先用源数据创建一个类,再把相关的计算逻辑搬移到类中。

做法

  • 创建一个变换函数,输入参数是需要变换的记录,并直接返回该记录的值。
  • 挑选一块逻辑,将其主体一如变换函数中,把结果作为字段添加到输出记录中。修改客户端代码,令其使用这个新字段
  • 测试
  • 针对其他相关的计算逻辑,重复上述步骤

拆分阶段(Split Phase | 154)

js 复制代码
const orderData = orderString.split(/\s+/);
const productPrice = priceList[orderData[0].split("-")[1]];
const orderPrice = parseInt(orderData[1]) * productPrice;
js 复制代码
const orderRecord = parseOrder(order);
const orderPrice = price(orderRecord, priceList);
function parseOrder(aString) {
const values = aString.split(/\s+/);
    return ({
        productID: values[0].split("-")[1],
        quantity: parseInt(values[1]),
    });
}
function price(order, priceList) {
	return order.quantity * priceList[order.productID];
}

动机

一段代码在同时处理两件不同的事。

最简洁的拆分方法之一,就是把一大段行为分成顺序执行的两个阶段。

做法

  • 将第二阶段的代码提炼成独立的函数
  • 测试
  • 引入一个中转数据结构,将其作为参数添加到提炼出的新函数的参数中
  • 测试
  • 逐一检查提炼出的"第二阶段函数"的每个参数。如果某个参数被第一阶段用到,将其移入中转数据结构。每次搬移之后都要执行测试。
  • 对第一阶段的代码运用提炼函数(106),让提炼出的函数返回中转数据结构。
相关推荐
YH行业报告分析4 分钟前
2026漂白紫胶粉市场深度:EUDR合规与脱蜡路线之争如何重构天然树脂供应链?
大数据·重构
workflower10 小时前
AI+ 新型ICT 基础设施是企业数智化转型的基石
大数据·人工智能·机器学习·重构·云计算·汽车
Navicat中国12 小时前
SQL 编辑器生产力:重构与实时错误检测
数据库·sql·重构·编辑器·navicat
志尊宝12 小时前
AI原生前端开发(005):Claude Code:终端AI重构工具深度使用
人工智能·重构·ai-native
学编程的小程1 天前
从正则替换到结构重构:KFS 异构 DDL 自动转换的实现思路
重构
xyzxyz5761 天前
从传统死链到AI语义引擎:新一代产品发现平台的技术重构
重构·独立开发者·开发者社区·builderoom·数字游民·产品发现平台
Sayai1 天前
ComfyUI+Flux ControlNet 实战:一张图拆线稿和深度图,双模型重构画面(12G 显存实测,附模型与工作流)
人工智能·重构·aigc·flux·controlnet·comfyui
ADAI_Bowen1 天前
当AI读懂图纸:ADAI,用垂直大模型重构建筑方案的迭代逻辑
人工智能·重构
风娜2 天前
用 Claude Code 重构遗留系统:从混乱到有序的实战指南
重构
深圳恒讯3 天前
维度重构:全球直播 vs 纯海外直播的云服务硬核配置
大数据·重构