C#重构经典全面汇总
1、 封装集合
概念:本文所讲的封装集合就是把集合进行封装,只提供调用端需要的接口。
正文:在很多时候,我们都不希望把一些不必要的操作暴露给调用端,只需要给它所需要的操作或数据就行,那么做法就是封装。这个重构在微软的代码库也经常遇到。比如最经典的属性对字段的封装就是一个很好的例子,那么下面我们将看到对集合的封装,如下代码所示,调用端只需要一个集合的信息,而我们则提供了一个IList的集合,大家都知道IList具有对集合的所有操作,所以这会带来很多隐患,最好的做法就是对它进行重构。
using System.Collections.Generic; |
那么重构之后,我们把IList换成了IEnumerable,大家都知道只包括一个返回值为IEnumerator的GetEnumerator()方法,所以这样只能遍历取出它的值,而不能对这个集合做出改变,这正是我们所需要的结果,具体代码如下:
using System.Collections.Generic; |
总结:这个例子很容易让我们想到以前系统间耦合常喜欢用数据库。每个系统都会操作数据库,并且有些系统还会对数据库的表结构或字段进行修改,那么这很容易就会造成维护的地狱,很明智的一个做法就是使用SOA来隔开这些耦合,让一些只需要数据展示的系统得到自己需要的数据即可。
2、 移动方法
概念:本文所讲的移动方法就是方法放在合适的位置(通常指放在合适的类中)。
正文:移动方法是一个很简单也很常见的重构,只要是系统就会存在很多类,那么类里面包括很多方法,如果一个方法经常被另外一个类使用(比本身的类使用还多)或者这个方法本身就不应该放在这个类里面,那么这个适合应该考虑把它移到合适的类中。代码如下:
namespace LosTechies.DaysOfRefactoring.MoveMethod.Before
public int AccountAge { get; private set; } public double CalculateInterestRate() if (AccountAge > 10) return 0.05; public class AccountInterest public AccountInterest(BankAccount account) public double InterestRate public bool IntroductoryRate
|
移动以后大家可以看到BankAccount类的职责也单一,同时CalculateInterestRate也放到了经常使用且适合它的类中了,所以此重构是一个比较好的重构,能让整个代码变得更加合理。
namespace LosTechies.DaysOfRefactoring.MoveMethod.After namespace LosTechies.DaysOfRefactoring.MoveMethod.After
|
总结:这个重构法则在很多时候能让我们把代码组织的结构调整得更合理,同时也能给以后的维护带来方便。
3、 提升方法
概念:提升方法是指将一个很多继承类都要用到的方法提升到基类中。
正文:提升方法是指将一个很多继承类都要用到的方法提升到基类中,这样就能减少代码量,同时让类的结构更清晰。如下代码所示,Turn方法在子类Car和Motorcycle 都会用到,因为Vehicle 都会有这个方法,所以我们就会想到把它提到基类中。
namespace LosTechies.DaysOfRefactoring.PullUpMethod.Before
|
重构后的代码如下,那么现在Car 和Motorcycle 都具有Turn这个方法,如果这个方法修改也只需要修改基类即可,所以给维护和以后的重构带来了方便。
namespace LosTechies.DaysOfRefactoring.PullUpMethod.After
|
总结:这个重构要根据具体情况使用,如果不是每个子类都有这个方法的话,可以考虑使用接口或者其他方式。
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 : Animal { public void Bark() { // code to bark } }
public class Cat : Animal { } }
|
总结:面向对象三大特征(继承、封装、多态)很多时候可以帮助我们,但同时也可能会造成使用过度或者使用不当,所以如何把握好设计,这个就变得至关重要。在什么时候使用继承的方式,在什么时候使用组合和聚合,接口和继承类的选择等久成了我们的重点。
5、 提升字段
概念:本文中的提升字段和前面的提升方法颇为相似,就是把子类公用的字段提升到基类中,从而达到公用的目的。
正文:如下代码所示, Account 的两个子类CheckingAccount 和SavingsAccount 都有minimumCheckingBalance 字段,所以可以考虑把这个字段提到基类中。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LosTechies.DaysOfRefactoring.PullUpField.Before
{
publicabstract class Account
{
}
public classCheckingAccount : Account
{
privatedecimal _minimumCheckingBalance = 5m;
}
public classSavingsAccount : Account
{
private decimal_minimumSavingsBalance = 5m;
}
}
重构后的代码如下,这样提的前提是这些子类有一个基类或者有很多相似的字段和方法,不然为了一个字段而单独建立一个抽象类是不可取的,所以这个就需要具体权衡。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LosTechies.DaysOfRefactoring.PullUpField.After
{
publicabstract class Account
{
protecteddecimal _minimumBalance = 5m;
}
public classCheckingAccount : Account
{
}
public classSavingsAccount : Account
{
}
}
总结:这个重构的策略比较简单,同时也是比较常用的一些做法,最主要就是要注意权衡是否真的有这个必要,看这样做究竟有没有什么好处(比如只需要改一个地方,维护简便了,同时代码量也更少了等)。
6、 降低字段
概念:本文中的降低字段和前篇的提升字段正好相反,就是把基类中只有某些少数类用到的字段降低到使用它们的子类中。
正文:如下代码所示,基类Task 类中的_resolution字段只会在子类BugTask 中用到,所以就考虑把它放到BugTask 类中。
namespace LosTechies.DaysOfRefactoring.PushDownField.Before
{
publicabstract class Task
{
protectedstring _resolution;
}
public classBugTask : Task
{
}
public classFeatureTask : Task
{
}
}
重构后的代码如下所示,这样做的好处可以简化基类,同时让其他没有使用它的子类也变得更加简单,如果这样的字段比较多的话,使用此重构也能节约一部分内存。
namespace LosTechies.DaysOfRefactoring.PushDownField.After
{
publicabstract class Task
{
}
public classBugTask : Task
{
privatestring _resolution;
}
public classFeatureTask : Task
{
}
}
总结:此重构也是一个非常简单的重构,在很多时候我们都会不自觉的使用它。
7、 重命名(方法,类,参数)
概念:本文中的改名(方法,类,参数)是指在写代码的时候对类、方法、参数、委托、事件等等元素取一个有意义的名称。
正文:如下代码所示,加入一个公司建立一个员工的类,类中有一个员工名字的字段和一个按照小时计算员工收入的方法,那么下面代码的取名就显得很难理解了,所以我们会重构名称。
namespace LosTechies.DaysOfRefactoring.Rename.Before
{
public classPerson
{
publicstring FN { get; set; }
publicdecimal ClcHrlyPR()
{
//code to calculate hourly payrate
return 0m;
}
}
}
重构后代码如下所示,这样看起来就非常清晰,如果有新进项目组的成员,也会变得很乐意看这个代码。
namespace LosTechies.DaysOfRefactoring.Rename.After
{
// Changedthe class name to Employee
publicclass Employee
{
publicstring FirstName { get; set; }
publicdecimal CalculateHourlyPay()
{
//code to calculate hourly payrate
return 0m;
}
}
}
总结:此重构经常被广大程序员所忽视,但是带来的隐患是不可估量的,也许老板要修改功能,那我们来看这段没有重构的代码(就算是自己写的,但由于时间和项目多等关系,我们也很难理解了),然后就会变得焦头烂额。相反重构后的代码就会觉得一目了然、赏心悦目。
8、 使用委派代替继承
概念:本文中的“使用委派代替继承”是指在根本没有父子关系的类中使用继承是不合理的,可以用委派的方式来代替。
如下代码所示,Child 和Sanitation(公共设施)是没有逻辑上的父子关系,因为小孩不可能是一个公共设施吧!所以我们为了完成这个功能可以考虑使用委派的方式。
namespace LosTechies.DaysOfRefactoring.ReplaceInheritance.Before
{
public class Sanitation
{
public string WashHands()</