前言
前两天写了一篇程序猿也爱学英语(上),有图有真相的文章,写作那篇文章只是自己一时兴起,或者说是自己的兴趣使然。文中的观点只是自己的学习心得和体会,属一家之言且鉴于本人不是学英语出身,所以也肯定有不正确的地方,也欢迎大家积极讨论并给我留言,再次感谢大家的热烈支持。关于大家询问下篇的发布问题,我想我会尽力在周末完成。
这几天由于刚发布完项目,所以有比较充裕的时间整理自己的知识库,发现三年多以前学习并记录了31天重构系列笔记,至今仍回味无穷,索性重新阅读、纠正错误并重新排版整理出来,希望再次和大家一起分享。
对31天重构系列文章最早接触是在2009年10月份,由于当时没有订阅Sean Chambers的 blog,所以是在国外的社区上闲逛的时候链接过去的。基于文章中的重构点都比较常用,所以一口气看完了整个系列,同时由于这些重构Tips基本上项目都在使用,只是我们没有专门把它标示和整理出来,所以当时也没有引起多大的重视。
但就在三年前,当时我们在做一个WPF的重构项目且鉴于团队成员技术和经验参差不齐,非常必要专门整理一个重构的纲要,所以就收集和整理了很多的资料(31天重构也在其中)。当然这个系列除了用语重构Tips之外,也非常适合做新系统的代码规范参考。总而言之:只要有代码的地方,这个重构规范就很有价值。同时鉴于当时自己刚到新加坡这个美丽的城市,没有亲戚或者朋友,周末也不想出去闲逛,所以才静下心来用了足足两天时间学习并写完了这个系列笔记。
31天重构这个系列和《代码大全》、《重构:改善既有代码的设计》比较起来最大的特点就是比较简单且浅显易懂。我这系列文章也都是学习并概括Sean Chambers的31天重构的知识要领,所以如果大家对这个笔记有任何的问题或者异议也可以指出,或者大家可以直接去看原文(即可掌握了技术,又可以学习英语!):
http://www.lostechies.com/blogs/sean_chambers/archive/2009/07/31/31-days-of-refactoring.aspx。
代码下载地址:http://github.com/schambers/days-of-refactoring
目录
- 前言
- 目录
- 31天重构学习笔记1. 封装集合
- 31天重构学习笔记2. 移动方法
- 31天重构学习笔记3. 提升方法
- 31天重构学习笔记4. 降低方法
- 31天重构学习笔记5. 提升字段
- 31天重构学习笔记6. 降低字段
- 31天重构学习笔记7. 重命名(方法,类,参数)
- 31天重构学习笔记8. 使用委派代替继承
- 31天重构学习笔记9. 提取接口
- 31天重构学习笔记10. 提取方法
- 31天重构学习笔记11. 使用策略类
- 31天重构学习笔记12. 分解依赖
- 31天重构学习笔记13. 提取方法对象
- 31天重构学习笔记14. 分离职责
- 31天重构学习笔记15. 移除重复内容
- 31天重构学习笔记16. 封装条件
- 31天重构学习笔记17. 提取父类
- 31 天重构学习笔记18. 使用条件判断代替异常
- 31天重构学习笔记19. 提取工厂类
- 31天重构学习笔记20. 提取子类
- 31天重构学习笔记21. 合并继承
- 31天重构学习笔记22. 分解方法
- 31天重构学习笔记23. 引入参数对象
- 31天重构学习笔记24. 分解复杂判断
- 31天重构学习笔记25. 引入契约式设计
- 31天重构学习笔记26. 避免双重否定
- 31天重构学习笔记27. 去除上帝类
- 31天重构学习笔记28. 为布尔方法命名
- 31天重构学习笔记29. 去除中间人对象
- 31天重构学习笔记30. 尽快返回
- 31天重构学习笔记31. 使用多态代替条件判断
- 代码及PDF下载
1. 封装集合
概念:本文所讲的封装集合就是把集合进行封装,只提供调用端需要的接口。
正文:在很多时候,我们都不希望把一些不必要的操作暴露给调用端,只需要给它所需要的操作或数据就行,那么做法就是封装。这个重构在微软的代码库也经常遇到。比如最经典的属性对字段的封装就是一个很好的例子,那么下面我们将看到对集合的封装,如下代码所示,调用端只需要一个集合的信息,而我们则提供了一个IList的集合,大家都知道IList具有对集合的所有操作,所以这会带来很多隐患,最好的做法就是对它进行重构。
using System.Collections.Generic; namespace LosTechies.DaysOfRefactoring.EncapsulateCollection.Before { public class Order { private List<OrderLine> _orderLines; private double _orderTotal; public IList<OrderLine> OrderLines { get { return _orderLines; } } public void AddOrderLine(OrderLine orderLine) { _orderTotal += orderLine.Total; _orderLines.Add(orderLine); } public void RemoveOrderLine(OrderLine orderLine) { orderLine = _orderLines.Find(o => o == orderLine); if (orderLine == null) return; _orderTotal -= orderLine.Total; _orderLines.Remove(orderLine); } } public class OrderLine { public double Total { get; private set; } } }
那么重构之后,我们把IList换成了IEnumerable,大家都知道只包括一个返回值为IEnumerator的GetEnumerator()方法,所以这样只能遍历取出它的值,而不能对这个集合做出改变,这正是我们所需要的结果,具体代码如下:
using System.Collections.Generic; namespace LosTechies.DaysOfRefactoring.EncapsulateCollection.After { public class Order { private List<OrderLine> _orderLines; private double _orderTotal; public IEnumerable<OrderLine> OrderLines { get { return _orderLines; } } public void AddOrderLine(OrderLine orderLine) { _orderTotal += orderLine.Total; _orderLines.Add(orderLine); } public void RemoveOrderLine(OrderLine orderLine) { orderLine = _orderLines.Find(o => o == orderLine); if (orderLine == null) return; _orderTotal -= orderLine.Total; _orderLines.Remove(orderLine); } } public class OrderLine { public double Total { get; private set; } } }
总结:这个例子很容易让我们想到以前系统间耦合常喜欢用数据库。每个系统都会操作数据库,并且有些系统还会对数据库的表结构或字段进行修改,那么这很容易就会造成维护的地狱,很明智的一个做法就是使用SOA来隔开这些耦合,让一些只需要数据展示的系统得到自己需要的数据即可。
2. 移动方法
概念:本文所讲的移动方法就是方法放在合适的位置(通常指放在合适的类中)。
正文:移动方法是一个很简单也很常见的重构,只要是系统就会存在很多类,那么类里面包括很多方法,如果一个方法经常被另外一个类使用(比本身的类使用还多)或者这个方法本身就不应该放在这个类里面,那么这个适合应该考虑把它移到合适的类中。代码如下:
namespace LosTechies.DaysOfRefactoring.MoveMethod.Before { public class BankAccount { public BankAccount(int accountAge, int creditScore, AccountInterest accountInterest) { AccountAge = accountAge; CreditScore = creditScore; AccountInterest = accountInterest; } public int AccountAge { get; private set; } public int CreditScore { get; private set; } public AccountInterest AccountInterest { get; private set; } public double CalculateInterestRate() { if (CreditScore > 800) return 0.02; if (AccountAge > 10) return 0.03; return 0.05; } } public class AccountInterest { public BankAccount Account { get; private set; } public AccountInterest(BankAccount account) { Account = account; } public double InterestRate { get { return Account.CalculateInterestRate(); } } public bool IntroductoryRate { get { return Account.CalculateInterestRate() < 0.05; } } } }
移动以后大家可以看到BankAccount类的职责也单一,同时CalculateInterestRate也放到了经常使用且适合它的类中了,所以此重构是一个比较好的重构,能让整个代码变得更加合理。
namespace LosTechies.DaysOfRefactoring.MoveMethod.After { public class AccountInterest { public BankAccount Account { get; private set; } public AccountInterest(BankAccount account) { Account = account; } public double InterestRate { get { return CalculateInterestRate(); } } public bool IntroductoryRate { get { return CalculateInterestRate() < 0.05; } } public double CalculateInterestRate() { if (Account.CreditScore > 800) return 0.02; if (Account.AccountAge > 10) return 0.03; return 0.05; } } }
namespace LosTechies.DaysOfRefactoring.MoveMethod.After { public class BankAccount { public BankAccount(int accountAge, int creditScore, AccountInterest accountInterest) { AccountAge = accountAge; CreditScore = creditScore; AccountInterest = accountInterest; } public int AccountAge { get; private set; } public int CreditScore { get; private set; } public AccountInterest AccountInterest { get; private set; } } }
总结:这个重构法则在很多时候能让我们把代码组织的结构调整得更合理,同时也能给以后的维护带来方便。
3. 提升方法
概念:提升方法是指将一个很多继承类都要用到的方法提升到基类中。
正文:提升方法是指将一个很多继承类都要用到的方法提升到基类中,这样就能减少代码量,同时让类的结构更清晰。如下代码所示,Turn方法在子类Car 和Motorcycle 都会用到,因为Vehicle 都会有这个方法,所以我们就会想到把它提到基类中。
namespace LosTechies.DaysOfRefactoring.PullUpMethod.Before { public abstract class Vehicle { // other methods } public class Car : Vehicle { public void Turn(Direction direction) { // code here } } public class Motorcycle : Vehicle { } public enum Direction { Left, Right } }
重构后的代码如下,那么现在Car 和Motorcycle 都具有Turn这个方法,如果这个方法修改也只需要修改基类即可,所以给维护和以后的重构带来了方便。
namespace LosTechies.DaysOfRefactoring.PullUpMethod.After { public abstract class Vehicle { public void Turn(Direction direction) { // code here } } public class Car : Vehicle { } public class Motorcycle : Vehicle { } public enum Direction { Left, Right } }
总结:这个重构要根据具体情况使用,如果不是每个子类都有这个方法的话,可以考虑使用接口或者其他方式。
4. 降低方法
概念:本文中的降低方法和前篇的提升方法整好相反,也就是把个别子类使用到的方法从基类移到子类里面去。
正文:如下代码所示,Animal 类中的方法Bark只有在其子类Dog 中使用,所以最好的方案就是把这个方法移到子类Dog 中。
namespace LosTechies.DaysOfRefactoring.PushDownMethod.Before { public abstract class Animal { public void Bark() { // code to bark } } public class Dog : Animal { } public class Cat : Animal { } }
重构后的代码如下,同时如果在父类Animal 中如果没有其他的字段或者公用方法的话,可以考虑把Bark方法做成一个接口,从而去掉Animal 类。
namespace LosTechies.DaysOfRefactoring.PushDownMethod.After { public abstract class Animal { } public class Dog :