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

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

返回總目錄 本小節目錄 Separate Query from Modifier(將查詢函數和修改函數分離) Parameterize Method(令函數攜帶參數) 4Separate Query from Modifier(將查詢函數和修改函數分離) 概要 某個函數既返回對象狀態值,又修改對象狀 ...


返回總目錄

本小節目錄

4Separate Query from Modifier(將查詢函數和修改函數分離)

概要

某個函數既返回對象狀態值,又修改對象狀態(副作用)。

建立兩個不同的函數,其中一個負責查詢,另一個負責修改。

動機

任何有返回值的函數,都不應該有看得到的副作用。如果有一個函數“既有返回值又有副作用”,那麼就應該嘗試著將查詢動作從修改動作中分割出來。

範例

有這樣一個函數:一旦有人入侵安全系統,它會返回入侵者的姓名,併發送一個警報:

class Security
{

    void CheckSecurity(string[] people)
    {
        string found = FoundMiscrent(people);
    }
    string FoundMiscrent(string[] people)
    {
        foreach (var person in people)
        {
            if (person == "Don")
            {
                SendAlert();
                return "Don";
            }
            if (person == "John")
            {
                SendAlert();
                return "John";
            }
        }
        return string.Empty;
    }

    void SendAlert()
    {

    }
}

為了將查詢動作和修改動作分開,首先建立一個適當的查詢函數,使其與修改函數返回相同的值,但不造成任何副作用。

string FoundPerson(string[] people)
{
    foreach (var person in people)
    {
        if (person == "Don")
        {
            return "Don";
        }
        if (person == "John")
        {
            return "John";
        }
    }
    return string.Empty;
}

然後,逐一替換原函數內所有的return語句,該調用新建的查詢函數:

string FoundMiscrent(string[] people)
{
    foreach (var person in people)
    {
        if (person == "Don")
        {
            SendAlert();
            return FoundPerson(people);
        }
        if (person == "John")
        {
            SendAlert();
            return FoundPerson(people);
        }
    }
    return FoundPerson(people);
}

現在修改調用者,將原本的單一調用者替換為兩個調用:先調用修改函數,然後調用查詢函數:

void CheckSecurity(string[] people)
{
    FoundMiscrent(people);
    string found = FoundPerson(people);
}

替換完畢後,將修改函數的返回值改為void,並修改其函數名稱:

void SendAlert(string[] people)
{
    foreach (var person in people
    {
        if (person == "Don")
        {
            SendAlert();
            return;
        }
        if (person == "John")
        {
            SendAlert();
            return;
        }
    }
}

當然,這種情況下,得到了大量重覆代碼,因為修改函數之中使用了與查詢函數相同的代碼。現在對修改函數實施Substitute Algorithm,設法讓它再簡潔一些:

void SendAlert(string[] people)
{
    if (FoundPerson(people) != "")
    {
        SendAlert();
    }
}

小結

如果某個函數只是向你提供一個值,沒有任何看得到的副作用,那麼你可以任意調用這個函數,也可以把調用動作搬移到函數的其他地方。簡而言之,需要操心的事情少多了。

5Parameterize Method(令函數攜帶參數)

概要

若幹函數做了類似的工作,但在函數本體中包含了不同的值。

建立單一函數,以參數表達那些不同的值

動機

有這樣兩個函數:它們做著類似的工作,但因少數幾個值致使行為略有不同。這時,可以將這些各自分離的函數統一起來,並通過參數來處理那些變化的情況,用以簡化問題。

範例

先看一個簡單的例子:

class Employee
{
    public double Salary { get; set; }

    void TenPercentRaise()
    {
        Salary *= 1.1;
    }
    void FivePercentRaise()
    {
        Salary *= 1.05;
    }
}

這段代碼可以替換如下:

class Employee
{
    public double Salary { get; set; }
void Raise(double factor) { Salary *= 1 + factor; } }

再來看一個稍微複雜的例子:

class Dollars
{
    public Dollars(double result)
    {

    }
}
class Charge
{
    private Dollars BaseCharge()
    {
        double result = Math.Min(LastUsage(), 100) * 0.03;
        if (LastUsage() > 100)
        {
            result += (Math.Min(LastUsage(), 200) - 100) * 0.05;
        }
        if (LastUsage() > 200)
        {
            result += (LastUsage() - 200) * 0.07;
        }
        return new Dollars(result);
    }

    private int LastUsage()
    {
        return 201;
    }
}

上述代碼可以替換如下:

class Charge
{
    private Dollars BaseCharge()
    {
        double result = UsageInRange(0, 100) * 0.03;
        result += UsageInRange(100, 200) * 0.05;
        result += UsageInRange(200, Int32.MaxValue) * 0.07;
        return new Dollars(result);
    }

    private int UsageInRange(int start, int end)
    {
        if (LastUsage() > start)
        {
            return Math.Min(LastUsage(), end) - start;
        }
        return 0;
    }
    private int LastUsage()
    {
        return 201;
    }
}

本項重構的要點在於:以“可將少量數值視為參數”為依據,找出帶有重覆性的代碼。

小結

本項重構可以去除重覆代碼,並提高靈活性,因為可以用這個參數處理更多的變化情況。

 

To Be Continued……


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

-Advertisement-
Play Games
更多相關文章
  • 1、創建和查看索引 所謂普通索引,就是在創建索引時,不附加任何限制條件(唯一、非空等限制)。該類型的索引可以創建在任何數據類型的欄位上。 (1)創建表時,創建普通索引 語法: 例子: (2)在已經存在的表上創建普通索引 語法: 例子: 2、創建和查看唯一索引 (1)創建表時創建唯一索引 語法: 例子 ...
  • 環境:aix 7.1 ,oracle 12.1.0.2 rac -3節點。 硬體故障後,硬體工程師更換了內聯網卡,不知為何資源VIP也有問題,只好先添加了VIP srvctl add vip -node rac-wy1 -address rac-wy1-vip.nsn.com/255.255.255 ...
  • oracle提供了三個隔離級別: 1.讀提交 ,簡而言之只能讀取語句開始執行前提交的數據 2.串列,這個好理解,就是事務串列運行,避免經典的三個場景-臟讀、不可重覆讀、幻讀。 3.只讀,oracle已經實現的只讀模式。 -- 這些都很容易理解,問題的關鍵是解決一些實際的問題,例如典型的汽車票銷售。 ...
  • 一、建表 1、最簡單的建表CREATE TABLE user(id int,name char(20),age int); 2、帶主鍵帶註釋和預設值創建表CREATE TABLE user(id INT PRIMARY KEY AUTO_INCREMENT COMMENT '設置主鍵自增',name ...
  • 1、表的基本概念 每一行代表一條唯一的記錄,每一列代表記錄中的一個欄位。 2、創建表 例子: 3、查看表結構 (1)DESCRIBE語句查看表定義 語法: 例子: (2)SHOW CREATE TABLE語句查看詳細表詳細定義 語法: 例子: 註意:在顯示表詳細定義信息時,可以使用“;”、“\g”和 ...
  • 1 //首先要添加 System.ServiceProcess.dll 引用 2 ServiceController sc = new ServiceController("MSSQLSERVER"); 3 4 //判斷服務是否已經關閉 5 if (sc.Status == ServiceContr ...
  • ThoughtWorks在每年都會出品兩期技術雷達,這是一份關於技術趨勢的報告,它比起一些我們能在市面上見到的其他各種技術行情和預測報告,更加具體,更具可操作性,因為它不僅涉及到新技術大趨勢,比如雲平臺和大數據,更有細緻到類庫和工具的推介和評論,從而更容易落地。 Thoughtworks技術雷達 T ...
  • Nginx集群是.NET WebApi提供了負載均衡的其中一種實現方式,同時還增加了SSL認證,能夠確保WebApi能夠以加密形式進行響應。Nginx使用其中的SSL模塊,能夠支持HTTPS的配置,當然也能夠讓HTTP與HTTPS並存(只需要增加listen 80監聽埠則可),本文主要實現HTTP... ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...