引入解释性变量

开门见山

发现:你有一个复杂的表达式。

解决:将该复杂的表达式(或其中的部分)的结果放进一个临时变量,并以此变量名称来解释表达式用途。


//重构前
if((platform.toUpperCase().indexOf(MAC) > -1) &&
    (browser.toUpperCase().indexOf(IE) > -1) &&
    wasInitialized() && resize > 0)
{
    //do something
}

//重构后
final boolean isMacOs = platform.toUpperCase().indexOf(MAC) > -1;
final boolean isIEBrowser = browser.toUpperCase().indexOf(IE) > -1;
final boolean wasResize = resize > 0;
 
if(isMacOs && isIEBrowser && wasInitialized() && wasResize){
    //do something
}

动机

在某些情况下,表达式可能非常的复杂以至于难以阅读。这样,临时变量可以帮助你将表达式分解为比较容易管理的形式。

在条件逻辑中,引入解释性变量就显得比较有价值:你可以用这项重构将每个子句提炼出来,以一个良好命名的临时变量来解释对应条件子句的意义。另一种可能的情况是,对于那些比较长的算法,可以运用临时变量来解释每一步运算的意义。

本文的重构手法是比较常见的手法之一,但是对其的使用又不是那么的多。因为一般情况下,我们都可以使用提炼函数来解释一段代码的意义。毕竟临时变量只有在它所处的那个函数中才有意义,局限性较大,函数则可以在对象的整个生命周期中都有用,并且可被其它对象使用。但是,当局部变量使用提炼函数难以进行时,就可以尝试使用引入解释性变量。

 

 

做法

(1)声明一个final型的临时变量,将待分解之复杂表达式中的一部分动作的运算结果赋值给它。 (2)将表达式中的“运算结果”这一部分,替换为上述的临时变量。(如果被替换的这一部分在代码中重复出现,可以每次一个,逐一进行替换) (3)编译,测试。 (4)重复上述过程,处理其它类似部分。

示例

我们从一个简单计算开始:

//重构前
double price(){
    // 价格 = basePrice - quantity discount + shipping
    return _quantity * _itemPrice -
            Math.max(0, _quantity - 800) * _itemPrice * 0.15 +
            Math.min(_quantity * _itemPrice * 0.25, 100);
}

这段代码还是比较简单,不过现在要让其更加容易理解一些。 首先发现底价(basePrice)等于数量(quantity)乘以单价(item price)。于是可以把这一部分的计算结果放进一个临时变量中,同时将Math.min()函数中参数进行同样替换。

double price(){
    // 价格 = basePrice - quantity discount + shipping
    final double basePrice = _quantity * _itemPrice;
    return basePrice -
            Math.max(0, _quantity - 800) * _itemPrice * 0.15 +
            Math.min(basePrice * 0.25, 100);
}

然后,将批发折扣(quantity discount)的计算提炼出来,并将运算结果赋予临时变量。

double price(){
    // 价格 = basePrice - quantity discount + shipping
    final double basePrice = _quantity * _itemPrice;
    final double quantityDiscount = Math.max(0, _quantity - 800) * _itemPrice * 0.15;
    return basePrice -quantityDiscount+
            Math.min(basePrice * 0.25, 100);
}

最后,再把搬运费(shipping)计算提炼出来,并将运算结果赋予临时变量。

//重构后
double price(){
    // 价格 = basePrice - quantity discount + shipping
    final double basePrice = _quantity * _itemPrice;
    final double quantityDiscount = Math.max(0, _quantity - 800) * _itemPrice * 0.15;
    final double shipping = Math.min(basePrice * 0.25, 100);
    return basePrice - quantityDiscount + shipping;
}

运用提炼函数处理

对于上述代码,通常不以临时变量来解释其动作意图,而是更喜欢使用提炼函数。 让我们从头开始:

//重构前
double price(){
    // 价格 = basePrice - quantity discount + shipping
    return _quantity * _itemPrice -
            Math.max(0, _quantity - 800) * _itemPrice * 0.15 +
            Math.min(_quantity * _itemPrice * 0.25, 100);
}

现在把底价计算提炼到一个独立的函数中。

double price(){
    // 价格 = basePrice - quantity discount + shipping
    return basePrice() -
            Math.max(0, _quantity - 800) * _itemPrice * 0.15 +
            Math.min(basePrice() * 0.25, 100);
}
 
private double basePrice(){
    return _quantity * _itemPrice;
}

继续进行提炼,每次提炼一个新的函数。最后得到代码如下。

//重构后
double price(){
    // 价格 = basePrice - quantity discount + shipping
    return basePrice() - quantityDiscount() + shipping();
}
 
private double basePrice(){
    return _quantity * _itemPrice;
}
 
private double shipping(){
    return Math.min(basePrice() * 0.25, 100);
}
 
private double quantityDiscount(){
    return Math.max(0, _quantity - 800) * _itemPrice * 0.15;
}

本文主要介绍了重构手法——引入解释性变量。该重构方法主要是在提炼函数需要花费更大工作量时才使用。比如你有一个拥有大量局部变量的算法,那么使用提炼函数绝非易事。这时候就可以使用本文的方法来整理代码,然后再考虑下一步怎么办;一旦搞清楚代码逻辑后,就可以运用以查询取代临时变量把中间引入的那些临时变量去掉。
我想你会比较喜欢提炼函数,因为对于同一对象的任何部分,都可以根据自己的需要取用这些提炼出来的函数。一开始会把这些新函数声明为private;如果其它对象也需要它们,就可以轻易释放这些函数的访问控制。


<think>嗯,用户问的是关于代码重构的方法,比如提炼函数、内联函数这些,还提到了一些具体的处理临时变量的技巧。首先,我需要确认用户对这些概念的理解程度,可能需要先简要解释每个术语,然后提供例子。用户还问到哪里可以找到更多类似的方法,以及还有哪些其他的重构技巧,所以需要推荐一些权威资料,比如《重构》这本书,或者设计模式相关的资源。 用户提到的这些方法大部分来自Martin Fowler的《重构》一书,所以应该提到这本书作为主要参考。另外,可能需要补充一些其他常见的重构方法,比如提取接口、封装字段、使用多态替代条件语句等。还要注意用户提到的不同编程语言中的实现差异,比如Java的final,C++的inline,C#的情况,可能需要简要说明这些语言特性对重构的影响。 用户可能是在实际开发中遇到了代码臃肿、难以维护的问题,想通过重构改善代码质量。他们可能希望了解具体的步骤和最佳实践,以及如何选择适合的重构方法。深层需求可能是提高代码的可读性、可维护性和重用性,所以回答时需要强调这些重构方法带来的好处,并指导如何在实际中应用。 需要检查是否有遗漏的重构技术,比如重命名方法、移动方法、分解条件表达式等,确保覆盖用户可能需要的其他方法。同时,提醒用户在进行重构时要有测试保障,避免引入错误。最后,整理回答结构,分点解释每个方法,再补充其他方法,最后推荐资源,保持条理清晰。</think>你提到的这些代码重构方法主要源自经典著作《重构:改善既有代码的设计》(Martin Fowler著),以下是系统化的整理和补充: 一、你提到的核心重构方法解析 1. 提炼函数(Extract Method) - 适用场景:重复代码段、过长函数、需要注释解释的代码块 - 关键点:为提取的函数命名应体现业务意图而非实现细节 - 示例:将订单金额计算逻辑提取为 calculateOrderTotal() 2. 内联函数(Inline Method) vs 内联临时变量 - 函数内联:当函数体比函数名更易读时(如仅包含一个简单表达式) - 变量内联:当临时变量导致代码结构复杂时(如 temp = a+b; return temp*2;) 3. 变量处理技巧 - 以查询取代临时变量:用方法调用替代中间变量 - 引入解释性变量:分解复杂表达式(如将数学公式拆解为多个有意义的中间变量) - 分解临时变量:同一变量被多次赋值不同语义值时,应拆分为多个变量 4. 参数与架构级重构 - 移除参数赋值:保持参数输入的不可变性 - 建立新类取代函数:当函数膨胀到难以维护时,通过类来封装状态和行为 二、其他重要重构方法补充 1. 对象操作类 - 搬移方法(Move Method):将方法移到更合适的类中 - 提取接口(Extract Interface):定义清晰的抽象契约 - 引入参数对象:将多个关联参数封装为DTO 2. 条件逻辑优化 - 分解条件表达式:将复杂条件判断提取为方法 - 用多态替代条件语句:通过继承体系消除switch-case - 引入Null对象:消除重复的null检查 3. 继承体系重构 - 字段上移/下移:合理调整继承层次中的成员位置 - 提炼超类:抽取公共特性形成基类 - 折叠继承体系:合并不必要的继承层次 三、语言特性相关建议 - Java:合理使用final、record类型(JDK14+) - C#:利用扩展方法、LINQ表达式进行声明式重构 - 函数式语言:采用管道操作、不可变数据结构 四、学习资源推荐 1. 必读经典: - 《重构:改善既有代码的设计》(第2版新增JavaScript示例) - 《代码整洁之道》(Clean Code) 2. 实践指南: - IDE内置重构工具(IntelliJ IDEA/VS/Eclipse的重构菜单) - SonarQube代码质量检测规则 - 重构模式识别(代码异味检测清单) 3. 进阶方向: - 测试驱动开发(TDD)中的微重构实践 - 领域驱动设计(DDD)中的限界上下文重构 - 持续重构与代码演进策略 关键实践原则: 1. 小步快走:每次重构不超过5分钟,配合单元测试保障 2. 语义保持:重构不改变外部可见行为(可通过IDE自动化重构保证) 3. 上下文感知:结合业务领域进行命名和结构设计 4. 度量驱动:通过圈复杂度、代码重复率等指标识别重构点 示例:电商系统重构案例 原始代码: ```java public double calculatePrice(Order order) { double basePrice = order.getQuantity() * order.getItemPrice(); if (basePrice > 1000) { return basePrice * 0.95; } else { return basePrice * 0.98; } } ``` 重构步骤: 1. 提炼计算基础价格的函数 2. 用查询方法替代临时变量basePrice 3. 分解条件表达式为折扣率计算函数 4. 最终结果: ```java public double calculatePrice(Order order) { return getBasePrice(order) * getDiscountRate(order); } private double getBasePrice(Order order) { return order.getQuantity() * order.getItemPrice(); } private double getDiscountRate(Order order) { return getBasePrice(order) > 1000 ? 0.95 : 0.98; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值