從實戰專案總結的Ruby小技巧(第三部分)

geekerzp發表於2013-11-24

這是ruby小技巧系列的第三部分,這些技巧是我們從過去兩年的實戰經驗中所收穫的。第一部分涵蓋了程式碼塊(blocks)和範圍物件(ranges)第二部分討論了拆分重構以(destructuring)及型別轉換(type conversions)

異常(Exceptions)

處理異常是富有技巧性的,你非常容易掉入自己挖的坑內並且難以走出。下面有一下規則可以借鑑,它會讓你的程式碼更加容易除錯,雖然你得付出一些小小的努力,但是你會得到很大的回報。

首先,不要使用rescue修飾符。這裡有一個例子,當你在獲取陣列中的一個元素時,如果發生發生錯誤,將返回nil。

但是我們不知道它隱藏了怎樣的錯誤。引數array有可能為nil,引數index可能為nil,引數index有可能為string,你有可能將引數array的名字寫錯,或者任何你可能想象到的任何錯誤。

這意味著我們不能從閱讀程式碼得到程式碼的意圖,同時意料之外的錯誤不可能展現出來。

但是,如果在執行時輸入nil將會發生意料之外的錯誤。這種情況下,我們很難發現導致錯誤的原因是錯誤的傳入了nil。

使用標準形式的rescue可以減少你很多痛苦,通過捕獲我們期望的異常型別,或者是輸出異常資訊。

rescue => erescue StandardError => e的簡寫形式,就像單獨的rescuerescue StandardError的簡寫形式。這意味著只能捕獲StandardError或StandardError的子類(subclasses)。通常,你應該按照Ruby的慣例,只捕獲StandardError以及它的子類(subclasses),永遠不要去捕獲Exception。捕獲Exception意味著你將捕獲到你不想要去捕獲的異常,例如SystemExit,當你的程式請求退出的時候它將被丟擲(raise)。如果你捕獲這個異常,那麼當你結束程式的時候將不能正常退出。

同樣的,當你丟擲異常的時候應該丟擲StandardError的子類(subclasses),否則你的異常將不能被異常處理機制正確的處理。

在你的專案中定義一種通用的錯誤類是一種很好的做法,這種做法在gem中也得到了很好的應用,其他的錯誤類從通用的錯誤類繼承而來。

通過這種方式,你可以使用rescue MyProject::Error捕獲所有的異常,或者你可以指定特定的異常。

一種更加友好,更加易於閱讀異常列表的方式是利用class.new方法來定義異常。Class.new通過繼承作為引數的類來定義一個新類。生成的新類賦給一個常量時,常量將作為類的名稱。

使用這種方式你要面對的一個問題是,並不是在你程式碼中所產生的所有異常都是通過程式碼直接丟擲的。例如,當你進行HTTP請求的時候,可能在連線的時候丟擲異常。

現在,你可以在程式碼中捕獲這些異常,同時丟擲你自己定義型別的異常。但是,你會丟失有價值的除錯資訊:產生異常異常的原始類,異常資訊和呼叫堆疊。

Ruby允許你實現一個類(class)或模組(module)應用於rescue關鍵字作為期待的異常型別。rescue關鍵字使用case相等性操作符#===去比價傳遞的引數和期待的異常型別。對於類(class),當引數是異常型別的例項物件或者子類的例項物件則返回true 。對於模組(module),當引數混入(include)了模組或者引數擴充套件(extend)了模組則返回true。

因此,如果你將基本異常定義為一個模組(module),同時將它混入(include)到特定的異常類中,這些異常類將具有像上面所講到的異常類的行為。你也可以將程式碼中引發異常的任意異常類通過擴充套件基本異常(extend)打上“標籤”,這樣這些異常就可以通過rescue關鍵字和基本異常進行捕獲,同時跟蹤它的原始異常類,異常資訊以及呼叫堆疊。(你將不能引發基本異常,但這通常是一種很好的做法,你應該丟擲特定的異常類)

這種方式的另一種用法是作為兩種不同服務的介面卡類(例如資料庫客戶端),對映不同種類的異常到相同的分類。因此,當在介面卡下切換不同的資料庫客戶端後,rescue仍然工作。

然而這種技術有一個小的缺點。#extend方法呼叫將會使Ruby的全域性方法快取失效,在每次呼叫的時候會降低程式的速度。我們將會在Ruby2.1中得到一個更加優秀的方法快取失效,讓我們不用去擔心任何事情。但即使是現在這仍是一項有效和強大的技術。

 

模組(Module)

模組在Ruby中有多種用途。或許最常見的就是作為名稱空間(name-spacing),另外一種主要的用途是作為混入(mixin)。

像Enumerable和Comparabel一樣的模組是非常神奇的,可以很容易的讓你的類增加複雜的行為,通過在類中定義一些簡單的方法同時混入(mixin)這些模組。另外,模組也擁有一些其他的用途。

有時候,你有一系列的方法非常接近函式,它們接受輸入,返回結果,它們不會對當前物件self做任何操作。模組可以作為一種很好的方式可以將這些方法集中在一起。

這裡有一個在我們專案中的例子(這裡還有一些其他更多的方法,由於特殊原因不太適合和大家分享)。

在模組開始的module_function宣告是一個方法可見性修飾符,像publicprivateprotected。它使方法直接呼叫時成為模組的類方法(class method),當模組被混入時成為一個私有例項方法(private instance method)。

module_function改為extend self可以得到相同的效果。

這裡,模組的例項方法也同時作為類方法。不同的是你可以使用其他方法可見性修飾符,使方法在模組中為私有(private)的,在例項方法中為公有(public)的(當使用module_function修飾符時,所有的方法作為模組為公有的,作為例項方法為私有的)。

模組對於處理單例物件來說也是非常方便的,你不用去浪費時間去阻止多餘一個物件被建立,或者考慮怎樣去獲取引用,在Ruby中這些都是內建的。

讓我們進入第四部分。

相關文章