重構手法之簡化函數調用【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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...