`
aaronjiu_00
  • 浏览: 69197 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

重构《Split Loop(分解循环)》

阅读更多

WHAT

在循环中,一次循环做了两件事情,将循环分解,重复这个循环,每次只做一件事情

 

潜在问题:性能问题,如果遇到性能问题,先让代码清晰可读,让你更快找到性能优化点,再做优化

 

本次重构涉及的基本重构较多,主要有:

  1. Split Loop(分解循环)
  2. Extract Method(提炼方法)
  3. Inline Temp(内联临时变量)
  4. Replace Temp With Query(用查询函数代替临时变量)
  5. Rename Temp(重命名临时变量)
  6. Split Temporary Variable(分解临时变量)

HOW

重构前的代码清单

Student类是一个哑对象,表示一个学生,有年龄(age)和成绩(grade)两个属性

 

package split.loop;

public class Student {

	/**
	 * 年龄
	 */
	private int age;
	/**
	 * 成绩
	 */
	private int grade;
	
	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public int getGrade() {
		return grade;
	}

	public void setGrade(int grade) {
		this.grade = grade;
	}
}
下面方法printValues(),用于计算学生们的平均年龄和总成绩
package split.loop;

public class SomeClass {

	private Student[] students;

	void printValues() {
		double averageAge = 0; // 平均年龄
		int totalGrade = 0; // 总成绩

		for (int i = 0; i < students.length; i++) {
			averageAge += students[i].getAge();
			totalGrade += students[i].getGrade();
		}
		averageAge = averageAge / students.length;

		System.out.println(averageAge);
		System.out.println(totalGrade);
	}

}
 

重构步骤

  • (手动)拷贝for循环语句,并除掉各自不相干部分代码,重构后的代码清单
package split.loop;

public class SomeClass {

	private Student[] students;

	void printValues() {
		double averageAge = 0; // 平均年龄
		int totalGrade = 0; // 总成绩

		for (int i = 0; i < students.length; i++) {
			averageAge += students[i].getAge();
		}

		for (int i = 0; i < students.length; i++) {
			totalGrade += students[i].getGrade();
		}

		averageAge = averageAge / students.length;

		System.out.println(averageAge);
		System.out.println(totalGrade);
	}

}
  • (手动)重新组织语句,让语义相近(for循环中使用到的局部变量)的语句放在一组,为其他重构做好准备,重构后的代码清单
package split.loop;

public class SomeClass {

	private Student[] students;

	void printValues() {
		double averageAge = 0; // 平均年龄
		for (int i = 0; i < students.length; i++) {
			averageAge += students[i].getAge();
		}
		averageAge = averageAge / students.length;
		
		int totalGrade = 0; // 总成绩
		for (int i = 0; i < students.length; i++) {
			totalGrade += students[i].getGrade();
		}

		System.out.println(averageAge);
		System.out.println(totalGrade);
	}

}

延伸重构

  • 本次重构,到此本应该结束,但是,我们还可以继续重构,让代码更优雅。我们观察到,上述代码清单中,printValues方法仍旧很长,到处充满了局部解释性变量(令人疑惑的东西),可以使用Replace Temp with Query(用查询函数代替临时变量,其实这个重构手法应用了两个基本的重构手法:Extract Method(提炼方法)Inline Temp(内联临时变量)),我们可以一步步来操作
    • 首先处理临时变量:averageAge,对上一组方法 Extract Method(提炼方法),使用快捷键(Alt + Shift +M)或者上下文菜单(Refactor -> Extract Method...),这里 averageAge ,在for循环前初始化,并在for循环中赋值,最后在for循环结束后,再次赋值。因此我们选中for循环以及前后语句,一起提炼,并给方法取个好名字( averageAge  ,这个方法是干什么的),提炼后的代码清单
package split.loop;

public class SomeClass {

	private Student[] students;

	void printValues() {
		double averageAge = averageAge();
		
		int totalGrade = 0; // 总成绩
		for (int i = 0; i < students.length; i++) {
			totalGrade += students[i].getGrade();
		}

		System.out.println(averageAge);
		System.out.println(totalGrade);
	}

	private double averageAge() {
		double averageAge = 0; // 平均年龄
		for (int i = 0; i < students.length; i++) {
			averageAge += students[i].getAge();
		}
		averageAge = averageAge / students.length;
		return averageAge;
	}

}
  •  
    •  接下来,处理新提炼出来的方法averageAge,按照惯例,我们把方法的返回值命名为result,使用快捷键(Alt + Shit + R)或者上下文菜单(Refactor > Rename ...),改名之后,直接回车即可,代码清单
package split.loop;

public class SomeClass {

	private Student[] students;

	void printValues() {
		double averageAge = averageAge();
		
		int totalGrade = 0; // 总成绩
		for (int i = 0; i < students.length; i++) {
			totalGrade += students[i].getGrade();
		}

		System.out.println(averageAge);
		System.out.println(totalGrade);
	}

	private double averageAge() {
		double result = 0; // 平均年龄
		for (int i = 0; i < students.length; i++) {
			result += students[i].getAge();
		}
		result = result / students.length;
		return result;
	}

}
  •  
    • 在往下,这里又涉及到一个问题,就是对局部变量赋值了多次,这时,需要应用Split Temporary Variable(手动操作,在第二次赋值的地方,重新声明变量,并更新接下来的引用),重构后的代码清单
package split.loop;

public class SomeClass {

	private Student[] students;

	void printValues() {
		double averageAge = averageAge();
		
		int totalGrade = 0; // 总成绩
		for (int i = 0; i < students.length; i++) {
			totalGrade += students[i].getGrade();
		}

		System.out.println(averageAge);
		System.out.println(totalGrade);
	}

	private double averageAge() {
		double result = 0; // 平均年龄
		for (int i = 0; i < students.length; i++) {
			result += students[i].getAge();
		}
		double result1 = result / students.length;
		return result1;
	}

}
  •  
    • 这时,我们注意到,result1变量只被赋值一次,我们将它内联,应用Inline temp (内联临时变量 ),选中变量,使用快捷键(Alt +Shift + I)或者上下文菜单(Refactor > Inline ...) ,重构后的代码清单
package split.loop;

public class SomeClass {

	private Student[] students;

	void printValues() {
		double averageAge = averageAge();
		
		int totalGrade = 0; // 总成绩
		for (int i = 0; i < students.length; i++) {
			totalGrade += students[i].getGrade();
		}

		System.out.println(averageAge);
		System.out.println(totalGrade);
	}

	private double averageAge() {
		double result = 0; // 平均年龄
		for (int i = 0; i < students.length; i++) {
			result += students[i].getAge();
		}
		return result / students.length;
	}

}  
  •  
    •  这时,我们回到调用处,发现printValues方法中,averageAge 变量只被赋值一次,我们也把它Inline掉,重构后的代码清单
package split.loop;

public class SomeClass {

	private Student[] students;

	void printValues() {
		int totalGrade = 0; // 总成绩
		for (int i = 0; i < students.length; i++) {
			totalGrade += students[i].getGrade();
		}

		System.out.println(averageAge());
		System.out.println(totalGrade);
	}

	private double averageAge() {
		double result = 0; // 平均年龄
		for (int i = 0; i < students.length; i++) {
			result += students[i].getAge();
		}
		return result / students.length;
	}

}
  •  接下来,同样方法处理这个变量:totalGrade
    • 首先,提炼方法,并重名返回结果变量名
package split.loop;

public class SomeClass {

	private Student[] students;

	void printValues() {
		int totalGrade = totalGrade();

		System.out.println(averageAge());
		System.out.println(totalGrade);
	}

	private double averageAge() {
		double result = 0; // 平均年龄
		for (int i = 0; i < students.length; i++) {
			result += students[i].getAge();
		}
		return result / students.length;
	}

	private int totalGrade() {
		int result = 0; // 总成绩
		for (int i = 0; i < students.length; i++) {
			result += students[i].getGrade();
		}
		return result;
	}

}
  •  
    • 在调用处,inline临时变量totalGrade,重构后的的代码清单
package split.loop;

public class SomeClass {

	private Student[] students;

	void printValues() {
		System.out.println(averageAge());
		System.out.println(totalGrade());
	}

	private double averageAge() {
		double result = 0; // 平均年龄
		for (int i = 0; i < students.length; i++) {
			result += students[i].getAge();
		}
		return result / students.length;
	}

	private int totalGrade() {
		int result = 0; // 总成绩
		for (int i = 0; i < students.length; i++) {
			result += students[i].getGrade();
		}
		return result;
	}

}
  •   这时,代码已经相当清晰了,我们不需要看方法注释,以及方法内部实现,就可以知道printValues这个方法做了哪些事情,这就是重构的魅力,小步慢跑,一步一前进,但是,最最重要的前提是:我们在重构之前,必须有一个运行良好且速度较快的单元测试,让我们在测试、编码、重构不断地切换角色,更换帽子

总结

本重构虽然很简单,一些很小的改动,能够使代码可读性更强,更易维护,希望本实践对你有益,希望大家和我一起动手做重构,也希望你提出宝贵意见或反馈

分享到:
评论

相关推荐

    emd.rar_emd信号重构_emd分解重构_emd重构_经验模态分解

    经验模态分解(Empirical Mode Decomposition,简称EMD)是一种强大的数据分析技术,尤其适用于处理非线性、非平稳信号。EMD由Nasa的科学家Hilbert和Huang在1998年提出,其核心思想是将复杂的信号自适应地分解为一...

    一维信号的分解和重构的Mallat分解与重构算法(matlab)

    本实验利用MATLAB2015进行编程,调用系统小波函数对信号进行分解,实现Mallat分解与重构算法对一维信号进行多层分解和重构。对信号进行多层分解可实现对信号的去噪和数据压缩处理,分解为小波函数的线性组合,阶数高...

    【EMD重构】.rar_EMD重构函数_IMF变量重构_tomorrowi4n_模态分解_重构

    标题中的"【EMD重构】.rar"指的是包含EMD重构过程的压缩文件,而"EMD重构函数"是指在处理EMD分解后的IMF分量时使用的特定函数。"IMF变量重构"则是指将分解得到的各个IMF重新组合成原始信号的过程。"tomorrowi4n"可能...

    POD.rar_POD 重构_POD正交分解_we75t_本征正交分解_流场重构

    在这个主题中,"POD.rar_POD 重构_POD正交分解_we75t_本征正交分解_流场重构" 提到了一种名为“主成分分析”(Principal Component Analysis,PCA)在流体力学中的应用,通常称为“正交分解”(Proper Orthogonal ...

    小波db4分解与重构.zip

    四层分解,低频重构小波db4分解与重构.zip 小波db4分解与重构,四层分解,低频重构小波db4分解与重构.zip 小波db4分解与重构,四层分解,低频重构小波db4分解与重构.zip 小波db4分解与重构,四层分解,低频重构小波...

    eemd分解得到各阶imf分量对信号重构

    eemd分解得到各阶imf分量对信号重构

    用于信号的EMD、EEMD、VMD分解_vmd重构_故障诊断emd_故障诊断_故障重构_VMD信号重构

    资源名:用于信号的EMD、EEMD、VMD分解_vmd重构_故障诊断emd_故障诊断_故障重构_VMD信号重构 资源类型:matlab项目全套源码 源码介绍:用于信号的分解、降噪和重构,实现故障诊断 源码说明: 全部项目源码都是经过...

    用于信号的EMD、EEMD、VMD分解_vmd重构_故障诊断emd_故障诊断_故障重构_VMD信号重构_源码.rar.rar

    标题中的“用于信号的EMD、EEMD、VMD分解_vmd重构_故障诊断emd_故障诊断_故障重构_VMD信号重构_源码.rar.rar”揭示了该压缩包文件包含的是与信号处理相关的源代码,特别是涉及了三种重要的信号分解方法:Empirical ...

    信号的小波分解与重构原理

    信号的小波分解与重构原理 小波分解是一种信号处理技术,它可以将信号分解成多个分量,每个分量对应一个特定的频率范围。小波分解的主要应用包括信号压缩、去噪、特征提取等。在小波分解中,我们通常使用离散小波...

    HHT_HHT变换实例_HHTMATLAB_hht_emd分解重构_EMD分解_

    【标题】:“HHT_HHT变换实例_HHTMATLAB_hht_emd分解重构_EMD分解_”这个标题指的是关于HHT(Hilbert-Huang变换)的一个实际应用案例,使用MATLAB编程语言进行实现。HHT是一种先进的信号处理技术,主要用于非线性、...

    小波包分解重构

    基于数字信号的小波包的分解与重构代码,适合初学者学习

    NMF 非负矩阵分解 图像重构

    如果NMF分解得当,重构后的图像应该与原始图像非常相似,尽管可能会有一些失真。 在计算机视觉领域,NMF的应用广泛,包括但不限于: 1. 图像分类:通过提取图像的非负特征,可以用于分类任务。 2. 图像去噪:NMF...

    小波的分解与重构VC实现

    与分解类似,重构同样需要通过循环来逐步恢复信号,但方向相反。从最底层的细节信号开始,逐步向上合并,最终得到原始信号。重构过程中,信号长度逐渐增加,直至恢复到原始信号长度。 ### 代码解析 给出的代码示例...

    wavelet_小波分解与重构_

    小波分解与重构是信号处理领域中的重要技术,它结合了频域分析和时域分析的优势,能够在不同尺度和时间分辨率下对信号进行分析。在本案例中,使用的是db10小波函数进行数据的分解,分解层数达到9层,这允许我们深入...

    小波分解和重构MATLAB程序

    小波分解和重构是信号处理领域中的重要技术,特别是在图像分析和压缩中有着广泛应用。MATLAB作为一种强大的数学计算和数据分析工具,提供了丰富的函数库来支持小波分析。本程序主要针对MATLAB内置小波函数非2:1抽样...

    小波修改_傅里叶重构_对信号分解和重构_进行小波分析分解与快速傅里叶变换_

    本文将深入探讨标题和描述中提及的“小波修改”、“傅里叶重构”以及“对信号分解和重构”的概念,并结合快速傅里叶变换(FFT)进行阐述。 首先,让我们了解小波分析。小波分析是一种数学方法,它能够同时在时间和...

    采用haar小波实现一维信号的分解与重构(mallat算法):

    在实现一维信号的分解与重构过程中,首先,我们需要对原始信号进行层次分解。这通常包括以下步骤: 1. **分解**:从最高分辨率(原始信号)开始,通过应用Haar小波滤波器,将信号分解为细节(高频)和近似(低频)两...

    小波变换分解与重构

    在这个主题“小波变换分解与重构”中,我们主要关注如何利用小波变换对图像进行模糊处理、噪声添加以及恢复,以及在三级重构过程中的LL1、LL2、LL3层的应用。 首先,图像的模糊通常是通过应用平均滤波器来实现的。...

    signalwaveletdecopositon.rar_小波 重构_小波分解_小波分解 matlab_小波分解重构

    一维信号的小波分解重构,其中matlat的源代码是对序列做了扩展的

Global site tag (gtag.js) - Google Analytics