本書分享的實(shí)用技巧可以幫助你編寫魯棒、可靠且易于團(tuán)隊(duì)成員理解和適應(yīng)不斷變化需求的代碼。內(nèi)容涉及如何像高效的軟件工程師一樣思考代碼,如何編寫讀起來像一個(gè)結(jié)構(gòu)良好的句子的函數(shù),如何確保代碼可靠且無錯(cuò)誤,如何進(jìn)行有效的單元測(cè)試,如何識(shí)別可能導(dǎo)致問題的代碼并對(duì)其進(jìn)行改進(jìn),如何編寫可重用并適應(yīng)新需求的代碼,如何提高讀者的中長(zhǎng)期生產(chǎn)力,同時(shí)還介紹了如何節(jié)省開發(fā)人員及團(tuán)隊(duì)的寶貴時(shí)間,等等。
1.易學(xué)易用:從零開始講解編程實(shí)踐,每一個(gè)經(jīng)驗(yàn)教訓(xùn)以“壞代碼”開始,以“好代碼”結(jié)束。
2.貼合實(shí)際:通過50+條錦囊妙計(jì)、100+個(gè)案例手把手教你編寫高質(zhì)量代碼。
3.內(nèi)容豐富:通過11大主題解讀卓越軟件工程師編寫可靠的、易于維護(hù)的代碼的關(guān)鍵概念與技術(shù)。
4.源于實(shí)踐:內(nèi)容整合作者及團(tuán)隊(duì)成員多年的軟件開發(fā)實(shí)踐經(jīng)驗(yàn),通過理論介紹與實(shí)戰(zhàn)相結(jié)合的方式詳細(xì)分析軟件開發(fā)實(shí)踐。
5.注重效率:通過清晰的注釋及代碼分析,幫你輕松理解和掌握編程技巧。
Tom Long,擁有劍橋大學(xué)信息工程專業(yè)碩士學(xué)位,目前擔(dān)任Google公司高級(jí)開發(fā)工程師,領(lǐng)導(dǎo)一支針對(duì)移動(dòng)設(shè)備廣告的自動(dòng)化及優(yōu)化的技術(shù)團(tuán)隊(duì)。目前重點(diǎn)關(guān)注軟件工程、Java開發(fā)、團(tuán)隊(duì)管理、數(shù)據(jù)分析、移動(dòng)廣告、技術(shù)創(chuàng)新等方向。
第 一部分 理論
第 1章 代碼質(zhì)量 3
1.1 代碼如何變成軟件 4
1.2 代碼質(zhì)量目標(biāo) 6
1.2.1 代碼應(yīng)該正常工作 7
1.2.2 代碼應(yīng)該持續(xù)正常工作 7
1.2.3 代碼應(yīng)該適應(yīng)不斷變化的需求 8
1.2.4 代碼不應(yīng)該重復(fù)別人做過的工作 9
1.3 代碼質(zhì)量的支柱 10
1.3.1 編寫易于理解的代碼 10
1.3.2 避免意外 11
1.3.3 編寫難以誤用的代碼 13
1.3.4 編寫模塊化的代碼 14
1.3.5 編寫可重用、可推廣的代碼 15
1.3.6 編寫可測(cè)試的代碼并適當(dāng)測(cè)試 16
1.4 編寫高質(zhì)量代碼是否會(huì)拖慢進(jìn)度 17
1.5 小結(jié) 19
第 2章 抽象層次 20
2.1 空值和本書中的偽代碼慣例 20
2.2 為什么要?jiǎng)?chuàng)建抽象層次 22
2.3 代碼層次 24
2.3.1 API和實(shí)現(xiàn)細(xì)節(jié) 25
2.3.2 函數(shù) 26
2.3.3 類 28
2.3.4 接口 36
2.3.5 當(dāng)層次太薄的時(shí)候 39
2.4 微服務(wù)簡(jiǎn)介 40
2.5 小結(jié) 41
第3章 其他工程師與代碼契約 42
3.1 你的代碼和其他工程師的代碼 42
3.1.1 對(duì)你來說顯而易見,但對(duì)其他人并不清晰的事情 44
3.1.2 其他工程師無意間試圖破壞你的代碼 44
3.1.3 過段時(shí)間,你會(huì)忘記自己的代碼的相關(guān)情況 44
3.2 其他人如何領(lǐng)會(huì)你的代碼的使用方法 45
3.2.1 查看代碼元素的名稱 45
3.2.2 查看代碼元素的數(shù)據(jù)類型 45
3.2.3 閱讀文檔 46
3.2.4 親自詢問 46
3.2.5 查看你的代碼 46
3.3 代碼契約 47
3.3.1 契約的附屬細(xì)則 47
3.3.2 不要過分依賴附屬細(xì)則 49
3.4 檢查和斷言 53
3.4.1 檢查 54
3.4.2 斷言 55
3.5 小結(jié) 56
第4章 錯(cuò)誤 57
4.1 可恢復(fù)性 57
4.1.1 可以從中恢復(fù)的錯(cuò)誤 57
4.1.2 無法從中恢復(fù)的錯(cuò)誤 58
4.1.3 只有調(diào)用者知道能否從某種錯(cuò)誤中恢復(fù) 58
4.1.4 讓調(diào)用者意識(shí)到他們可能想從中恢復(fù)的錯(cuò)誤 60
4.2 魯棒性與故障 60
4.2.1 快速失敗 61
4.2.2 大聲失敗 62
4.2.3 可恢復(fù)性的范圍 62
4.2.4 不要隱藏錯(cuò)誤 64
4.3 錯(cuò)誤報(bào)告方式 67
4.3.1 回顧:異常 68
4.3.2 顯式:受檢異常 68
4.3.3 隱式:非受檢異常 70
4.3.4 顯式:允許為空的返回類型 71
4.3.5 顯式:結(jié)果返回類型 72
4.3.6 顯式:操作結(jié)果返回類型 74
4.3.7 隱式:承諾/未來 76
4.3.8 隱式:返回“魔法值” 78
4.4 報(bào)告不可恢復(fù)的錯(cuò)誤 79
4.5 報(bào)告調(diào)用者可能想要從中恢復(fù)的錯(cuò)誤 79
4.5.1 使用非受檢異常的論據(jù) 79
4.5.2 使用顯式報(bào)錯(cuò)技術(shù)的論據(jù) 82
4.5.3 我的觀點(diǎn):使用顯式報(bào)錯(cuò)技術(shù) 84
4.6 不要忽視編譯器警告 85
4.7 小結(jié) 86
第二部分 實(shí)踐
第5章 編寫易于理解的代碼 91
5.1 使用描述性名稱 91
5.1.1 非描述性名稱使代碼難以理解 91
5.1.2 用注釋代替描述性名稱是很不好的做法 92
5.1.3 解決方案:使名稱具有描述性 93
5.2 適當(dāng)使用注釋 94
5.2.1 多余的注釋可能有害 94
5.2.2 注釋不是可讀代碼的合格替代品 95
5.2.3 注釋可能很適合于解釋代碼存在的理由 96
5.2.4 注釋可以提供有用的高層概述 96
5.3 不要執(zhí)著于代碼行數(shù) 97
5.3.1 避免簡(jiǎn)短但難以理解的代碼 98
5.3.2 解決方案:編寫易于理解的代碼,即便需要更多行代碼 99
5.4 堅(jiān)持一致的編程風(fēng)格 99
5.4.1 不一致的編程風(fēng)格可能引發(fā)混亂 100
5.4.2 解決方案:采納和遵循風(fēng)格指南 100
5.5 避免深嵌套代碼 101
5.5.1 嵌套很深的代碼可能難以理解 102
5.5.2 解決方案:改變結(jié)構(gòu),最大限度地減少嵌套 103
5.5.3 嵌套往往是功能過多的結(jié)果 103
5.5.4 解決方案:將代碼分解為更小的函數(shù) 104
5.6 使函數(shù)調(diào)用易于理解 105
5.6.1 參數(shù)可能難以理解 105
5.6.2 解決方案:使用命名參數(shù) 105
5.6.3 解決方案:使用描述性類型 106
5.6.4 有時(shí)沒有很好的解決方案 107
5.6.5 IDE又怎么樣呢 108
5.7 避免使用未做解釋的值 108
5.7.1 未做解釋的值可能令人困惑 109
5.7.2 解決方案:使用恰當(dāng)命名的常量 110
5.7.3 解決方案:使用恰當(dāng)命名的函數(shù) 110
5.8 正確使用匿名函數(shù) 111
5.8.1 匿名函數(shù)適合于小的事物 112
5.8.2 匿名函數(shù)可能導(dǎo)致代碼難以理解 113
5.8.3 解決方案:用命名函數(shù)代替 113
5.8.4 大的匿名函數(shù)可能造成問題 114
5.8.5 解決方案:將大的匿名函數(shù)分解為命名函數(shù) 115
5.9 正確使用新奇的編程語言特性 116
5.9.1 新特性可能改善代碼 117
5.9.2 不為人知的特性可能引起混亂 117
5.9.3 使用適合于工作的工具 118
5.10 小結(jié) 118
第6章 避免意外 119
6.1 避免返回魔法值 119
6.1.1 魔法值可能造成缺陷 120
6.1.2 解決方案:返回空值、可選值或者錯(cuò)誤 121
6.1.3 魔法值可能偶然出現(xiàn) 122
6.2 正確使用空對(duì)象模式 124
6.2.1 返回空集可能改進(jìn)代碼 125
6.2.2 返回空字符串有時(shí)可能造成問題 126
6.2.3 較復(fù)雜的空對(duì)象可能造成意外 128
6.2.4 空對(duì)象實(shí)現(xiàn)可能造成意外 129
6.3 避免造成意料之外的副作用 130
6.3.1 明顯、有意的副作用沒有問題 131
6.3.2 意料之外的副作用可能造成問題 131
6.3.3 解決方案:避免副作用或者使其顯而易見 134
6.4 謹(jǐn)防輸入?yún)?shù)突變 135
6.4.1 輸入?yún)?shù)突變可能導(dǎo)致程序缺陷 136
6.4.2 解決方案:在突變之前復(fù)制 137
6.5 避免編寫誤導(dǎo)性的函數(shù) 137
6.5.1 在關(guān)鍵輸入缺失時(shí)什么都不做可能造成意外 138
6.5.2 解決方案:將關(guān)鍵輸入變成必要的輸入 140
6.6 永不過時(shí)的枚舉處理 141
6.6.1 隱式處理未來的枚舉值可能造成問題 141
6.6.2 解決方案:使用全面的switch語句 143
6.6.3 注意默認(rèn)情況 144
6.6.4 注意事項(xiàng):依賴另一個(gè)項(xiàng)目的枚舉類型 146
6.7 我們不能只用測(cè)試解決所有此類問題嗎 146
6.8 小結(jié) 147
第7章 編寫難以被誤用的代碼 148
7.1 考慮不可變對(duì)象 149
7.1.1 可變類可能很容易被誤用 149
7.1.2 解決方案:只在構(gòu)建時(shí)設(shè)值 151
7.1.3 解決方案:使用不可變性設(shè)計(jì)模式 152
7.2 考慮實(shí)現(xiàn)深度不可變性 157
7.2.1 深度可變性可能導(dǎo)致誤用 157
7.2.2 解決方案:防御性復(fù)制 159
7.2.3 解決方案:使用不可變數(shù)據(jù)結(jié)構(gòu) 160
7.3 避免過于通用的類型 161
7.3.1 過于通用的類型可能被誤用 162
7.3.2 配對(duì)類型很容易被誤用 164
7.3.3 解決方案:使用專用類型 166
7.4 處理時(shí)間 167
7.4.1 用整數(shù)表示時(shí)間可能帶來問題 168
7.4.2 解決方案:使用合適的數(shù)據(jù)結(jié)構(gòu)表示時(shí)間 170
7.5 擁有單一可信數(shù)據(jù)源 172
7.5.1 第二個(gè)可信數(shù)據(jù)源可能導(dǎo)致無效狀態(tài) 172
7.5.2 解決方案:使用原始數(shù)據(jù)作為單一可信數(shù)據(jù)源 173
7.6 擁有單一可信邏輯來源 175
7.6.1 多個(gè)可信邏輯來源可能導(dǎo)致程序缺陷 175
7.6.2 解決方案:使用單一可信來源 177
7.7 小結(jié) 179
第8章 實(shí)現(xiàn)代碼模塊化 180
8.1 考慮使用依賴注入 180
8.1.1 硬編程的依賴項(xiàng)可能造成問題 181
8.1.2 解決方案:使用依賴注入 182
8.1.3 在設(shè)計(jì)代碼時(shí)考慮依賴注入 184
8.2 傾向于依賴接口 185
8.2.1 依賴于具體實(shí)現(xiàn)將限制適應(yīng)性 186
8.2.2 解決方案:盡可能依賴于接口 186
8.3 注意類的繼承 187
8.3.1 類繼承可能造成問題 188
8.3.2 解決方案:使用組合 192
8.3.3 真正的“is-a”關(guān)系該怎么辦 194
8.4 類應(yīng)該只關(guān)心自身 196
8.4.1 過于關(guān)心其他類可能造成問題 196
8.4.2 解決方案:使類僅關(guān)心自身 197
8.5 將相關(guān)聯(lián)的數(shù)據(jù)封裝在一起 198
8.5.1 未封裝的數(shù)據(jù)可能難以處理 199
8.5.2 解決方案:將相關(guān)數(shù)據(jù)組合為對(duì)象或類 200
8.6 防止在返回類型中泄露實(shí)現(xiàn)細(xì)節(jié) 201
8.6.1 在返回類型中泄露實(shí)現(xiàn)細(xì)節(jié)可能造成問題 202
8.6.2 解決方案:返回對(duì)應(yīng)于抽象層次的類型 203
8.7 防止在異常中泄露實(shí)現(xiàn)細(xì)節(jié) 204
8.7.1 在異常中泄露實(shí)現(xiàn)細(xì)節(jié)可能造成問題 204
8.7.2 解決方案:使異常適合抽象層次 206
8.8 小結(jié) 208
第9章 編寫可重用、可推廣的代碼 209
9.1 注意各種假設(shè) 209
9.1.1 代碼重用時(shí)假設(shè)將導(dǎo)致缺陷 210
9.1.2 解決方案:避免不必要的假設(shè) 210
9.1.3 解決方案:如果假設(shè)是必要的,則強(qiáng)制實(shí)施 211
9.2 注意全局狀態(tài) 213
9.2.1 全局狀態(tài)可能使重用變得不安全 215
9.2.2 解決方案:依賴注入共享狀態(tài) 217
9.3 恰當(dāng)?shù)厥褂媚J(rèn)返回值 219
9.3.1 低層次代碼中的默認(rèn)返回值可能損害可重用性 220
9.3.2 解決方案:在較高層次代碼中使用默認(rèn)值 221
9.4 保持函數(shù)參數(shù)的集中度 223
9.4.1 如果函數(shù)參數(shù)超出需要,可能難以重用 224
9.4.2 解決方案:讓函數(shù)只取得需要的參數(shù) 225
9.5 考慮使用泛型 226
9.5.1 依賴于特定類型將限制可推廣性 226
9.5.2 解決方案:使用泛型 227
9.6 小結(jié) 228
第三部分 單元測(cè)試
第 10章 單元測(cè)試原則 231
10.1 單元測(cè)試入門 232
10.2 是什么造就好的單元測(cè)試 233
10.2.1 準(zhǔn)確檢測(cè)破壞 234
10.2.2 與實(shí)現(xiàn)細(xì)節(jié)無關(guān) 235
10.2.3 充分解釋失敗 236
10.2.4 易于理解的測(cè)試代碼 237
10.2.5 便捷運(yùn)行 237
10.3 專注于公共API,但不要忽略重要的行為 238
10.4 測(cè)試替身 242
10.4.1 使用測(cè)試替身的理由 242
10.4.2 模擬對(duì)象 246
10.4.3 樁 248
10.4.4 模擬對(duì)象和樁可能有問題 250
10.4.5 偽造對(duì)象 253
10.4.6 關(guān)于模擬對(duì)象的不同學(xué)派 256
10.5 挑選測(cè)試思想 257
10.6 小結(jié) 258
第 11章 單元測(cè)試實(shí)踐 259
11.1 測(cè)試行為,而不僅僅是函數(shù) 259
11.1.1 每個(gè)函數(shù)一個(gè)測(cè)試用例往往是不夠的 260
11.1.2 解決方案:專注于測(cè)試每個(gè)行為 261
11.2 避免僅為了測(cè)試而使所有細(xì)節(jié)可見 263
11.2.1 測(cè)試私有函數(shù)往往是個(gè)壞主意 264
11.2.2 解決方案:首選通過公共API測(cè)試 265
11.2.3 解決方案:將代碼分解為較小的單元 266
11.3 一次測(cè)試一個(gè)行為 270
11.3.1 一次測(cè)試多個(gè)行為可能導(dǎo)致降低測(cè)試質(zhì)量 270
11.3.2 解決方案:以單獨(dú)的測(cè)試用例測(cè)試每個(gè)行為 272
11.3.3 參數(shù)化測(cè)試 273
11.4 恰當(dāng)?shù)厥褂霉蚕頊y(cè)試配置 274
11.4.1 共享狀態(tài)可能帶來問題 275
11.4.2 解決方案:避免共享狀態(tài)或者重置狀態(tài) 277
11.4.3 共享配置可能帶來問題 278
11.4.4 解決方案:在測(cè)試用例中定義重要配置 281
11.4.5 何時(shí)適用共享配置 283
11.5 使用合適的斷言匹配器 284
11.5.1 不合適的匹配器可能導(dǎo)致無法充分解釋失敗 284
11.5.2 解決方案:使用合適的匹配器 286
11.6 使用依賴注入來提高可測(cè)試性 287
11.6.1 硬編程的依賴項(xiàng)可能導(dǎo)致代碼無法測(cè)試 287
11.6.2 解決方案:使用依賴注入 288
11.7 關(guān)于測(cè)試的一些結(jié)論 289
11.8 小結(jié) 290
附錄A 巧克力糕餅食譜 291
附錄B 空值安全與可選類型 292
附錄C 額外的代碼示例 295