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

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

異常能清楚地將“普通程式”和“錯誤處理”分開了,這使得程式更容易理解。 代碼的可理解性應該是我們虔誠追求的目標。 ...


返回總目錄

本小節目錄

13Replace Error Code with Exception(以異常取代錯誤碼)

概要

某個函數返回一個特定的代碼,用以表示某種特定的情況。改用異常。

動機

異常能清楚地將“普通程式”和“錯誤處理”分開了,這使得程式更容易理解。

代碼的可理解性應該是我們虔誠追求的目標。

範例

class Account
{
    /// <summary>
    /// 餘額
    /// </summary>
    private int _balance;
    /// <summary>
    /// 取款
    /// </summary>
    /// <param name="amount">取款金額</param>
    /// <returns></returns>
    public int Withdraw(int amount)
    {
        if (amount > _balance)
        {
            return -1;
        }
        _balance -= amount;
        return 0;
    }
    public bool CanWithdraw(int amount)
    {
        return amount <= _balance;
    }

    public void HandOverdran()
    {

    }

    public void DoTheUsualThing()
    {

    }
}

為了讓這段代碼使用異常,首先決定使用受控異常還是非受控異常。關鍵在於:調用者是否有責任在取款之前檢查存款餘額,還是應該由Withdraw()函數負責檢查。如果“檢查餘額”是調用者的責任,那麼“取款金額大於存款金額”就是一個編程錯誤,應該使用非受控異常。如果“檢查餘額”是Withdraw()函數的責任,就必須在函數中拋出這個異常。

範例:非受控異常

使用非受控異常就表示應該由調用者負責檢查。首先需要檢查調用端的代碼,它不應該使用Withdraw()函數的返回值,因為返回值只是用來指出程式員的錯誤。如果看到這樣的代碼:

Account account = new Account();
if (account.Withdraw(100) == -1)
{
    account.HandOverdran();
}
else
{
    account.DoTheUsualThing();
}

應該將它替換成這樣的代碼:

Account account = new Account();
if (!account.CanWithdraw(100))
{
    account.HandOverdran();
}
else
{
    account.Withdraw(100);
    account.DoTheUsualThing();
}

現在移除錯誤碼,併在程式出錯時拋出異常。由於這種行為是異常的、罕見的,所以使用衛語句檢查這種情況:

public void Withdraw(int amount)
{
    if (amount > _balance)
    {
        throw new ArgumentException("Amount too large.");
    }
    _balance -= amount;
}

範例:使用受控異常

受控異常的處理方式略有不同。首先可以新建一個合適的異常:

class  BalanceException:Exception
{
    
}

當然了,這裡不新建也是可以的。

然後調整調用端如下:

Account account = new Account();    
try
{
    account.Withdraw(100);
    account.DoTheUsualThing();
}
catch (BalanceException ex)
{
    account.HandOverdran();
}

接下來修改Withdraw()函數,讓它以異常表示錯誤狀況:

public void Withdraw(int amount)
{
    if (amount > _balance)
    {
        throw new BalanceException();
    }
    _balance -= amount;
}

小結

將錯誤碼替換成異常之後,使得代碼更容易理解。

15Replace Exception with Test(以測試取代異常)

 概要

面對一個調用者可以預先檢查的條件,你拋出了一個異常。

修改調用者,使它在調用函數之前先做檢查。

動機

異常可以協助我們避免很多複雜的錯誤處理邏輯。但是,異常也會被濫用。“異常”只應該被用於異常的、罕見的行為,也就是那些產生意料之外的錯誤的行為,而不應該成為條件檢查的替代者。

範例

下麵的例子中,以一個ResourcePool對象管理一些創建代價高昂而又可以重覆使用的資源。這個對象帶有兩個“池”:一個用以保存可用資源,一個用以保存已分配資源。當用戶請求一份資源時,ResourcePool對象從“可用資源池”中取出一份資源交出,並將這份資源轉移到“已分配資源池”。當用戶釋放一份資源時,ResourcePool對象就該將資源從“已分配資源池”放回“可用資源池”。如果“可用資源池”不能滿足用戶的請求,ResourcePool對象就創建一份新資源。

class ResourcePool
{
    private Stack<Resource> _available;

    private Stack<Resource> _allocated;

    public Resource GetResource()
    {
        Resource result;
        try
        {
            result = _available.Pop();
            _allocated.Push(result);
            return result;
        }
        catch (Exception ex)
        {
            result = new Resource();
            _allocated.Push(result);
            return result;
        }
    }
}

class Resource
{

}

在這裡,“可用資源用盡”並不是一件意料外的事件,因此不該使用異常表示這種情況。

為了去掉這裡的異常,首先添加一個適當的提前測試,併在其中處理“可用資源為空”的情況:

class ResourcePool
{
    private Stack<Resource> _available;

    private Stack<Resource> _allocated;

    public Resource GetResource()
    {
        Resource result;
        if (_available.Count == 0)
        {
            result = new Resource();
            _allocated.Push(result);
            return result;
        }
        result = _available.Pop();
        _allocated.Push(result);
        return result;
    }
}

class Resource
{

}

在這裡,可以對條件代碼加以整理,使用Consolidate Duplicate Conditional Fragments

class ResourcePool
{
    private Stack<Resource> _available;

    private Stack<Resource> _allocated;

    public Resource GetResource()
    {
        Resource result;
        if (_available.Count == 0)
        {
            result = new Resource();
        }
        else
        {
            result = _available.Pop();
        }     
        _allocated.Push(result);
        return result;
    }
}

class Resource
{

}

小結

 

 階段性小結

在對象技術中,最紅要的概念莫過於“介面”。容易被理解和被使用的介面,是開發良好面向對象軟體的關鍵。

最簡單也最重要的一件事就是修改函數名稱。名稱是程式寫作者與閱讀者交流的關鍵工具。只要能理解一段程式的功能,就應該大膽地使用Rename Method將所知道的東西傳達給他人。

函數參數在介面中扮演十分重要的角色。Add ParameterRemove Parameter都是很常見的重構手法。剛接觸面向對象技術的程式員往往使用很長的參數列。但是,使用對象技術,可以保持參數列的簡短。如果來自同一個對象的多個值被當做參數傳遞,可以運用Preserve Whole Object將它們替換為單一對象,從而縮減參數列。如果此前並不存在這樣一個對象,可以運用Introduce Parameter Object將它創建出來。如果函數參數來自該函數可獲取的一個對象,則可以使用Replace Parameter with Methods避免傳遞參數。如果某些參數被用來在條件表達式中做選擇依據,可以實施Replace Parameter with Explicit Method。另外,還可以使用Parameterize Method為數個相似函數添加參數,將它們合併到一起。

明確地將“修改對象狀態”的函數和“查詢對象狀態”的函數分開設計是一個很好的習慣。如果看到這兩種函數混在一起,可以使用Separate Query from Modifier將它們分開。

良好的介面只向用戶展現必須展現的東西。如果一個介面暴露了過多細節,可以將不必要暴露的東西隱藏起來,從而改進介面的質量。進行重構時,往往需要暫時暴露某些東西,最後再以Hide MethodRemove Setting Method將它們隱藏起來。

構造函數往往比較“麻煩”,因為它強迫你必須知道要創建的對象屬於哪個類,而你往往並不需要知道這一點。可以使用Replace Constructor with Factory Method避免了這不必要的信息。

和許多現代編程語言一樣,C#也有異常處理機制,這使得錯誤處理相對容易一些。不習慣使用異常的程式員,往往會以錯誤碼表示程式遇到麻煩。可以使用Replace Error Code with Exception來運用新的異常特性。但有時候異常也並不是最合適的選擇,應該實施Replace Exception with Test先測試一番。

 

 

To Be Continued……


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

-Advertisement-
Play Games
更多相關文章
  • 我們可以對坐標軸進行設置,設置坐標軸的範圍,設置坐標軸上的文字描述等。 基本用法 例如: 輸出圖為: 修改坐標軸的刻度 想要修改x軸的刻度,從 1到2,總共5點: 只要把上述代碼放在畫圖之前,最後畫出來的圖為: 這樣x軸上的刻度就被修改成了從 1到2共5個點。 把刻度用文字來表示 上面代碼的意思就是 ...
  • 上一章介紹了Python的相關知識,本章就開始著手操作,創建我們的第一個Python程式,首先需要配置好Python的運行環境 1.python環境準備 1.1.windows下安裝 Windows10配置環境變數,防止程式調用出錯 如:我的路徑為D:\Program Files (x86)\pyt ...
  • 說明 本文寫於2017 11 16,使用Go 1.9.2,操作系統為CentOS 7。 官方文檔參看 "https://golang.org/doc/install?download=go1.9.2.linux amd64.tar.gz" 。 安裝 上述步驟在 中增加一行: 環境變數 Go預設使用 ...
  • 1. Myeclipse自帶有Tomcat服務和預覽網頁的功能,非常的方便快捷,但是有時候在運行項目會出現一些非代碼錯誤的問題,這種問題不是由於代碼錯誤引起的,使用外部的瀏覽器瀏覽項目的網頁就正常顯示,一般建議使用外部的Tomcat伺服器和瀏覽器。 2.首先下載Tomcat,百度一搜即可下載,下解壓 ...
  • 沒學指針之前如何操作? 用一個臨時變數進行交換 學習指針的方法之後,如何操作? 把指針作為函數參數的方法處理從大到小排序問題。 ...
  • 就業形勢嚴峻的情況下,每個企業對於人才的需求都不一樣,並不是說公司不願意招聘培訓班出來的人,而是看你的能力是不是能勝任企業招聘人才的需求,是不是能給企業帶來價值的人。 現在市面上的培訓機構多如牛毛,然後很多看到了互聯網企業的高薪都紛紛跑去學習軟體開發,不清楚自己到底是不是真的喜歡搞技術,還是為了一份 ...
  • 一、序言 大家或多或少都聽過WebService(Web服務),有一段時間很多電腦期刊、書籍和網站都大肆的提及和宣傳WebService技術,其中不乏很多吹噓和做廣告的成分。但是不得不承認的是WebService真的是一門新興和有前途的技術,那麼WebService到底是什麼?何時應該用? 當前的 ...
  • 下麵有一個字元串陣列: 要求是獲取元素最長或最短的長度。你可以在程式中創建一個對象,這個對象有兩個屬性元素值和元素長度: class Class6 { private string _ElementValue; public string ElementValue { get { return _E ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...