重构笔记——以查询取代临时变量

本文介绍了一种重构手法——以查询取代临时变量,通过将临时变量的计算过程封装成独立函数,提高代码的复用性和清晰度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文是在学习中的总结,欢迎转载但请注明出处:http://blog.csdn.net/pistolove/article/details/42386305


        在上一篇文章中介绍了“ 内联临时变量“。本文将介绍“以查询取代临时变量”这种重构手法。

        下面让我们来学习这种重构手法吧。


开门见山

        发现:你的程序以一个临时变量保存某一表达式的运算结果。

        解决:将这个表达式提炼到一个独立函数中。将这个临时变量的所有引用点替换为对新函数的调用。

	//重构前
	double basePrice = _quantity * _itemPrice;
	if(basePrice > 5000)
		return basePrice * 0.95;
	else
		return basePrice * 0.98;
	//重构后
	if(basePrice() > 5000)
		return basePrice() * 0.95;
	else
		return basePrice() * 0.98;
	...
	
	double basePrice(){
		 return  _quantity * _itemPrice;
	}


动机

        我们都知道临时变量都是暂时的,而且只能在所属的函数中使用。所以它们会驱使你写出更长的函数,因为只有这样你才能访问到需要的临时变量。如果把临时变量替换为一个查询,那么同一个类中所有函数都将可以获得这份信息。这将带给你极大帮助,使你能够为这个类编写出更清晰的代码。

        该重构方法往往是你运用提炼函数之前必不可少的一个步骤。局部变量会使得代码难以提炼,所以应该尽可能把它们替换为查询式。比较简单的情况是:临时变量只被赋值一次,或者赋值给临时变量的表达式不受其它条件影响。


做法

        对于简单的情况:
(1)找出只被赋值依次的临时变量(如果被使用多次,考虑将其分割成多个变量,对应在后续重构方法中)。
(2)将该临时变量声明为final。
(3)编译(确保该临时变量的确只被赋值一次)
(4)将“对该临时变量赋值”之语句的等号右侧部分提炼到一个独立的函数中。(首先将函数声明为private。日后你可能会发现还有好多地方需要使用它,那时放松对它的保护也很容易;确保提炼出的函数无任何副作用,即该函数不修改任何对象内容)
(5)编译,测试。
(6)对该临时变量实施“内联临时变量”重构方法。

        我们常常使用临时变量保存循环中的累加信息。在这种情况下,整个循环都可以被提炼为一个独立的函数,这可以减少原函数中几行循环逻辑代码。该手法的使用可能会让你担心性能上的问题。就像和其它性能问题一样,我们现在不用管它,因为十有八九根本没有造成任何影响。就算真的出问题了,你也可以在优化时期解决它。代码组织良好,你也会发现更有效的优化方案;如果没有进行重构,好的优化方案就可能与你失之交臂。


示例

        我们从一个简单函数开始:
//重构前
double getPrice() {
	int basePrice = _quantity * _itemPrice;
	double discountFactor;
	if (basePrice > 5000)
		discountFactor = 0.95;
	else
		discountFactor = 0.98;
	return basePrice * discountFactor;
}
        我们希望将其中的两个临时变量都去掉。当然,每次一个进行处理。
        代码虽然简单,但是还是有必要先把临时变量声明为final,检查它们是否的确只被赋值一次。
double getPrice() {
	final int basePrice = _quantity * _itemPrice;
	final double discountFactor;
	if (basePrice > 5000)
		discountFactor = 0.95;
	else
		discountFactor = 0.98;
	return basePrice * discountFactor;
}
        这样,一旦有问题编译器就会警告。之所以需要这个做是因为如果临时不安路口不只被赋值一次,就不该进行这项重构。
        下面开始替换临时变量,每次一个,不要着急,按部就班。
        首先,把赋值动作右侧表达式提炼出来:
double getPrice() {
	final int basePrice = basePrice();
	final double discountFactor;
	if (basePrice > 5000)
		discountFactor = 0.95;
	else
		discountFactor = 0.98;
	return basePrice * discountFactor;
}

private double basePrice() {
	return _quantity * _itemPrice;
}
        然后,编译并测试,再开始使用内联临时变量。
        首先把临时变量basePrice的第一个引用点替换掉:
double getPrice() {
	final int basePrice = basePrice();
	final double discountFactor;
	if (basePrice() > 5000)
		discountFactor = 0.95;
	else
		discountFactor = 0.98;
	return basePrice * discountFactor;
}
        编译、测试,然后进行下一个。由于下一个是最后一个引用点,所以把basePrice临时变量的声明式一并去掉:
double getPrice() {
	final double discountFactor;
	if (basePrice() > 5000)
		discountFactor = 0.95;
	else
		discountFactor = 0.98;
	return basePrice()  * discountFactor;
}
        完成basePrice之后,以类似的方法提炼出discountFactor():
double getPrice() {
	final double discountFactor = discountFactor() ;
	return basePrice()  * discountFactor;
}

private double discountFactor(){
	if (basePrice() > 5000)
		return 0.95;
	else
		return 0.98;
}
        你会发现,如果没有把临时变量basePrice替换为一个查询式,将多么难以提炼discountFactor()!
        最终,getPrice()变成这样:
//重构后
double getPrice() {
	return basePrice()  * discountFactor();
}

         本文主要介绍了重构手法——以查询取代临时变量。通过重构后代码可见,代码变的相当地简洁了,代码所表达的意图也变的很清晰了。一旦代码中出现问题,修改起来也比较轻松,因为代码很简短、清晰。我想你是否也慢慢喜欢上重构了呢!如果是,那么就一起学习、进步,让代码变得漂亮吧!
        最后,希望本文对你有所帮助。有问题可以留言,谢谢。(PS:下一篇将介绍重构笔记——引入解释性变量)


重构笔记文章如下

       重构笔记——入门篇

       重构笔记——代码的坏味道(上

       重构笔记——代码的坏味道(下)

       重构笔记——构筑测试体

       重构笔记——提炼函数

       重构笔记——内联函数

       重构笔记——内联临时变量


<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; } ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值