小酌重構系列[1]——移動方法


概要

有些開發者在編寫方法時,可能較少地去思考一個問題:方法放在這個class中是否合適?

他們可能會覺得:這個方法已經實現xxx功能了,放在哪個class都一樣的,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打開的。
較好的做法是,新建一個CsvUtil類,將ExportCsv()方法定義在其中,下圖闡述了這個重構過程(藍色表示重構前,紅色表示重構后)。
雖然多了一個class,但這使得Excel工具類和Csv工具類的語義更佳精確,代碼的可讀性也提高了。

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; }
        }
    }
}

這兩個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;
        }
    }
}

下圖描述了重構前后的區別(藍色表示重構前,紅色表示重構后)。

image

總結

移動方法是較簡單的一種重構策略,它旨在將方法移動到合適的類。
這個策略的關鍵在於找到方法體現的行為(功能),以及該行為對應的語義。
找到語義,我們就能將它放到合適的類。
其中的難點也在於語義,語義應該具備“准確性”。
有時候業務知識、對現實物體(對象)的理解可能會阻礙我們尋找到准確的語義。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM