小酌重構系列[1]——移動方法
概要
有些開發者在編寫方法時,可能較少地去思考一個問題:方法放在這個class中是否合適?
他們可能會覺得:這個方法已經實現xxx功能了,放在哪個class都一樣的,class不就是一個裝方法的容器嘛。
我贊同class是一個裝東西的容器,且不僅限于方法。
但是,容器是有區別的。本文要講的“移動方法”,是一種讓方法放進合適的class的重構策略。
選擇合適的容器
生活中我們會用到杯子和箱子,杯子和箱子都是容器。
倘若你用杯子裝書,用箱子裝水,會產生不好的結果——杯子里放不下書,水裝進箱子后,會打濕箱子。
// 杯子
public class Cup
{
// 裝書
public void HoldBook()
{
}
}
// 箱子
public class Box
{
// 裝水
public void HoldWater()
{
}
}
按照生活常識,我們應該用杯子裝水,用箱子裝書。
// 杯子
public class Cup
{
// 裝水
public void HoldWater()
{
}
}
// 箱子
public class Box
{
// 裝書
public void HoldBook()
{
}
}
每個程序員在開發完功能后,都應該回頭讀一讀自己的代碼,確認是否存在一些“牛頭不對馬嘴”的方法。
class是方法的容器,我們應該為每個方法尋找最合適的容器。
移動方法
現在引入本文的主題:“移動方法”。
該定義中有兩個關鍵詞:1. 適用 2. 語義。
“適用性”是指方法實現的功能應該適用它的class,“語義”是指方法的所描述的功能和class的語義是一致的。
“語義”的重要性高于“適用性”,例如:在程序中定義的擴展方法或者幫助類,通常都是被其它class調用的。這種情況下,我們應該著重體現“語義”。
有些開發者在編寫Excel工具類時,在ExcelUtil中出定義了ExportCsv()這樣的方法,盡管導出的Csv格式是可以用Excel打開的。
較好的做法是,新建一個CsvUtil類,將ExportCsv()方法定義在其中,下圖闡述了這個重構過程(藍色表示重構前,紅色表示重構后)。
雖然多了一個class,但這使得Excel工具類和Csv工具類的語義更佳精確,代碼的可讀性也提高了。
示例
這段代碼定義了兩個類: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; }
}
}
}
這兩個class用于描述一件客觀事實——”銀行賬戶和計算賬戶利率的方式“。
咋一看,這兩個類沒有什么問題。
但由于已經定義了AccountInterest類,這個class的語義是和利率相關的,而CalculateInterestRate()方法用于計算賬戶利率。
所以將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;
}
}
}
下圖描述了重構前后的區別(藍色表示重構前,紅色表示重構后)。
總結
移動方法是較簡單的一種重構策略,它旨在將方法移動到合適的類。
這個策略的關鍵在于找到方法體現的行為(功能),以及該行為對應的語義。
找到語義,我們就能將它放到合適的類。
其中的難點也在于語義,語義應該具備“準確性”。
有時候業務知識、對現實物體(對象)的理解可能會阻礙我們尋找到準確的語義。
本文鏈接: 文章作者:keepfool 文章出處:http://www.rzrgm.cn/keepfool/ 如果您覺得閱讀本文對您有幫助,請點一右下角的“推薦”按鈕,您的“推薦”將是我最大的寫作動力!歡迎看官們轉載,轉載之后請給出作者和原文連接。

有些開發者在編寫方法時,可能較少地去思考一個問題:方法放在這個class中是否合適?
他們可能會覺得:這個方法已經實現xxx功能了,放在哪個class都一樣的,class不就是一個裝方法的容器嘛。
我贊同class是一個裝東西的容器,且不僅限于方法。
但是,容器是有區別的。本文要講的“移動方法”可以讓方法放進合適的class的一種重構策略。





浙公網安備 33010602011771號