重構手法之簡化函數調用【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
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...