)
如果发现一个函数的代码很长, 很可能的一种情况是这个函数做了很多事情, 找找看函数中有没有注释, 往往注释都是为了解释下面一块代码做的什么事情, 可以考虑将这块代码提炼(Extract)成一个独立的函数.这样做的好处不言而喻, 是面向对象五大基本原则中的单一职责原则 (Single Responsibility Principle), 比较长的函数被拆分成一个个小函数, 将有利于代码被复用.冲动前:1234567891011publicvoidPrint(Employee employee){//print employees informationConsole.WriteLine(Name: employee.Name);Console.WriteLine(Sex: employee.Sex);Console.WriteLine(Age: employee.Age);//print employees salaryConsole.WriteLine(Salary: employee.Salary);Console.WriteLine(Bonus: employee.Bonus);}冲动后:1234567891011121314151617181920publicvoidPrint(Employee employee){//print employees informationPrintInfo(employee);//print employees salaryPrintSalary(employee);}publicvoidPrintInfo(Employee employee){Console.WriteLine(Name: employee.Name);Console.WriteLine(Sex: employee.Sex);Console.WriteLine(Age: employee.Age);}publicvoidPrintSalary(Employee employee){Console.WriteLine(Salary: employee.Salary);Console.WriteLine(Bonus: employee.Bonus);}2. Inline Method (将函数内联)解释:有些函数很短, 只有一两行, 而且代码的意图也非常明显, 这时可以考虑将这个函数干掉, 直接使用函数中的代码.物件中过多的方法会让人感到不舒服, 干掉完全不必要的函数后代码会更简洁.冲动前:123456789publicboolIsDeserving(intscore){returnIsScoreMoreThanSixty(score);}publicboolIsScoreMoreThanSixty(intscore){return(score 60);}冲动后:1234publicboolIsDeserving(intscore){return(score 60) ;}3. Inline Temp (将临时变量内联)解释:如果有一个临时变量 (Temp)用来表示某个函数的返回值, 一般来说, 这样的做法挺好的. 但如果这个临时变量实在多余, 将这个临时变量内联之后毫不影响代码的阅读, 甚至这个临时变量妨碍了其它重构工作, 就应该将这个临时变量内联化.把这个临时变量干掉的好处在于减少了函数的长度, 有时可以让其它重构工作更顺利的进行.冲动前:12intsalary employee.Salary;return(salary 10000);冲动后:1return(employee.Salary 10000);4. Replace Temp With Query (用查询式代替临时变量)解释:程序中有一个临时变量(Temp)用来保存某个表达式的计算结果, 将这个计算表达式提炼(Extract)到一个独立的函数(即查询式Query)中, 将这个临时变量所有被调用的地方换成对新函数(Query)的调用, 新函数还可以被其它函数使用.好处在于减少函数长度, 增加代码复用率, 有利于代码进一步的重构. 并且注意Replace Temp With Query往往是Extract Method之前必不可少的步骤, 因为局部变量会使代码不太容易被提炼, 所以在进行类似的重构前可以将它们替换成查询式.下面的这个例子不是很有必要使用Replace Temp With Query, 主要展示如何Replace Temp With Query.试想冲动前函数中有很多个代码块都使用到totalPrice, 突然有一天我发现这个函数太长, 我需要将这一块块的代码提炼成单独的函数, 这样就需要将 totalPrice price * num; 放到每一个提炼出来的函数中. 而如果原来函数中使用的是查询式, 就不存在这个问题. 如果查询式中的计算量很大, 也不建议使用Replace Temp With Query.冲动前:12345678publicdoubleFinalPrice(doubleprice,intnum){doubletotalPrice price * num;if(totalPrice 100)returntotalPrice * 0.8;elsereturntotalPrice * 0.9;}冲动后:1234567891011publicdoubleFinalPrice(doubleprice,intnum){if(TotalPrice(price, num) 100)returnTotalPrice(price, num) * 0.8;elsereturnTotalPrice(price, num) * 0.9;}publicdoubleTotalPrice(doubleprice,intnum){returnprice * num;}5. Introduce Explaining Variable (引入可以理解的变量)解释:很多时候在条件逻辑表达式中, 很多条件令人难以理解它的意义, 为什么要满足这个条件? 不清楚. 可以使用Introduce Explaining Variable将每个条件子句提炼出来, 分别用一个恰当的临时变量名表示条件子句的意义.好处在于增加了程序的可读性.冲动前:12345if((operateSystem.Contains(Windows))(browser.Contatins(IE))){//do something}冲动后:123456boolisWindowsOS operateSystem.Contains(Windows);boolisIEBrowser browser.Contatins(IE);if(isWindowsOS isIEBrowser){//do something}6. Split Temporary Variable (撇清临时变量)解释:例如代码中有个临时变量在函数上面某处表示长方形周长, 在函数下面被赋予面积, 也就是这个临时变量被赋值超过一次, 且表示的不是同一种量. 应该针对每次赋值, 分配一个独立的临时变量.一个变量只应表示一种量, 否则会令代码阅读者感到迷惑.冲动前:1234doubletemp (width height) * 2;//do somethingtemp width * height;//do something冲动后:1234doubleperimeter (width height) * 2;//do somethingdoublearea width * height;//do something