一、 讓自己熟悉Ruby
1、理解 Ruby 中的 True
在 Ruby 中,除了 false 和 nil, 其他值都是真值。
false 和 nil 是唯二的假值,因此用 true 對(duì)象表示真值是冗余的,任何非 false、非 nil 的對(duì)象都可以表示真值。
false == x ,盡可能的將判斷的標(biāo)準(zhǔn)寫(xiě)在 '==' ?的左邊,以避免被其他類所覆蓋
2、所有對(duì)象的值都可能為 nil
我們需要通過(guò)優(yōu)秀的測(cè)試挑出各種各樣的錯(cuò)誤。
根據(jù)Ruby的類型系統(tǒng)運(yùn)作方式,任何對(duì)象都可以為nil。
在適合的時(shí)候用轉(zhuǎn)換方法,如to_s 和 to_i,可以將nil對(duì)象強(qiáng)制轉(zhuǎn)換為你期待的類型。
Array#compact方法返回去除所有nil元素的數(shù)組。
3、避免使用 Ruby 中奇怪的 Perl 風(fēng)格語(yǔ)法
方法 String#match 比 “ =~ ” 更符合語(yǔ)言習(xí)慣,并且該方法不使用任何操作符 “ =~ ” 所產(chǎn)生的特殊全局變量。
用 $LOAD_PATH 代替 全局變量 $:?
避免使用隱式讀寫(xiě)全局變量 $_ 的方法(比如, Kernel#print、Regexp#~ 等)。
4、留神,常量是可變的
總是將常量?jī)鼋Y(jié),從而防止其被改變。
如果常量引用了一個(gè)集合對(duì)象比如數(shù)組或者散列,那么凍結(jié)這個(gè)集合及其所有元素。
要防止常量被重新賦值,可以凍結(jié)定義它的那個(gè)模塊。
5、留意運(yùn)行時(shí)警告
使用命令行選項(xiàng) “ -w ” 來(lái)運(yùn)行 Ruby 解釋器以啟用編譯時(shí)和運(yùn)行時(shí)的警告,設(shè)置環(huán)境變量 RUBYOPT 為 “ -w ”也可以達(dá)到相同目的。
如果必須禁用運(yùn)行時(shí)的警告,可以臨時(shí)將全局變量 $VERBOSE 設(shè)置為nil。
二、類、對(duì)象和模塊
6、了解 Ruby 如何構(gòu)建繼承體系
要尋找一個(gè)方法、Ruby 只需要向上搜索類體系。 如果沒(méi)有找到這個(gè)方法,就從起點(diǎn)開(kāi)始搜索 method_missing 方法。
包含模塊時(shí) Ruby 會(huì)悄悄地創(chuàng)建單例類,并將其插入在繼承體系中包含它的類的上方。
單例方法(類方法和針對(duì)對(duì)象的方法)存儲(chǔ)于單例類中,它也會(huì)被插入繼承體系中。
對(duì)象是變量的容器。類是方法和常量的容器。超類是一個(gè)類的父類的花哨名字。Ruby 可以通過(guò) include 方法將模塊引入類,實(shí)現(xiàn)類似多繼承的效果。單例類是繼承體系不可見(jiàn)的類,也僅僅是沒(méi)有名字的、被加以限制的常規(guī)類。
7、了解 super 的不同行為
當(dāng)你想重載繼承體系中的一個(gè)方法時(shí),關(guān)鍵字 super 可以幫你調(diào)用它。
不加括號(hào)地?zé)o參調(diào)用 super 等價(jià)于講宿主方法的所有參數(shù)傳遞給要調(diào)用的方法。這樣使用 super 僅僅在目標(biāo)方法和宿主方法接受相同數(shù)量的參數(shù)時(shí)才可用。
如果希望使用 super 并且不向重載方法傳遞任何參數(shù),必須使用空括號(hào),即super()。
當(dāng) super 調(diào)用失敗時(shí),自定義的 method_missing 方法將丟棄一些有用的信息。第30條中有 method_missing 的替代解決方案。
如果你希望調(diào)用一個(gè)定義在超類的方法,而同時(shí)如果包含的模塊中也定義了同名方法,super 會(huì)在找到第一個(gè)匹配的同名方法后停下來(lái),而那時(shí)包含的模塊中的方法,不是超類中的。如果真的遇到這種情況,可能是設(shè)計(jì)上存在嚴(yán)重的問(wèn)題。可以考慮使用組合而非繼承了。
8、初始化子類時(shí)調(diào)用 super
當(dāng)創(chuàng)建子類對(duì)象時(shí),Ruby 不會(huì)自動(dòng)調(diào)用超類中的 initialize 方法。作為替代,常規(guī)的方法查詢規(guī)則也適用于 initialize 方法,只有第一個(gè)匹配的副本會(huì)被調(diào)用。
當(dāng)為顯式使用繼承的類定義 initialize 方法時(shí),使用 super 來(lái)初始化其父類。在定義 initialize_copy 方法時(shí),應(yīng)使用相同的規(guī)則。
initialize 并不是構(gòu)建新對(duì)象的唯一方式。Ruby允許我們使用 dup 和 clone 方法創(chuàng)建對(duì)象的副本。當(dāng)你使用這些方法任意一個(gè)時(shí),可以通過(guò)定義 initialize_copy 方法對(duì)新創(chuàng)建的副本對(duì)象執(zhí)行一些特別的邏輯。
9、提防 Ruby 最棘手的解析
setter 方法在調(diào)用時(shí)需要顯式的接收者。沒(méi)有接收者時(shí),會(huì)被 Ruby 解析為變量賦值。
在實(shí)例方法仲調(diào)用 setter 方法時(shí),使用 self 作為接收者。
在調(diào)用非 setter 方法時(shí),不需要顯式指定接收者。換句話說(shuō),不要使用不必要的 self , 那會(huì)弄亂你的代碼。
10、推薦使用 Struct 而非 Hash 存儲(chǔ)結(jié)構(gòu)化數(shù)據(jù)
在處理結(jié)構(gòu)化數(shù)據(jù)時(shí),如果創(chuàng)建一個(gè)新類不那么合適時(shí),推薦使用 Struct 而非 Hash。
將 Struct::new 的返回值賦給常量,并像類一樣使用它。
使用 Struct 在屬性名拼錯(cuò)的時(shí)候會(huì)引發(fā)一個(gè) NoMethodError 異常,使用哈希時(shí)不會(huì)有這個(gè)問(wèn)題,因?yàn)樵L問(wèn)非法鍵的時(shí)候只會(huì)返回nil,不過(guò)也意味著在之后的代碼中,你將被卷入更難發(fā)現(xiàn)的 TypeError 異常。
11、通過(guò)在模塊中嵌入代碼來(lái)創(chuàng)建命名空間
通過(guò)在模塊中嵌入代碼來(lái)創(chuàng)建命名空間。
讓你的命名空間結(jié)構(gòu)和目錄結(jié)構(gòu)相同。
如果使用時(shí)可能出現(xiàn)歧義,可使用 “ :: ” 來(lái)限定頂級(jí)常量(比如:::Array)
12、理解等價(jià)的不同用法
絕不要重載 equal? 方法。該方法的預(yù)期行為是,嚴(yán)格比較兩個(gè)對(duì)象,僅當(dāng)它們同時(shí)指向內(nèi)存中同一個(gè)對(duì)象時(shí)其值為真(即,當(dāng)它們具有相同的 object_id 時(shí))。
Hash 類在沖突檢測(cè)時(shí)使用 eql? 方法來(lái)比較鍵對(duì)象。默認(rèn)實(shí)現(xiàn)可能和你的想象不同。遵循第13條的建議之后再使用別名 eql? 來(lái)替代 “ == ” 書(shū)寫(xiě)更合理的 hash 方法。
使用 “ == ” 操作符來(lái)測(cè)試兩個(gè)對(duì)象是否表示相同的值。有些類比如表示數(shù)字的類會(huì)有一個(gè)粗糙的等號(hào)操作符進(jìn)行類型轉(zhuǎn)換。
case 表達(dá)式使用 “ == ” 操作符來(lái)測(cè)試每個(gè) when 語(yǔ)句的值。左操作數(shù)是 when 的參數(shù),右操作數(shù)是 case 的參數(shù)。
13、通過(guò) “ <=> ” 操作符實(shí)現(xiàn)比較和比較模塊
通過(guò)定義 “ <=> ” 操作符和引入 Comparable 模塊實(shí)現(xiàn)對(duì)象的排序。( Comparable 模塊包含 “<” 、"<=" 、"==" 、">" 、">=")
如果左操作數(shù)不能與右操作數(shù)進(jìn)行比較,“ <=> ” 操作符應(yīng)該返回 nil。
如果要實(shí)現(xiàn)類的 “ <=> ” 運(yùn)算符,應(yīng)該考慮將 eql? 方法設(shè)置為 “ == ” 操作符的別名,特別是當(dāng)你希望該類的所有實(shí)例可以被用來(lái)作為哈希鍵的時(shí)候,就應(yīng)該重載哈希方法。
14、通過(guò) protected 方法共享私有狀態(tài)
封裝是面向?qū)ο缶幊讨械闹饕獪?zhǔn)則之一,它是指一個(gè)對(duì)象的內(nèi)部實(shí)現(xiàn)僅可被內(nèi)部訪問(wèn),不可被外部訪問(wèn)。
當(dāng)設(shè)計(jì)一個(gè)具有內(nèi)部狀態(tài)的類時(shí),正確的做法是盡可能減少對(duì)該狀態(tài)的直接訪問(wèn),而是使用最小數(shù)量的訪問(wèn)方法。

通過(guò)protected方法共享私有狀態(tài)。
一個(gè)對(duì)象的protected方法若要被顯示接收者調(diào)用,除非改對(duì)象與接收者是同類對(duì)象或具有相同的定義該protected方法的超類。
15、優(yōu)先使用實(shí)例變量而非類變量
Ruby 語(yǔ)言存在兩種用 @ 標(biāo)識(shí)的變量: 實(shí)例變量和類變量。
如果你的程序存在多線程控制,那么在不使用互斥鎖的情況下改變?nèi)魏巫兞慷际遣话踩摹?/p>
優(yōu)先使用實(shí)例變量而非類變量。
類也是對(duì)象,所以它們擁有自己的私有實(shí)例變量集合。
三、集合
16、在改變作為參數(shù)的集合之前復(fù)制它們
Ruby 中參數(shù)時(shí)按引用傳遞的,而不是值傳遞。這個(gè)規(guī)則有一個(gè)例外值注意: 它不適用于 Fixnum 對(duì)象。
在改變集合之前先復(fù)制它們。
dup 方法和 clone 方法只會(huì)進(jìn)行淺拷貝。
對(duì)于多數(shù)對(duì)象來(lái)說(shuō),可以使用 Marshal 來(lái)完成深拷貝。
17、使用 Array 方法將 nil 及標(biāo)量對(duì)象轉(zhuǎn)換成數(shù)組
使用 Array 方法將 nil 及標(biāo)量對(duì)象轉(zhuǎn)換成數(shù)組。
不要講哈希傳給 Array 方法,它會(huì)被轉(zhuǎn)化成一個(gè)嵌套數(shù)組的集合。
18、考慮使用集合高效檢察元素的包含性
Ruby 自帶了兩套不同的類庫(kù),核心庫(kù)已經(jīng)被每個(gè)程序預(yù)先加載,另一套就是標(biāo)準(zhǔn)庫(kù),龐大而且不會(huì)被自動(dòng)加載,在你使用的時(shí)候需要正確的引入它們。
Array 的 include? 方法的性能是最差的。當(dāng)數(shù)組中的元素增加時(shí),可以考慮轉(zhuǎn)用哈希來(lái)代替。
如果你不需要元素按照某一特定順序來(lái)排列,無(wú)需隨機(jī)訪問(wèn)任一元素,且需要高效的檢測(cè)元素的包含性,Set 類就是你需要的。
插入 Set 的對(duì)象必須也被當(dāng)作哈希的鍵來(lái)使用。并且使用之前必須引入它。
19、了解如何通過(guò) reduce 方法折疊集合
總是要給累加器一個(gè)初始值。
給予 reduce 的塊總是要返回一個(gè)累加器。對(duì)當(dāng)前累加器的修改是可行的,要記住從塊中返回。
20、考慮使用默認(rèn)哈希值
考慮使用默認(rèn)的 Hash 值。
使用 has_key? 方法或它的任意別名來(lái)檢查哈希是否包含某個(gè)鍵。也就是說(shuō),不要以為當(dāng)訪問(wèn)一個(gè)不存在的鍵是都會(huì)返回 nil。
如果某段代碼在接受哈希的非法鍵時(shí)會(huì)返回 nil,不要為傳入該方法的哈希使用默認(rèn)值。
相比使用默認(rèn)值,有些時(shí)候使用 Hash#fetch 方法能更加安全。
21、對(duì)集合優(yōu)先使用委托而非繼承
對(duì)集合優(yōu)先使用委托而非繼承
不要忘記編寫(xiě)用來(lái)復(fù)制委托目標(biāo)的 initialize_copy 方法
編寫(xiě) freeze、taint 以及 untaint 方法時(shí),先傳遞信息給委托目標(biāo),之后調(diào)用 super 方法。
四、異常
22、使用定制的異常而不是拋出字符串
避免使用字符串作為一場(chǎng),它們會(huì)被轉(zhuǎn)換成原聲 RuntimeError 對(duì)象。取而代之,創(chuàng)建一個(gè)定制的異常類。
定制的異常類應(yīng)該繼承自 StandardError , 且類名應(yīng)以 ’Error‘ 結(jié)尾。
當(dāng)為一個(gè)工程創(chuàng)建了不止一個(gè)異常類時(shí),從創(chuàng)建一個(gè)繼承自 StandardError 的基類開(kāi)始。其他的異常類應(yīng)該繼承自該定制的基類。
如果你對(duì)你的定制異常類編寫(xiě)了 initialize 方法,務(wù)必確保其調(diào)用了 super 方法,最好在調(diào)用時(shí)以錯(cuò)誤信息作為參數(shù)。
在 initialize 方法中設(shè)置錯(cuò)誤信息時(shí),請(qǐng)牢記: 如果在 raise 方法中再度設(shè)置錯(cuò)誤信息會(huì)覆蓋原本在 initialize 中設(shè)置的那一條。
23、捕獲可能的最具體的異常
只捕獲那些你知道如何恢復(fù)的異常
當(dāng)捕獲異常時(shí),首先處理最特殊的類型。在異常的繼承關(guān)系中位置越高的,越應(yīng)該排在 rescue 鏈的后面。
避免捕獲如 StandardError 這樣的通用異常。如果你已經(jīng)這么做了,就應(yīng)該想想你真正想做的是不是能夠用 ensure 語(yǔ)句來(lái)實(shí)現(xiàn)。
在異常發(fā)生的情況下,從 rescue 語(yǔ)句中拋出的異常將會(huì)替換當(dāng)前異常并離開(kāi)當(dāng)前的作用域。
24、通過(guò)塊和 rescue 管理資源
通過(guò) ensure 語(yǔ)句來(lái)釋放任何已獲得的資源。
公國(guó)在類方法上使用塊和 ensure 語(yǔ)句講資源管理的邏輯抽離出來(lái)。
確保 ensure 語(yǔ)句中使用的變量已經(jīng)被初始化過(guò)了。
25、通過(guò)臨近的 end 退出 ensure 語(yǔ)句
避免在 ensure 語(yǔ)句中顯式使用 return 語(yǔ)句。這意味著方法體內(nèi)存在著某些錯(cuò)誤的邏輯。
同樣,不要再 ensure 語(yǔ)句中直接使用 throw。你應(yīng)該將 throw 放在方法主體呢。
當(dāng)執(zhí)行迭代時(shí),不要再 ensure 語(yǔ)句中執(zhí)行 next 或 break。仔細(xì)想想在迭代內(nèi)到底需不需要 begin 塊。將關(guān)系反轉(zhuǎn)或許更加合理,就是將迭代放在 begin 塊中。
一般來(lái)說(shuō),不要再 ensure 語(yǔ)句中改變控制流。在 resucue 語(yǔ)句中完成這樣的工作,你的意圖會(huì)更加清晰。
26、限制 retry 次數(shù),改變重試頻率并記錄異常信息。
永遠(yuǎn)不要無(wú)條件 retry,要把它看做代碼中的隱式循環(huán);在代碼塊的外圍定義重試次數(shù),當(dāng)超出最大重試次數(shù)時(shí)重新拋出異常。
retry 時(shí)記錄具有審計(jì)作用的異常信息, 如果重試有問(wèn)題的代碼解決不了問(wèn)題,需要追根溯源地去了解異常是如何發(fā)生的。
當(dāng)在 retry 之前使用延時(shí)時(shí),需要考慮增加延時(shí)避免加劇問(wèn)題。
27、throw 比 raise 更適合用來(lái)跳出作用域
在復(fù)雜的流程控制中,可以考慮使用 throw 和 raise,這種方法一個(gè)額外的好處是可以把一個(gè)對(duì)象傳遞到上層調(diào)用棧并作為 catch 的最終返回值。
盡量使用簡(jiǎn)單的方法來(lái)控制程序結(jié)果,可以通過(guò)方法調(diào)用和 return 重寫(xiě) catch 和 throw。
五、元編程
28、熟悉 Ruby 模塊和類的鉤子方法
所有的鉤子方法都需要被定義為單例方法。
添加、刪除、取消定義方法的鉤子方法參數(shù)是方法名,而不是類名,如果需要,使用 self 去獲取類的信息。
定義 singleton_method_added 會(huì)觸發(fā)自身。
不要覆蓋 extend_object、append_features 和 prepend_features 方法,使用 extended、included 和 prepended 替代。
29、在類的鉤子方法中執(zhí)行 super 方法
在類的鉤子方法中執(zhí)行 super。
30、推薦使用 define_method 而非 method_missing
define_method 優(yōu)于 method_missing。
如果必須使用 method_missing,最好也定義 respond_to_missing?方法。
31、了解不同類型的 eval 間的差異
使用 instance_eval 和 instance_exec 定義的是單例方法。
class_eval、 module_eval、 class_exec 和 module_exec 方法只可以被模塊或者方法使用。通過(guò)這些定義的方法都是實(shí)例方法。
32、猴子補(bǔ)丁
盡管 refinement 已經(jīng)不再是實(shí)驗(yàn)性的功能,它仍然有可能被修改得更為成熟。
在不同的語(yǔ)法作用域,在使用 refinement 之前必須先激活它。
33、使用別名鏈執(zhí)行被修改的方法
在設(shè)置別名鏈時(shí),需要確保別領(lǐng)是獨(dú)一無(wú)二的。
必要的時(shí)候要考慮提供一個(gè)撤銷(xiāo)別名鏈的方法。
34、 支持多種 Proc 參數(shù)數(shù)量
與弱 proc 對(duì)象不同,在參數(shù)數(shù)量不匹配時(shí),強(qiáng) Proc 對(duì)象會(huì)拋出 ArgumentError 異常。
可以使用 Proc#arity 方法得到 Proc 期望的參數(shù)數(shù)量,如果返回的是正數(shù),則意味著有多少參數(shù)時(shí)必須的。如果返回的是負(fù)數(shù),則意味著 Proc 有些參數(shù)是可選的,可以通過(guò) ’ ~ ‘ 來(lái)得到有多少是必須參數(shù)。
35、使用模塊前置時(shí)請(qǐng)謹(jǐn)慎思考
prepend 方法在使用時(shí)對(duì)類體系結(jié)構(gòu)的影響是:它將模塊插入到接收者之前。這和 include 方法有很大不同:include 則是將模塊插入到接收者和其超類之間。
與 included 和 extended 模塊鉤子一樣,前置模塊也會(huì)觸發(fā) prepended 鉤子。
六、測(cè)試
36、熟悉單元測(cè)試工具 MiniTest
測(cè)試方法需要以 ' test_ ' 作為前綴。
簡(jiǎn)短的測(cè)試更容易理解,也更容易維護(hù)。
使用合適的斷言方法生成更易讀的出錯(cuò)信息。
斷言和反演的文檔在 MiniTest::Assertions 中。
37、熟悉 MiniTest 的需求測(cè)試
使用 describe 方法創(chuàng)建測(cè)試類,使用 it 定義測(cè)試用例。
雖然在需求說(shuō)明測(cè)試中,斷言仍然可用,但是更推薦使用諸如到 Object 中的期望方法。
在 MiniTest::Expectations 模塊中,可以找到關(guān)于期望方法更詳細(xì)的文檔。
38、使用 Mock 模擬特定對(duì)象
使用 Mock 來(lái)隔離外部系統(tǒng)的不穩(wěn)定因素。
Mock 或者替換沒(méi)有被測(cè)試過(guò)的方法,有可能會(huì)讓這些被 Mock 的代碼在生產(chǎn)環(huán)境中出現(xiàn)問(wèn)題。
請(qǐng)確保在測(cè)試方法代碼的最后調(diào)用了 MiniTest::Mock#verify 方法。
39、力爭(zhēng)代碼被有效測(cè)試過(guò)
使用模糊測(cè)試和屬性測(cè)試工具,幫助測(cè)試代碼的快樂(lè)路徑和異常路徑。
測(cè)試覆蓋率工具會(huì)給你一種虛假的安全感,因?yàn)楸粓?zhí)行過(guò)的代碼不代表這行代碼是正確的。
在編寫(xiě)特性的同事就加上測(cè)試,會(huì)讓測(cè)試容易很多。
在你開(kāi)始尋找導(dǎo)致 bug 的根本原因之前,先寫(xiě)一個(gè)針對(duì)該 bug 測(cè)試。
盡可能多地自動(dòng)化你的測(cè)試。
40、學(xué)會(huì)使用 Ruby 文檔
ri 工具用來(lái)讀取文檔,rdoc 工具用來(lái)生成文檔。
使用命令行選項(xiàng) ’ -d doc ‘ 來(lái)為 RI 工具指定在 ’ doc ‘ 路徑下查找文檔。
運(yùn)行 doc 時(shí),后面跟上命令行選項(xiàng) ’ -f ri ‘ 來(lái)為 RI 工具生成文檔。另外,用 ’ -f darkfish ‘ 來(lái)生成 HTML 格式文檔。
完整的 RDoc 文檔可以在 RDoc::Markup 類中找到。
41、認(rèn)識(shí) IRB 的高級(jí)特性
利用下劃線變量( ’ _ ‘ )來(lái)獲取上一個(gè)表達(dá)式的結(jié)果。
irb 命令可以用來(lái)創(chuàng)建一個(gè)新的會(huì)話,并將當(dāng)前的評(píng)估上下文改變成任意對(duì)象。
考慮 Pry gem 作為 IRB 的替代品。
42、用 Bundler 管理 Gem 依賴
在加載完 Bundler 之后,使用 Bundler,require 會(huì)犧牲一點(diǎn)點(diǎn)靈活性,但是可以加載 Gemfile 仲所有的 gem。
當(dāng)開(kāi)發(fā)應(yīng)用時(shí), 在 Gemfile 中列出所有的 gem,然后把 Gemfile.lock 添加到版本控制系統(tǒng)中。
當(dāng)打包 RubyGem, 在 gem 規(guī)格文件中列出 gem 所有依賴,但不要把 Gemfile.lock 添加到你的版本系統(tǒng)中。
43、為 Gem 依賴設(shè)定版本上限
忽略掉版本上限需求相當(dāng)于你說(shuō)了你可以支持未來(lái)所有的版本。
相當(dāng)于悲觀版本操作符,更加傾向于使用明確的版本范圍。
當(dāng)公開(kāi)發(fā)布一個(gè) gem 時(shí),致命依賴包的版本先知要求,在安全的范圍內(nèi)越寬越好,上線可以擴(kuò)展到下一個(gè)主要發(fā)布版本之前。
八、內(nèi)存管理與性能
44、熟悉 Ruby 的垃圾收集器
垃圾收集器通過(guò)維護(hù)一個(gè)由頁(yè)組成的堆來(lái)管理內(nèi)存。頁(yè)又由槽組成,每個(gè)槽存儲(chǔ)一個(gè)對(duì)象。
在垃圾收集過(guò)程中,可以訪問(wèn)的對(duì)象唄標(biāo)記,而沒(méi)有標(biāo)記的對(duì)象將被清楚,釋放槽來(lái)儲(chǔ)存新對(duì)象。
新對(duì)象被稱為年輕代對(duì)象,如果再一個(gè)垃圾收集周期后依然存活,會(huì)被升級(jí)為年老代對(duì)象,年老代對(duì)象會(huì)在次要標(biāo)記階段自動(dòng)被標(biāo)記為活躍,因此,只有在主要標(biāo)記階段后才能被清除。
GC::stat 方法會(huì)以散列的形式返回垃圾收集器所有的統(tǒng)計(jì)數(shù)據(jù)。
你可以通過(guò)設(shè)定環(huán)境變量來(lái)調(diào)優(yōu)垃圾收集器,使其更實(shí)用于你的應(yīng)用程序。
45、用 Finalizer 構(gòu)建資源安全網(wǎng)
最好使用 ensure 子句來(lái)保護(hù)有限的資源。
如果必須要在 ensure 子句外暴露一個(gè)資源,那么就給它創(chuàng)建一個(gè) finalizer。
永遠(yuǎn)不要再這樣一個(gè)綁定中創(chuàng)建 finalizer Proc。改綁定引用一個(gè)注定會(huì)被銷(xiāo)毀的對(duì)象。啫會(huì)造成垃圾收集器無(wú)法釋放改對(duì)象。
記住,finalizer 可能在一個(gè)對(duì)象銷(xiāo)毀后以及程序終止前的任何時(shí)間被調(diào)用。
46、認(rèn)識(shí) Ruby 性能分析工具
在修改性能差的代碼之前,先使用性能分析工具收集性能相關(guān)的信息。
在 ruby-prof gem 和 Ruby 自帶的標(biāo)準(zhǔn) profile 庫(kù)之間,選擇前者,因?yàn)榍罢吒於铱梢蕴峁┒喾N不同的報(bào)告。
如果使用 Ruby 2.1 或者更新的版本,應(yīng)該考慮使用 stackprof gem 和 memory_profiler gem。
47、避免在循環(huán)中使用對(duì)象字面量
將循環(huán)中的不會(huì)變化的對(duì)象字面量變成常量。
在 Ruby 2.1 及更高的版本中凍結(jié)字符串字面量,相當(dāng)于把它作為常量,可以被整個(gè)運(yùn)行程序共享。
48、考慮從記憶化大開(kāi)銷(xiāo)計(jì)算
考慮提供一個(gè)方法通過(guò)將緩存的變量職位 nil 來(lái)重置記憶化。
確保時(shí)鐘認(rèn)真考慮過(guò)這些由記憶化而跳過(guò)副作用所導(dǎo)致的后果。
如果不希望調(diào)用者修改緩存的變量,那應(yīng)該考慮讓被記憶化的方法返回凍結(jié)對(duì)象。
先用工具分析程序的性能,再考慮是否需要記憶化。