重構手法之簡化函數調用【3】

来源:http://www.cnblogs.com/liuyoung/archive/2017/12/02/7900566.html
-Advertisement-
Play Games

你有一個函數,其中完全取決於參數值而採取不同行為。針對該參數的每個可能值,建立一個獨立的函數。 ...


返回總目錄

本小節目錄

6Replace Parameter with Explicit Methods(以明確函數取代參數)

概要

你有一個函數,其中完全取決於參數值而採取不同行為。針對該參數的每個可能值,建立一個獨立的函數

動機

如果某個參數有多種可能的值,而函數內又以表達式檢查這些參數值,並根據不同參數值做出不同的行為,就該使用本項重構;

可以獲得好處:“編譯期代碼檢查”,“介面更清楚”(如果用參數值決定函數行為,那麼函數用戶不但需要觀察該函數,而且還要判斷參數是否“合法化”。——而合法的參數,很少在文檔中提到,必須通過上下文,才能判斷);

考慮“編譯期檢驗”的好處,為了獲取一個清晰的介面,我們也值得這麼做。

範例

下列代碼根據不同的參數值,建立Employee之下不同的子類。

class EmployeeType
{
    static Employee Create(int type)
    {
        switch (type)
        {
            case 0:
                return new Engineer();
            case 1:
                return new Salesman();
            case 2:
                return new Manager();
            default:
                throw new ArgumentException("Incorrect type code value!");
        }
    }
}

class Employee
{

}

class Engineer:Employee
{
    
}

class Salesman:Employee
{
   
}

class Manager:Employee
{
    
}

由於這是一個工廠函數,不能實施Replace Conditional with Polymorphism,因為使用該函數時對象根本還沒有創建出來。由於可以預見到Employee不會有太多新的子類,所以可以放心地為每個子類建立一個工廠函數,而不必擔心工廠函數的數量會劇增。首先,根據參數值建立相應的新函數:

static Employee CreateEngineer()
{
    return new Engineer();
}
static Employee CreateSalesman()
{
    return new Salesman();
}
static Employee CreateManager()
{
    return new Manager();
}

找到函數的調用端,把諸如下麵這樣的代碼:

Employee kent = EmployeeType.Create(0);

替換為:

Employee kent = EmployeeType.CreateEngineer();

替換完成之後,就可以可以刪掉Create()函數了。

小結

如果函數根據參數做出不同的響應,那麼就是需要重構的時候了。

7Preserve Whole Object(保持對象完整)

概要

你從某個對象中取出若幹值,將它們作為某一次函數調用中的參數,改為傳遞整個對象

動機

我們常常會將來自同一對象的若幹數據項作為參數,傳遞給某個函數。這樣做的問題在於:萬一被調函數需要新的數據項,就必須查找並修改對此函數的所有調用。但是如果傳遞的是整個對象,則不會有此問題。

該項手法的好處:(1)使參數列更加穩固;(2)提高代碼的可讀性。

範例

class Room
{
    public bool WithinPlan(HeatingPlan plan)
    {
        int low = DaysTempRange().GetLow();
        int high = DaysTempRange().GetHigh();
        return plan.WithinRange(low, high);
    }

    public TempRange DaysTempRange()
    {
        return new TempRange();
    }
}

class HeatingPlan
{
    private TempRange _range;
    public bool WithinRange(int low, int high)
    {
        return low >= _range.GetLow() && high <= _range.GetHigh();
    }
}

class TempRange
{
    public int GetLow()
    {
        return 6;
    }

    public int GetHigh()
    {
        return 28;
    }
}

這裡其實不必將TempRange對象的信息拆開來單獨傳遞,只需將整個對象傳遞給WithinPlan()函數即可:

class Room
{
    public bool WithinPlan(HeatingPlan plan)
    {
        return plan.WithinRange(DaysTempRange());
    }

    public TempRange DaysTempRange()
    {
        return new TempRange();
    }
}
class HeatingPlan
{
    private TempRange _range;
    public bool WithinRange(TempRange roomRange)
    {
        return roomRange.GetLow() >= _range.GetLow() && roomRange.GetHigh() <= _range.GetHigh();
    }
}

class TempRange
{
    public int GetLow()
    {
        return 6;
    }

    public int GetHigh()
    {
        return 28;
    }
}

小結

傳遞整個對象雖然好,但事情總是有兩面性。如果傳的是數值,被調用函數就只依賴於這些數值,而不依賴它們所屬的對象。但是如果傳遞的是整個對象,被調用函數所在的對象就需要依賴參數對象。如果這會使依賴結構惡化,就不該使用本項重構。

 

To Be Continued……


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

-Advertisement-
Play Games
更多相關文章
  • 在nuget中下載ServiceStack.Redis,但是運行之後會出現一個問題: Exception: “Com.JinYiWei.Cache.RedisHelper”的類型初始值設定項引發異常。System.TypeInitializationException: “Com.JinYiWei. ...
  • data.xml 通過 linq to xml ,查找價格超過10的產品,並列印供應商名稱與產品名稱; 輸出 SupplierName=CD-by-CD-by-Sondheim , ProductName=AssassinsSupplierName=Solely Sondheim , Product ...
  • 瞭解一個語言最好的方式就是在編輯器中按照語法規則輸入代碼,然後運行並查看結果是否符合預期。本博文介紹S#編輯器軟體界面及其相關各模塊的主要功能,並通過通過帶有局部變數的S#代碼來表達和生成幾何圖形,從而說明瞭S#代碼的常用編寫流程。 ...
  • 瞭解一個語言最好的方式就是在編輯器中按照語法規則輸入代碼,然後運行並查看結果是否符合預期。本博文內容非常重要,承上啟下,不但公開了S#語言的所有武功招式——語法規則,並提供了練功的基礎工具——編輯器,統統都是乾貨呀。 ...
  • 在C#的字元串,其中有許多空格,現要求是把多餘的空格去除保留一個。原理是使用Split()方法進行分割,分割有一個選項是RemoveEmptyEntries,然後再把分割後的字元串Join起來。 string string1 = "AAaaA Oopps 32 211 44.8 69 15.9 C# ...
  • 最近學習數據驅動UI,瞭解到INotifyPropertyChanged這個介面的用法,看了很多網上的文章,自己作了一個總結。 INotifyPropertyChanged這個介面其實非常簡單,只有一個PropertyChanged事件,如果類繼承了這個介面,就必須實現介面。用VS的提示,就是補充了 ...
  • RoutData.values[Key] 只能獲取路由定義中的數據,url參數的數據不包含在裡面 https://i.cnblogs.com/EditPosts.aspx?opt=1 路由:功能變數名稱到?之間 -->EditPosts.aspx URL參數:?之後 -->opt=1 修改路由規則後 ...
  • 鏈接的地址,可以根據路由規則動態生成,不用寫死,文檔結構有變化時,依然可以找到正確的路徑 1.url.Action(只生成URL) 有很多重載方法,可以根據需要選擇,生成URL時,會根據路由規則生成,不過調用時也是根據路由規則定址,所以OK 2.Html.ActionLink(生成整個Link標簽) ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...