你的位置:首页 > ASP.net教程

[ASP.net教程]小酌重构系列[1]移动方法


前言

有些开发者在编写方法时,可能较少地去思考一个问题:方法放在这个class中是否合适?

他们可能会觉得:这个方法已经实现xxx功能了,放在哪个class都一样的,class不就是一个装方法的容器嘛。

我赞同class是一个装东西的容易,且不仅限于方法。但是,容器是有区别的。

生活中我们会用到杯子和箱子,杯子和箱子都是容器。
倘若你用杯子装书,用箱子装水,会产生不好的结果——杯子里放不了书,水装进箱子后,会打湿箱子。

image

隐藏代码 |
// 杯子public class Cup{  // 装书  public void HoldBook()  {  }}// 箱子public class Box{  // 装水  public void HoldWater()  {  }}


按照生活常识,我们应该用杯子装水,用箱子装书。

image

隐藏代码 |
// 杯子public class Cup{  // 装水  public void HoldWater()  {  }}// 箱子public class Box{  // 装书  public void HoldBook()  {  }}

每个开发者在完成功能后,回过头来最读一读自己的代码,是否存在一些“牛头不对马嘴”的方法?至少应该确认方法和class的功能是匹配的。

移动方法

这个前言有些长,现在引入本文的主题:”移动方法“。

当某个类的方法实现的功能更多地适用于另外一个类,且符合它的语义时,应将该方法移动到另外一个类。

该定义中有两个关键点:1. 适用性 2. 语义。”适用性“是指方法实现的功能应该适用它所处的class,”语义“是指方法的所描述的功能和class的语义是一致的。
”语义“的重要性优于”适用性“,一些扩展方法或者帮助类(工具类),它们通常只会被其它class调用。这种情况下,符合“语义”更优先。

例如:有些开发者在编写Excel工具类时,在ExcelUtil中出现了ExportCsv()这样的方法,尽管导出的Csv格式是可以用Excel打开的。

image

 

示例

这段代码定义了两个类:BankAccount、AccountInterest,分别表示银行账户和账户利率。请注意,计算利率的方法CalculateInterestRate()在BankAccount类中。

隐藏代码 |
namespace MoveMethod.Before{  /// <summary>  /// 银行账户  /// </summary>  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;    }  }  /// <summary>  /// 账户利率  /// </summary>  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; }    }  }}

 

咋一看,这两个类没有什么问题,它们已经能够描述一件客观事实——”银行账户信息和账户计算利率的方式“。由于已经定义了AccountInterest类,这个类本身是和利率相关的,所以将CalculateInterestRate()方法放在BankAccount类中不太合适,应将其移动到AccountInterest类中。

隐藏代码 |
namespace MoveMethod.After{  /// <summary>  /// 银行账户  /// </summary>  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; }  }  /// <summary>  /// 账户利率  /// </summary>  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; }    }    /// <summary>    /// 计算利率    /// </summary>    public double CalculateInterestRate()    {      if (Account.CreditScore > 800)        return 0.02;      if (Account.AccountAge > 10)        return 0.03;      return 0.05;    }  }}

下图描述了重构前后的区别(蓝色表示重构前,红色表示重构后)

image