本書總結(jié)了32條Ruby編程技巧,幫助讀者寫出清晰、優(yōu)雅、穩(wěn)定的Ruby代碼。作者Avdi Grimm主張Ruby方法應(yīng)該像故事一樣易于閱讀。他將Ruby方法分成輸入處理(Collecting Input)、功能實(shí)現(xiàn)(Performing Work)、輸出處理(Delivering Output)、失敗處理(Handling Failures)四個(gè)部分,針對每個(gè)部分的特點(diǎn)歸納實(shí)用的編程模式,并配合豐富的實(shí)例講解,讓讀者寫出優(yōu)雅實(shí)用的Ruby代碼,找回Ruby編程的樂趣。
在2011年新奧爾良的RubyConf上,我認(rèn)識了Avdi。我并無過人之處,不過是一個(gè)無名小輩,趕鴨子上架,做了一兩個(gè)演講而已。Jim Weirich 聽了其中一個(gè)演講,當(dāng)即招手叫來Avdi,把我介紹給他。我們?nèi)颂^部分演講,在會場外聊得非常開心。這次聊天不但開闊了見聞,而且改變了我們的生活。 我們?nèi)齻(gè)當(dāng)時(shí)是那么激動,以至于我都好奇為啥沒人叫我們“閉嘴”,也沒被休息室的負(fù)責(zé)人“請出去”。
時(shí)至今日,回想起那段時(shí)光,我們的談話仍縈繞耳邊,仿佛依然能感受到Jim那極具感染力的熱情和Avdi的誠摯。我都不敢相信這一切是真的,不相信我有那么幸運(yùn)。我仿佛已羽化登仙——登上了那片只屬于程序員的仙境。
我坦言在寫一本名為《Practical Object-Oriented Design in Ruby》的書,Avdi很有風(fēng)度地表示愿意試讀。說是“試讀”,他實(shí)際上費(fèi)了很多心血和精力校對。他不僅給出建議,還逐行看Ruby代碼,糾正錯(cuò)誤、提升代碼質(zhì)量。當(dāng)我感謝他時(shí),他雖很高興,但總是謙虛地說沒做什么。就這點(diǎn)而言,我有相當(dāng)?shù)陌l(fā)言權(quán),可以負(fù)責(zé)任地告訴你,他非常友好和有耐心。
因此,我很榮幸推薦這本《優(yōu)雅的Ruby》。Avdi的思想貫穿全書。他關(guān)于方法如何組織的觀點(diǎn)為我們思考代碼提供了新的視角,他的“秘籍”為我們編寫代碼提供了最直接、最通俗易懂的指導(dǎo)。
他有一種天賦,能將嚴(yán)謹(jǐn)?shù)募夹g(shù)理念用獨(dú)特而輕松的方式講出來。在本書中,有銀行賬戶,當(dāng)然也有書迷們喜歡的“Bookface”。當(dāng)你需要放松時(shí),會有調(diào)皮的小貓(emergency kittens)來陪你玩。他的這種天賦讓書中那些例子顯得既實(shí)用合理又有趣。
寫書可謂是一件苦差事,但寫書的動力深深扎根在我心里。正是這種動力促使我們幫助他人,講解知識,提升自我,改變世界。我問Avdi為何要寫這本書,他在郵件中提到了幾個(gè)原因,在此,我想分享其中的兩點(diǎn)。他說,“講解知識很有趣”,而且“整個(gè)社區(qū)一直以來都對我很好,我一直在想他們?yōu)槭裁催@么好,我希望自己也可以(為社區(qū))做點(diǎn)貢獻(xiàn)”。
現(xiàn)在你知道了,他就是這樣一個(gè)充滿幽默感和責(zé)任感的人。
Avdi寫的代碼可讀性非常高。在這本書里,他將教你寫出優(yōu)雅的代碼。這對開發(fā)人員來講,真是無上的榮耀。
好好欣賞這本書吧!
Sandi Metz
2014年3月28日
Avdi Grimm是ShipRise.com和RubyTapas.com的創(chuàng)始人,有著十幾年Ruby編程經(jīng)驗(yàn),是Ruby程序界的領(lǐng)軍人物。他目前與妻子居住在 美國賓夕法尼亞南部。
第1章 引言 1
1.1 當(dāng)Ruby遭遇現(xiàn)實(shí) 2
1.2 自信優(yōu)雅的代碼 2
1.3 好的故事,糟糕的講述 3
1.4 像寫故事一樣寫代碼 4
1.5 方法的四個(gè)部分 4
1.6 本書組織結(jié)構(gòu) 8
第2章 功能實(shí)現(xiàn) 11
2.1 發(fā)送有效的消息 12
2.2 導(dǎo)入交易記錄 13
2.3 識別消息 14
2.4 識別角色 14
2.5 避免馬蓋先主義 17
2.6 讓語言為系統(tǒng)服務(wù) 17
2.7 像鴨子一樣叫 18
2.8 馴養(yǎng)鴨群 19
第3章 收集輸入 21
3.1 輸入處理概述 21
3.1.1 間接輸入 23
3.1.2 從角色到對象 26
3.1.3 保護(hù)邊界而非內(nèi)部 27
3.2 使用內(nèi)置的類型轉(zhuǎn)換協(xié)議 28
3.2.1 適用場景 28
3.2.2 摘要 28
3.2.3 基本原理 28
3.2.4 示例:宣布獲獎(jiǎng)結(jié)果 28
3.2.5 示例:Emacs配置文件 30
3.2.6 標(biāo)準(zhǔn)類型轉(zhuǎn)換方法列表 32
3.2.7 顯式轉(zhuǎn)換和隱式轉(zhuǎn)換 33
3.2.8 明確提出參數(shù)要求 37
3.2.9 小結(jié) 39
3.3 有條件地使用類型轉(zhuǎn)換方法 40
3.3.1 使用場景 40
3.3.2 摘要 40
3.3.3 基本原理 40
3.3.4 示例:打開文件 40
3.3.5 違反鴨子類型的唯一特例 42
3.3.6 小結(jié) 45
3.4 自定義類型轉(zhuǎn)換協(xié)議 46
3.4.1 使用場景 46
3.4.2 摘要 46
3.4.3 基本原理 46
3.4.4 示例:接收一個(gè)點(diǎn)或一對坐標(biāo) 46
3.4.5 小結(jié) 48
3.5 定義自定義類型的轉(zhuǎn)換協(xié)議 49
3.5.1 使用場景 49
3.5.2 摘要 49
3.5.3 基本原理 49
3.5.4 示例:將英尺轉(zhuǎn)換為米 49
3.5.5 小結(jié) 52
3.6 利用內(nèi)置強(qiáng)制類型轉(zhuǎn)換方法 53
3.6.1 使用場景 53
3.6.2 摘要 53
3.6.3 基本原理 53
3.6.4 示例:格式化打印數(shù)字 53
3.6.5 Hash.[] 57
3.6.6 小結(jié) 57
3.7 用Array()將輸入數(shù)組化 58
3.7.1 使用場景 58
3.7.2 摘要 58
3.7.3 基本原理 58
3.7.4 示例:可變參數(shù) 58
3.7.5 小結(jié) 60
3.8 自定義強(qiáng)制類型轉(zhuǎn)換方法 61
3.8.1 使用場景 61
3.8.2 摘要 61
3.8.3 基本原理 61
3.8.4 示例:應(yīng)用于2D圖形中的強(qiáng)制類型轉(zhuǎn)換方法 62
3.8.5 關(guān)于module_function 63
3.8.6 結(jié)合類型轉(zhuǎn)換協(xié)議和強(qiáng)制類型轉(zhuǎn)換方法 64
3.8.7 用Lambdas表達(dá)式作case分支 66
3.8.8 小結(jié) 67
3.9 用自定義類替換類字符串類型 68
3.9.1 使用場景 68
3.9.2 摘要 68
3.9.3 基本原理 68
3.9.4 示例:紅綠燈的狀態(tài)問題 69
3.9.5 小結(jié) 77
3.10 用適配器裝飾輸入 78
3.10.1 使用場景 78
3.10.2 摘要 78
3.10.3 基本原理 78
3.10.4 示例:將日志寫進(jìn)IRC 78
3.10.5 小結(jié) 82
3.11 利用透明適配器逐步消除類型依賴 83
3.11.1 適用場景 83
3.11.2 摘要 83
3.11.3 基本原理 83
3.11.4 示例:再探將日志寫進(jìn)IRC的示例 83
3.11.5 小結(jié) 86
3.12 利用先決條件排除非法輸入 87
3.12.1 使用場景 87
3.12.2 摘要 87
3.12.3 基本原理 87
3.12.4 示例:員工入職日期 87
3.12.5 “可執(zhí)行文檔” 91
3.12.6 小結(jié) 91
3.13 利用#fetch確保Hash鍵的存在性 92
3.13.1 使用場景 92
3.13.2 摘要 92
3.13.3 基本原理 92
3.13.4 示例:useradd(8)包裝器 92
3.13.5 嘗試#fetch 95
3.13.6 自定義#fetch 98
3.13.7 小結(jié) 99
3.14 利用#fetch提供默認(rèn)參數(shù) 100
3.14.1 使用場景 100
3.14.2 摘要 100
3.14.3 基本原理 100
3.14.4 示例:可選的logger參數(shù) 100
3.14.5 可重用的#fetch代碼塊 104
3.14.6 雙參數(shù)#fetch 106
3.14.7 小結(jié) 107
3.15 用斷言驗(yàn)證假設(shè) 108
3.15.1 使用場景 108
3.15.2 摘要 108
3.15.3 基本原理 108
3.15.4 示例:導(dǎo)入銀行記錄 108
3.15.5 小結(jié) 113
3.16 用衛(wèi)語句來處理特殊場景 114
3.16.1 使用場景 114
3.16.2 摘要 114
3.16.3 基本原理 114
3.16.4 示例:“靜音模式”標(biāo)志 114
3.16.5 提前返回 116
3.16.6 小結(jié) 117
3.17 用對象表示特殊場景 118
3.17.1 使用場景 118
3.17.2 摘要 118
3.17.3 基本原理 118
3.17.4 示例:游客用戶 118
3.17.5 用特例對象來表示當(dāng)前用戶 121
3.17.6 小步改進(jìn) 126
3.17.7 保持特例對象和普通對象的同步 128
3.17.8 小結(jié) 129
3.18 用空對象表示不做事的情況 130
3.18.1 使用場景 130
3.18.2 摘要 130
3.18.3 基本原理 130
3.18.4 示例:輸出日志到shell命令行 131
3.18.5 通用空對象 133
3.18.6 穿越事界 134
3.18.7 讓空對象返回false 138
3.18.8 小結(jié) 140
3.19 用良性值替代nil 142
3.19.1 使用場景 142
3.19.2 摘要 142
3.19.3 基本原理 142
3.19.4 示例:顯示會員地理位置信息 142
3.19.5 無害就好 145
3.19.6 小結(jié) 146
3.20 用symbols做占位符 147
3.20.1 使用場景 147
3.20.2 摘要 147
3.20.3 基本原理 147
3.20.4 示例:web service可選認(rèn)證 147
3.20.5 都是nil惹的禍 149
3.20.6 帶語義的占位符 152
3.20.7 小結(jié) 154
3.21 將參數(shù)封裝到參數(shù)對象中 155
3.21.1 使用場景 155
3.21.2 摘要 155
3.21.3 基本原理 155
3.21.4 參數(shù)對象回顧 155
3.21.5 添加可選參數(shù) 159
3.21.6 小結(jié) 163
3.22 提取參數(shù)構(gòu)建器 164
3.22.1 使用場景 164
3.22.2 摘要 164
3.22.3 基本原理 164
3.22.4 示例:方便的繪點(diǎn)API 164
3.22.5 Net/HTTP vs. Faraday 168
3.22.6 提取參數(shù)Builder 170
3.22.7 小結(jié) 172
第4章 輸出處理 173
4.1 用全函數(shù)作為方法返回值 174
4.1.1 使用場景 174
4.1.2 摘要 174
4.1.3 基本原理 174
4.1.4 示例:單詞搜索 174
4.1.5 小結(jié) 178
4.2 執(zhí)行回調(diào)而非返回狀態(tài) 179
4.2.1 使用場景 179
4.2.2 摘要 179
4.2.3 基本原理 179
4.2.4 示例 179
4.2.5 小結(jié) 182
4.3 用良性值表示失敗 183
4.3.1 使用場景 183
4.3.2 摘要 183
4.3.3 基本原理 183
4.3.4 示例:在側(cè)邊欄上顯示推文 183
4.3.5 小結(jié) 185
4.4 用特例對象表示失敗 186
4.4.1 使用場景 186
4.4.2 摘要 186
4.4.3 基本原理 186
4.4.4 示例:游客用戶 186
4.4.5 小結(jié) 187
4.5 返回狀態(tài)對象 188
4.5.1 使用場景 188
4.5.2 摘要 188
4.5.3 基本原理 188
4.5.4 示例:記錄導(dǎo)入結(jié)果 188
4.5.5 小結(jié) 192
4.6 將狀態(tài)對象傳給回調(diào) 193
4.6.1 使用場景 193
4.6.2 摘要 193
4.6.3 基本原理 193
4.6.4 示例:將導(dǎo)入結(jié)果傳給回調(diào) 193
4.6.5 測試狀態(tài)對象 198
4.6.6 小結(jié) 199
4.7 用throw提前終止執(zhí)行 200
4.7.1 使用場景 200
4.7.2 摘要 200
4.7.3 示例:提前終止HTML文檔解析 200
4.7.4 小結(jié) 205
第5章 失敗處理 207
5.1 優(yōu)先使用頂層異常捕獲 208
5.1.1 使用場景 208
5.1.2 摘要 208
5.1.3 基本原理 208
5.1.4 示例 208
5.1.5 小結(jié) 209
5.2 用受檢方法封裝危險(xiǎn)操作 210
5.2.1 使用場景 210
5.2.2 摘要 210
5.2.3 基本原理 210
5.2.4 示例 210
5.2.5 演進(jìn)成Adapters 212
5.2.6 小結(jié) 212
5.3 使用護(hù)衛(wèi)方法 213
5.3.1 使用場景 213
5.3.2 摘要 213
5.3.3 基本原理 213
5.3.4 示例:子進(jìn)程狀態(tài)檢測 213
5.3.5 小結(jié) 216
第6章 為了優(yōu)雅重構(gòu) 217
6.1 MetricFu 218
6.1.1 Location 218
6.1.2 HotspotAnalyzedProblems 222
6.1.3 排名 225
6.2 Stringer 227
第7章 后記 231
全書可以分成六個(gè)部分。
首先討論用消息和角色的思想來實(shí)現(xiàn)方法。
第2章討論“實(shí)現(xiàn)功能”。雖然這看起來不符合前文提到的“方法組成順序”,但是通過這一章的學(xué)習(xí),你將學(xué)會思考如何設(shè)計(jì)方法,以便為后面的模式學(xué)習(xí)打下基礎(chǔ)。
第3章到第5章是本書最核心的模式部分,每個(gè)模式又由五個(gè)部分組成:
1. 適用場景。就像藥品包裝上寫有適用癥狀,這部分內(nèi)容簡要地介紹了模式的適用場景,比如用來解決特定問題,或者修正編寫代碼的不良習(xí)慣。
2. 摘要。當(dāng)你嘗試回憶某個(gè)模式,但又不記得名字時(shí),摘要能夠給你莫大的幫助。
3. 基本原理,闡述為何要用這個(gè)模式。
4. 示例。借助一兩個(gè)具體的例子闡述選擇該模式的原因及實(shí)現(xiàn)方法。
5. 小結(jié)。總結(jié)模式的優(yōu)點(diǎn)、潛在的陷阱和不足。
根據(jù)我提出的組成方法的原則,這些模式被分為以下三大系列。
? 輸入處理的模式。
? 輸出處理的模式,讓方法調(diào)用者優(yōu)雅地調(diào)用方法。
異常處理模式,保障方法井然有序。
第6章將討論一些實(shí)際的Ruby開源項(xiàng)目示例,并把本書中的模式應(yīng)用到它們身上。
3.times { rejoice! }
總之,學(xué)習(xí)這套模式可以讓你寫出優(yōu)雅的代碼:易于理解和維護(hù)、更少的bug、更靈活地適應(yīng)需求變化。
除了幫助讀者掌握寫出優(yōu)雅代碼的技巧,我更希望幫助讀者重拾初學(xué)Ruby的那份快樂,找回寫代碼寫到不經(jīng)意微笑的狀態(tài),養(yǎng)成快樂編程的習(xí)慣。這些收獲可以讓你為獲得更大的快樂而嘗試更大的項(xiàng)目,這種快樂就如同初識Ruby時(shí)的興奮一樣。