重構手法之在對象之間搬移特性【1】

来源:http://www.cnblogs.com/liuyoung/archive/2017/11/22/7858062.html
-Advertisement-
Play Games

返回總目錄 本小節目錄 Move Method(搬移函數) Move Field(搬移欄位) 1Move Method(搬移函數) 概要 你的程式中,有個函數與其所駐類之外的另一個類進行更多交流:調用後者,或被或者調用。 在該函數最常引用的類中建立一個有著類似行為的新函數。將舊函數變成一個單純的委托 ...


返回總目錄

本小節目錄

1Move Method(搬移函數)

概要

你的程式中,有個函數與其所駐類之外的另一個類進行更多交流:調用後者,或被或者調用。

在該函數最常引用的類中建立一個有著類似行為的新函數。將舊函數變成一個單純的委托函數,或是將舊函數完全移除。

動機

如果一個類有太多行為,或如果一個類與另一個類有太多合作而形成高度耦合,又或者使用另一個對象的次數比使用自己所駐對象的次數還多。那就要搬移函數。

搬移函數時,要根據“這個函數與哪個對象的交流比較多”來決定其移動路徑。

範例

用一個表示“賬戶”的Account類來說明這項重構:

class Account
{
    private AccountType _accountType;
    private int _daysOverdrawn;
    /// <summary>
    /// 透支金額計費規則
    /// </summary>
    /// <returns></returns>
    double OverdraftCharge()
    {
        if (_accountType.IsPremium())
        {
            double result = 10;
            if (_daysOverdrawn > 7)
            {
                result += (_daysOverdrawn - 7) * 0.85;
            }
            return result;
        }
        return _daysOverdrawn * 1.75;
    }

    double BankCharge()
    {
        double result = 4.5;
        if (_daysOverdrawn > 0)
        {
            result += OverdraftCharge();
        }
        return result;
    }
}

AccountType類如下:

class AccountType
{
       public bool IsPremium()
       {
            return true;
       }
 }

假設有幾種新賬戶,每一種都有自己的“透支金額計費規則”。所有我們將OverdraftCharge()搬移到AccountType類去。

首先要做的就是:觀察OverdraftCharge()使用的每一項特性,考慮是否值得將它們與OverdraftCharge()一起移動。此例中,我們需要讓_daysOverdrawn 欄位留在Account類中,因為這個值不會隨著不同種類的賬戶而變化。然後我們將OverdraftCharge()函數代碼複製到AccountType中,並做相應調整。

class AccountType
{
    public double OverdraftCharge(int daysOverdrawn)
    {
        if (IsPremium())
        {
            double result = 10;
            if (daysOverdrawn > 7)
            {
                result += (daysOverdrawn - 7) * 0.85;
            }
            return result;
        }
        return daysOverdrawn * 1.75;
    }


    public bool IsPremium()
    {
        return true;
    }
}

然後將源函數的函數本體替換為一個簡單的委托動作。

class Account
{/// <summary>
    /// 透支金額計費規則
    /// </summary>
    /// <returns></returns>
    double OverdraftCharge()
    {
        return _accountType.OverdraftCharge(_daysOverdrawn);
    }
}

重構到這裡就可以結束了。當然了,我們也可以刪除Account中的源函數。我們找到源函數的所有調用者,並將這些調用重新定向,改為調用Account的BankCharge()。

class Account
{
    private AccountType _accountType;
    private int _daysOverdrawn;
  
    double BankCharge()
    {
        double result = 4.5;
        if (_daysOverdrawn > 0)
        {
            result += _accountType.OverdraftCharge(_daysOverdrawn);
        }
        return result;
    }
}

此例中被搬移函數只引用了一個欄位,所以只需將這個欄位作為參數傳給目標函數就行了。如果被搬移函數調用了Account中的另一個函數,可以將源對象傳遞給目標函數。

class AccountType
{
    public double OverdraftCharge(Account account)
    {
        if (IsPremium())
        {
            double result = 10;
            if (daysOverdrawn > 7)
            {
                result += (account.GetDaysOverdrawn() - 7) * 0.85;
            }
            return result;
        }
        return account.GetDaysOverdrawn()* 1.75;
    }


    public bool IsPremium()
    {
        return true;
    }
}

 小結

在搬移函數時,檢查源類中被源函數所使用的一切特性,考慮它們是否也該被搬移。如果某個特性只被你打算搬移的那個函數用到,那就應該將它一併搬移。如果另有其他函數使用了這個特性,就可以考慮將使用該特性的所有函數全都一併搬移。

2Move Field(搬移欄位)

概要

在你的程式中,某個欄位被其所駐類之外的另一個類更多地用到。

在目標類建立一個欄位,修改源欄位的所有用戶,令它們改用新欄位。

動機

在類之間移動狀態和行為,是重構中必不可少的措施。隨著系統的發展,我們會發現自己需要新的類,並需要將現有的工作責任拖到新的類中。

對於一個欄位,在其所駐類之外的另一個類中有更多函數使用了它,就要考慮搬移這個欄位。

範例

還是以Account類為例。

class Account
{
    private AccountType _accountType;

    private double _interestRate;

    double GetInterestForAmountByDays(double amount, int days)
    {
        return _interestRate * amount * days / 365;
    }
}

我們想要把_interestRate搬移到AccountType類中去。目前已經有數個函數引用了它,GetInterestForAmountByDays()就是其中之一。

我們在AccountType中建立一個_interestRate欄位,並封裝成屬性。

class AccountType
{

    private double _interestRate;

    public double InterestRate
    {
        get => _interestRate;
        set => _interestRate = value;
    }
}

現在讓Account類中訪問的_interestRate欄位的函數轉而使用AccountType對象,並且刪除Account類中的_interestRate欄位。

class Account
{
    private AccountType _accountType;

    double GetInterestForAmountByDays(double amount, int days)
    {
        return _accountType.InterestRate * amount * days / 365;
    }
}

小結

對於C#來說,可能這個重構手法叫“搬移屬性”更合適一點。因為基本上欄位都是私有的,屬性才是供其他函數訪問的。搬移屬性做法和範例中是一樣的。

 

To Be Continued……


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • zabbix 監控的搭建、使用--詳解 zabbix 是一個基於WEB界面的提供分散式系統監視以及網路監視功能的企業級的開源解決方案 ...
  • 最終環境: Ubuntu17.10、Apache2.4.27、MySQL5.7.20、PHP7.1 1. 安裝 apache 官方源有,直接安裝: 2. 安裝 mysql 官方源有,直接安裝: 安裝期間會提示設置 MySQL administrator 的密碼 PS:需要什麼軟體或包,直接用 apt ...
  • 方法一:支持rsync的網站 對於常用的centos、Ubuntu、等使用官方yum源在 http://mirrors.ustc.edu.cn 都存在鏡像。 同時 http://mirrors.ustc.edu.cn 網站又支持 rsync 協議, 可以通過rsync實現 鏡像yum源。 在wind ...
  • Linux下軟體的安裝一般由3個步驟組成: 若取消編譯: 若卸載軟體: 本節主要討論configure配置腳本。 如下圖所示,有些軟體就有configure配置腳本: 就可以使用命令./configure –help 輸出詳細的選項列表 常用選項如下: --host 編譯運行後的程式,預設為buil ...
  • 三劍客之awk 第1章 awk簡介 1.1 awk簡介 一種名字怪異的語言。 模式掃描和處理。處理文本流,水流。 awk不僅僅是linux系統中的一個命令,而且是一種編程語言,可以用來處理數據和生成報告。 處理的數據可以是一個或多個文件,可以是來自標準輸入,也可以通過管道獲取標準輸入,awk可以 在 ...
  • [20171120]關於find 軟連接問題.txt--//上個星期為了測試oracle參數filesystemio_options,將資料庫做了一次移動.但是我使用find對軟鏈接目錄查詢時--//遇到一些問題做一個記錄1.建立軟鏈接:$ cp -a /mnt/ramdisk/book /home ...
  • 1. CentOS6及以前 在CentOS6及以前的版本中,free命令輸出是這樣的: 第一行: 系統記憶體主要分為四部分:used(程式已使用記憶體),free(空閑記憶體),buffers(buffer cache),cached(Page cache)。 系統總記憶體total = used + fr ...
  • 紙殼CMS發佈了2.3版本,主要是添加了商城功能,強化產品功能。讓您的網站輕鬆實現電子商務。 有關2.3版本的更多信息,請查看以下鏈接: https://github.com/SeriaWei/ZKEACMS.Core/releases/tag/v2.3 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...