- 浏览: 67916 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
bo_hai:
assembly:single 不包含源代码,只包含依赖的cl ...
关于创建可执行的jar文件 -
lsy:
相当详细的好文!
关于创建可执行的jar文件 -
steven0lisa:
学习了。不过在有动态代码生成的项目中,就要谨慎用了。
Hide Method(隐藏方法) -
steven0lisa:
好久没看源代码了,没记错的话,因为是由以下原因造成的:1.ke ...
编码最佳实践系列之二 -
steven0lisa:
最后一点确实很有艺术,学习了~~~
编码最佳实践系列之一
WHAT
在循环中,一次循环做了两件事情,将循环分解,重复这个循环,每次只做一件事情
潜在问题:性能问题,如果遇到性能问题,先让代码清晰可读,让你更快找到性能优化点,再做优化
本次重构涉及的基本重构较多,主要有:
- Split Loop(分解循环)
- Extract Method(提炼方法)
- Inline Temp(内联临时变量)
- Replace Temp With Query(用查询函数代替临时变量)
- Rename Temp(重命名临时变量)
- 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这个方法做了哪些事情,这就是重构的魅力,小步慢跑,一步一前进,但是,最最重要的前提是:我们在重构之前,必须有一个运行良好且速度较快的单元测试,让我们在测试、编码、重构不断地切换角色,更换帽子
总结
本重构虽然很简单,一些很小的改动,能够使代码可读性更强,更易维护,希望本实践对你有益,希望大家和我一起动手做重构,也希望你提出宝贵意见或反馈
发表评论
-
Encapsulate Classes with Factory(用工厂封装类)
2012-09-09 16:57 0TODO -
Compose Method(组合方法)
2012-09-09 16:55 0就是Extract Method()的运用 -
Chain Constructors(链构造函数)
2012-09-09 16:53 0同一个类中的各个构造函数,存在重复代码 做法 -
Substitute Algorithm(替换算法)
2012-09-09 14:30 0Substitute Algorithm -
Split Temporary Variable(分解临时变量)
2012-09-09 14:29 0Split Temporary Variable -
Separate Query from Modifier(将查询方法与修改方法分离)
2012-09-09 14:28 0Separate Query from Modifier ... -
Self Encapsulate Field(自封装字段)
2012-09-09 14:27 0Self Encapsulate Field -
Reverse Conditional(颠倒条件表达式)
2012-09-11 00:00 1142WHAT 如果只有一个then,而没有else的分支条件表达 ... -
Replace Type Code with Subclasses(用子类代替类型码)
2012-09-09 14:25 0Replace Type Code with Subcl ... -
Replace Type Code with State/Strategy(用 State/Strategy代替类型码)
2012-09-09 14:24 0State/Strategy -
Replace Type Code with Class(用类代替类型码)
2012-09-09 14:23 0Replace Type Code with Class ... -
Replace Temp with Query(用查询方法代替临时变量)
2012-09-09 14:23 0Replace Temp with Query -
Replace Subclass with Fields(用字段代替子类)
2012-09-09 14:22 0Replace Subclass with Fields ... -
Replace Static Variable with Parameter(用参数代替静态变量)
2012-09-09 14:21 0Replace Static Variable with ... -
Replace Recursion with Iteration(用迭代代替递归)
2012-09-09 14:20 0Replace Recursion with Itera ... -
Replace Record with Data Class(用数据类代替记录)
2012-09-09 14:19 0Replace Record with Data Cla ... -
Replace Parameter with Method(用方法代替参数)
2012-09-09 14:19 0Replace Parameter with Metho ... -
Replace Parameter with Explicit Methods(用显示方法代替参数)
2012-09-09 14:18 0Replace Parameter with Expli ... -
Replace Nested Conditional with Guard Clauses(用守卫语句代替嵌套条件语句)
2012-09-09 14:17 0Replace Nested Conditional w ... -
Replace Method with Method Object(用方法对象代替方法)
2012-09-09 14:16 0Replace Method with Method O ...
相关推荐
经验模态分解(希尔伯特黄变换)用于非平稳信号的分解和重构
本实验利用MATLAB2015进行编程,调用系统小波函数对信号进行分解,实现Mallat分解与重构算法对一维信号进行多层分解和重构。对信号进行多层分解可实现对信号的去噪和数据压缩处理,分解为小波函数的线性组合,阶数高...
四层分解,低频重构小波db4分解与重构.zip 小波db4分解与重构,四层分解,低频重构小波db4分解与重构.zip 小波db4分解与重构,四层分解,低频重构小波db4分解与重构.zip 小波db4分解与重构,四层分解,低频重构小波...
eemd分解得到各阶imf分量对信号重构
对经验模态分解后的各分量IMF进行重构代码,函数可直接调用。
资源名:用于信号的EMD、EEMD、VMD分解_vmd重构_故障诊断emd_故障诊断_故障重构_VMD信号重构 资源类型:matlab项目全套源码 源码介绍:用于信号的分解、降噪和重构,实现故障诊断 源码说明: 全部项目源码都是经过...
流场的本征正交分解与流场重构,包括读取数据,生成模态,模态系数,重构。代码清晰,每部分都有注释。
用于信号的EMD、EEMD、VMD分解_vmd重构_故障诊断emd_故障诊断_故障重构_VMD信号重构_源码.rar.rar
一维信号的小波分解重构,其中matlat的源代码是对序列做了扩展的
信号的小波分解与重构原理doc,附带matlab程序,可以参考
进行小波分析分解与快速傅里叶变换,对信号分解和重构
小波包分解与重构多种特征提取MATLAB代码 内容概要:该资源为博主自己编写,内含小波包分解与重构,小波包分解与重构后的频谱分析,小波包升降采样,小波包能量熵,小波包能量,小波包能量占比三种特征提取方法,...
基于数字信号的小波包的分解与重构代码,适合初学者学习
基于MATLAB个人编写的EMD分解及信号重构例子,显示hilbert谱分析图像、各级分解结果,并显示重构误差。
程序可实现图像的小波分解和重构,严格按照2:1比例分解和重构,避免了MATLAB软件提供的小波函数不是2:1抽样的缺点。
根据地震资料有效信号具有良好相关性的特性,采用SVD分解对LLE重构后的地震数据进行有效信号和噪声分离,剔除不相干的噪声,最终实现地震数据的随机噪声压制。在正演模型和真实地震资料上进行了实验,实验结果表明:...
老师布置的作业,对自己的自拍照进行二级小波重构与分解,并且对图像加高斯噪声、去燥
Matlab程序以及注释介绍小波包的分解与重构,通俗易懂
本ppt文件解释了小波变换以及小波在图像分解与重构中的应用