论坛首页 Java企业应用论坛

重构之分解循环

浏览 1759 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (1) :: 隐藏帖 (0)
作者 正文
   发表时间:2012-09-14  

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)两个属性

 

Java代码 
  1. package split.loop;  
  2.   
  3. public class Student {  
  4.   
  5.     /** 
  6.      * 年龄 
  7.      */  
  8.     private int age;  
  9.     /** 
  10.      * 成绩 
  11.      */  
  12.     private int grade;  
  13.       
  14.     public int getAge() {  
  15.         return age;  
  16.     }  
  17.   
  18.     public void setAge(int age) {  
  19.         this.age = age;  
  20.     }  
  21.   
  22.     public int getGrade() {  
  23.         return grade;  
  24.     }  
  25.   
  26.     public void setGrade(int grade) {  
  27.         this.grade = grade;  
  28.     }  
  29. }  
下面方法printValues(),用于计算学生们的平均年龄和总成绩
Java代码 
  1. package split.loop;  
  2.   
  3. public class SomeClass {  
  4.   
  5.     private Student[] students;  
  6.   
  7.     void printValues() {  
  8.         double averageAge = 0// 平均年龄  
  9.         int totalGrade = 0// 总成绩  
  10.   
  11.         for (int i = 0; i < students.length; i++) {  
  12.             averageAge += students[i].getAge();  
  13.             totalGrade += students[i].getGrade();  
  14.         }  
  15.         averageAge = averageAge / students.length;  
  16.   
  17.         System.out.println(averageAge);  
  18.         System.out.println(totalGrade);  
  19.     }  
  20.   
  21. }  
 

重构步骤

  • (手动)拷贝for循环语句,并除掉各自不相干部分代码,重构后的代码清单
Java代码 
  1. package split.loop;  
  2.   
  3. public class SomeClass {  
  4.   
  5.     private Student[] students;  
  6.   
  7.     void printValues() {  
  8.         double averageAge = 0// 平均年龄  
  9.         int totalGrade = 0// 总成绩  
  10.   
  11.         for (int i = 0; i < students.length; i++) {  
  12.             averageAge += students[i].getAge();  
  13.         }  
  14.   
  15.         for (int i = 0; i < students.length; i++) {  
  16.             totalGrade += students[i].getGrade();  
  17.         }  
  18.   
  19.         averageAge = averageAge / students.length;  
  20.   
  21.         System.out.println(averageAge);  
  22.         System.out.println(totalGrade);  
  23.     }  
  24.   
  25. }  
  • (手动)重新组织语句,让语义相近(for循环中使用到的局部变量)的语句放在一组,为其他重构做好准备,重构后的代码清单
Java代码 
  1. package split.loop;  
  2.   
  3. public class SomeClass {  
  4.   
  5.     private Student[] students;  
  6.   
  7.     void printValues() {  
  8.         double averageAge = 0// 平均年龄  
  9.         for (int i = 0; i < students.length; i++) {  
  10.             averageAge += students[i].getAge();  
  11.         }  
  12.         averageAge = averageAge / students.length;  
  13.           
  14.         int totalGrade = 0// 总成绩  
  15.         for (int i = 0; i < students.length; i++) {  
  16.             totalGrade += students[i].getGrade();  
  17.         }  
  18.   
  19.         System.out.println(averageAge);  
  20.         System.out.println(totalGrade);  
  21.     }  
  22.   
  23. }  

延伸重构

  • 本次重构,到此本应该结束,但是,我们还可以继续重构,让代码更优雅。我们观察到,上述代码清单中,printValues方法仍旧很长,到处充满了局部解释性变量(令人疑惑的东西),可以使用Replace Temp with Query(用查询函数代替临时变量,其实这个重构手法应用了两个基本的重构手法:Extract Method(提炼方法)Inline Temp(内联临时变量)),我们可以一步步来操作
    • 首先处理临时变量:averageAge,对上一组方法 Extract Method(提炼方法),使用快捷键(Alt + Shift +M)或者上下文菜单(Refactor -> Extract Method...),这里 averageAge ,在for循环前初始化,并在for循环中赋值,最后在for循环结束后,再次赋值。因此我们选中for循环以及前后语句,一起提炼,并给方法取个好名字( averageAge  ,这个方法是干什么的),提炼后的代码清单
Java代码 
  1. package split.loop;  
  2.   
  3. public class SomeClass {  
  4.   
  5.     private Student[] students;  
  6.   
  7.     void printValues() {  
  8.         double averageAge = averageAge();  
  9.           
  10.         int totalGrade = 0// 总成绩  
  11.         for (int i = 0; i < students.length; i++) {  
  12.             totalGrade += students[i].getGrade();  
  13.         }  
  14.   
  15.         System.out.println(averageAge);  
  16.         System.out.println(totalGrade);  
  17.     }  
  18.   
  19.     private double averageAge() {  
  20.         double averageAge = 0// 平均年龄  
  21.         for (int i = 0; i < students.length; i++) {  
  22.             averageAge += students[i].getAge();  
  23.         }  
  24.         averageAge = averageAge / students.length;  
  25.         return averageAge;  
  26.     }  
  27.   
  28. }  
  •  
    •  接下来,处理新提炼出来的方法averageAge,按照惯例,我们把方法的返回值命名为result,使用快捷键(Alt + Shit + R)或者上下文菜单(Refactor > Rename ...),改名之后,直接回车即可,代码清单
Java代码 
  1. package split.loop;  
  2.   
  3. public class SomeClass {  
  4.   
  5.     private Student[] students;  
  6.   
  7.     void printValues() {  
  8.         double averageAge = averageAge();  
  9.           
  10.         int totalGrade = 0// 总成绩  
  11.         for (int i = 0; i < students.length; i++) {  
  12.             totalGrade += students[i].getGrade();  
  13.         }  
  14.   
  15.         System.out.println(averageAge);  
  16.         System.out.println(totalGrade);  
  17.     }  
  18.   
  19.     private double averageAge() {  
  20.         double result = 0// 平均年龄  
  21.         for (int i = 0; i < students.length; i++) {  
  22.             result += students[i].getAge();  
  23.         }  
  24.         result = result / students.length;  
  25.         return result;  
  26.     }  
  27.   
  28. }  
  •  
    • 在往下,这里又涉及到一个问题,就是对局部变量赋值了多次,这时,需要应用Split Temporary Variable(手动操作,在第二次赋值的地方,重新声明变量,并更新接下来的引用),重构后的代码清单
Java代码 
  1. package split.loop;  
  2.   
  3. public class SomeClass {  
  4.   
  5.     private Student[] students;  
  6.   
  7.     void printValues() {  
  8.         double averageAge = averageAge();  
  9. font-size: 1em; margin: 0px 0px 0px 38px; padding: 0px 0px 0px 10px; border-left-width: 1px; border-left-style: solid; border-left-color: #d1d7dc; background-color: #faf
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics