本書結(jié)合真實項目案例,從面向?qū)ο缶幊谭妒健⒃O(shè)計原則、代碼規(guī)范、重構(gòu)技巧和設(shè)計模式5個方面詳細介紹如何編寫高質(zhì)量代碼。
第1章為概述,簡單介紹了本書涉及的各個模塊,以及各個模塊之間的聯(lián)系;第2章介紹面向?qū)ο缶幊谭妒;?章介紹設(shè)計原則;第4章介紹代碼規(guī)范;第5章介紹重構(gòu)技巧;第6章介紹創(chuàng)建型設(shè)計模式;第7章介紹結(jié)構(gòu)型設(shè)計模式;第8章介紹行為型設(shè)計模式。
本書可以作為各類研發(fā)工程師的學(xué)習、進階讀物,也可以作為高等院校相關(guān)專業(yè)師生的教學(xué)和學(xué)習用書,以及計算機培訓(xùn)學(xué)校的教材。
從面向?qū)ο、設(shè)計原則、編程規(guī)范、代碼重構(gòu)鋪開,Google前工程師帶你追本溯源,幫助掌握編寫高質(zhì)量代碼的所有知識。
(1)搞懂 23 種常用的設(shè)計模式,幫你跨越知識到應(yīng)用的鴻溝
(2)200+ 真實代碼分析設(shè)計與實現(xiàn),手把手教你寫出高質(zhì)量代碼;
(3)大互聯(lián)網(wǎng)公司的編程經(jīng)驗分享,輕松應(yīng)對設(shè)計模式面試的關(guān)卡;
(4)極客時間《設(shè)計模式之美》專欄4萬+用戶、《算法之美》10+萬用戶共同選擇,經(jīng)過讀者驗證的作品。
(5)通俗易懂,每個知識點結(jié)合實戰(zhàn)代碼講解,達到學(xué)以致用的目標,如論新手或是工程師,無論求職面試或是項目開發(fā),希望本書可以給出有益的指導(dǎo)。
王爭,前Google工程師,《數(shù)據(jù)結(jié)構(gòu)與算法之美》書籍作者,熱衷技術(shù)分享,gzh:小爭哥。熱愛分享,對數(shù)據(jù)結(jié)構(gòu)、設(shè)計模式和算法有很好的的研究。
量身打造
第 1章概述 1
1.1 為什么學(xué)習代碼設(shè)計 2
1.1.1 編寫高質(zhì)量的代碼 2
1.1.2 應(yīng)對復(fù)雜代碼的開發(fā) 2
1.1.3 程序員的基本功 3
1.1.4 職場發(fā)展的技能 4
1.1.5 思考題 4
1.2 如何評價代碼質(zhì)量 4
1.2.1 可維護性(maintainability) 5
1.2.2 可讀性(readability) 6
1.2.3 可擴展性(extensibility) 6
1.2.4 靈活性(flexibility) 6
1.2.5 簡潔性(simplicity) 7
1.2.6 可復(fù)用性(reusability) 7
1.2.7 可測試性(testability) 7
1.2.8 思考題 8
1.3 如何寫出高質(zhì)量代碼 8
1.3.1 面向?qū)ο?8
1.3.2 設(shè)計原則 8
1.3.3 設(shè)計模式 9
1.3.4 代碼規(guī)范 9
1.3.5 重構(gòu)技巧 10
1.3.6 思考題 11
1.4 如何避免過度設(shè)計 11
1.4.1 代碼設(shè)計的初衷是提高代碼質(zhì)量 11
1.4.2 代碼設(shè)計的原則是“先有問題,后有方案” 12
1.4.3 代碼設(shè)計的應(yīng)用場景是復(fù)雜代碼 12
1.4.4 持續(xù)重構(gòu)可有效避免過度設(shè)計 12
1.4.5 不要脫離具體的場景談代碼設(shè)計 13
1.4.6 思考題 13
第 2章面向?qū)ο缶幊谭妒?14
2.1 當我們在談?wù)撁嫦驅(qū)ο髸r,到底在談?wù)撌裁?15
2.1.1 面向?qū)ο缶幊毯兔嫦驅(qū)ο缶幊陶Z言 15
2.1.2 非嚴格定義的面向?qū)ο缶幊陶Z言 16
2.1.3 面向?qū)ο蠓治龊兔嫦驅(qū)ο笤O(shè)計 16
2.1.4 關(guān)于UML的說明 17
2.1.5 思考題 17
2.2 封裝、抽象、繼承和多態(tài)為何而生 17
2.2.1 封裝(encapsulation) 18
2.2.2 抽象(abstraction) 20
2.2.3 繼承(inheritance) 21
2.2.4 多態(tài)(polymorphism) 22
2.2.5 思考題 25
2.3 如何進行面向?qū)ο蠓治觥⒚嫦驅(qū)ο笤O(shè)計和面向?qū)ο缶幊?25
2.3.1 案例介紹和難點剖析 25
2.3.2 如何進行面向?qū)ο蠓治?26
2.3.3 如何進行面向?qū)ο笤O(shè)計 28
2.3.4 如何進行面向?qū)ο缶幊?34
2.3.5 思考題 35
2.4 面向?qū)ο缶幊膛c面向過程編程和函數(shù)式編程之間的區(qū)別 35
2.4.1 面向過程編程 36
2.4.2 面向?qū)ο缶幊毯兔嫦蜻^程編程的對比 38
2.4.3 函數(shù)式編程 40
2.4.4 面向?qū)ο缶幊毯秃瘮?shù)式編程的對比 44
2.4.5 思考題 44
2.5 哪些代碼看似面向?qū)ο缶幊田L格,實則面向過程編程風格 45
2.5.1 濫用getter、setter方法 45
2.5.2 濫用全局變量和全局方法 47
2.5.3 定義數(shù)據(jù)和方法分離的類 49
2.5.4 思考題 50
2.6 基于“貧血”模型的傳統(tǒng)開發(fā)模式是否違背OOP 51
2.6.1 基于“貧血”模型的傳統(tǒng)開發(fā)模式 51
2.6.2 基于“充血”模型的DDD開發(fā)模式 52
2.6.3 兩種開發(fā)模式的應(yīng)用對比 53
2.6.4 基于“貧血”模型的傳統(tǒng)開發(fā)模式被廣泛應(yīng)用的原因 57
2.6.5 基于“充血”模型的DDD開發(fā)模式的應(yīng)用場景 58
2.6.6 思考題 59
2.7 接口和抽象類:如何使用普通類模擬接口和抽象類 59
2.7.1 抽象類和接口的定義與區(qū)別 59
2.7.2 抽象類和接口存在的意義 62
2.7.3 模擬實現(xiàn)抽象類和接口 64
2.7.4 抽象類和接口的應(yīng)用場景 65
2.7.5 思考題 65
2.8 基于接口而非實現(xiàn)編程:有沒有必要為每個類都定義接口 65
2.8.1 接口的多種理解方式 66
2.8.2 設(shè)計思想實戰(zhàn)應(yīng)用 66
2.8.3 避免濫用接口 69
2.8.4 思考題 69
2.9 組合優(yōu)于繼承:什么情況下可以使用繼承 70
2.9.1 為什么不推薦使用繼承 70
2.9.2 相比繼承,組合有哪些優(yōu)勢 72
2.9.3 如何決定是使用組合還是使用繼承 73
2.9.4 思考題 74
第3章設(shè)計原則 75
3.1 單一職責原則:如何判定某個類的職責是否單一 76
3.1.1 單一職責原則的定義和解讀 76
3.1.2 如何判斷類的職責是否單一 76
3.1.3 類的職責是否越細化越好 78
3.1.4 思考題 79
3.2 開閉原則:只要修改代碼,就一定違反開閉原則嗎 79
3.2.1 如何理解“對擴展開放、對修改關(guān)閉” 80
3.2.2 修改代碼就意味著違反開閉原則嗎 83
3.2.3 如何做到“對擴展開放、對修改關(guān)閉” 84
3.2.4 如何在項目中靈活應(yīng)用開閉原則 85
3.2.5 思考題 86
3.3 里氏替換原則:什么樣的代碼才算違反里氏替換原則 86
3.3.1 里氏替換原則的定義 86
3.3.2 里氏替換原則與多態(tài)的區(qū)別 88
3.3.3 違反里氏替換原則的反模式 89
3.3.4 思考題 89
3.4 接口隔離原則:如何理解該原則中的“接口” 89
3.4.1 把“接口”理解為一組API或函數(shù) 90
3.4.2 把“接口”理解為單個API或函數(shù) 91
3.4.3 把“接口”理解為OOP中的接口概念 92
3.4.4 思考題 96
3.5 依賴反轉(zhuǎn)原則:依賴反轉(zhuǎn)與控制反轉(zhuǎn)、依賴注入有何關(guān)系 97
3.5.1 控制反轉(zhuǎn)(IoC) 97
3.5.2 依賴注入(DI) 98
3.5.3 依賴注入框架(DI Framework) 99
3.5.4 依賴反轉(zhuǎn)原則(DIP) 100
3.5.5 思考題 100
3.6 KISS原則和YAGNI原則:二者是一回事嗎 100
3.6.1 KISS原則的定義和解讀 101
3.6.2 代碼并非行數(shù)越少越簡單 101
3.6.3 代碼復(fù)雜不一定違反KISS原則 103
3.6.4 如何寫出滿足KISS原則的代碼 104
3.6.5 YAGNI原則和KISS原則的區(qū)別 104
3.6.6 思考題 104
3.7 DRY原則:相同的兩段代碼就一定違反DRY原則嗎 104
3.7.1 代碼邏輯重復(fù) 105
3.7.2 功能(語義)重復(fù) 106
3.7.3 代碼執(zhí)行重復(fù) 107
3.7.4 代碼的復(fù)用性 109
3.7.5 思考題 110
3.8 LoD:如何實現(xiàn)代碼的“高內(nèi)聚、低耦合” 110
3.8.1 何為“高內(nèi)聚、低耦合” 110
3.8.2 LoD的定義描述 111
3.8.3 定義解讀與代碼示例一 112
3.8.4 定義解讀與代碼示例二 114
3.8.5 思考題 116
第4章代碼規(guī)范 117
4.1 命名與注釋:如何精準命名和編寫注釋 118
4.1.1 長命名和短命名哪個更好 118
4.1.2 利用上下文信息簡化命名 118
4.1.3 利用業(yè)務(wù)詞匯表統(tǒng)一命名 118
4.1.4 命名既要精準又要抽象 119
4.1.5 注釋應(yīng)該包含哪些內(nèi)容 119
4.1.6 注釋并非越多越好 120
4.1.7 思考題 120
4.2 代碼風格:與其爭論標準,不如團隊統(tǒng)一 121
4.2.1 類、函數(shù)多大才合適 121
4.2.2 一行代碼多長才合適 121
4.2.3 善用空行分割代碼塊 121
4.2.4 是四格縮進還是兩格縮進 122
4.2.5 左大括號是否要另起一行 122
4.2.6 類中成員的排列順序 122
4.2.7 思考題 123
4.3 編程技巧:小技巧,大作用,一招提高代碼的可讀性 123
4.3.1 將復(fù)雜的代碼模塊化 123
4.3.2 避免函數(shù)的參數(shù)過多 124
4.3.3 移除函數(shù)中的flag參數(shù) 125
4.3.4 移除嵌套過深的代碼 126
4.3.5 學(xué)會使用解釋性變量 128
4.3.6 思考題 129
第5章重構(gòu)技巧 130
5.1 重構(gòu)四要素:目的、對象、時機和方法 131
5.1.1 重構(gòu)的目的:為什么重構(gòu)(why) 131
5.1.2 重構(gòu)的對象:到底重構(gòu)什么(what) 131
5.1.3 重構(gòu)的時機:什么時候重構(gòu)(when) 132
5.1.4 重構(gòu)的方法:應(yīng)該如何重構(gòu)(how) 132
5.1.5 思考題 133
5.2 單元測試:保證重構(gòu)不出錯的有效手段 133
5.2.1 什么是單元測試 133
5.2.2 為什么要編寫單元測試代碼 135
5.2.3 如何設(shè)計單元測試 136
5.2.4 為什么單元測試落地困難 138
5.2.5 思考題 139
5.3 代碼的可測試性:如何編寫可測試代碼 139
5.3.1 編寫可測試代碼的方法 139
5.3.2 常見不可測試代碼示例 146
5.3.3 思考題 147
5.4 解耦:哪些方法可以用來解耦代碼 147
5.4.1 為何解耦如此重要 147
5.4.2 如何判斷代碼是否需要解耦 148
5.4.3 如何給代碼解耦 148
5.4.4 思考題 150
5.5 重構(gòu)案例:將ID生成器代碼從“能用”重構(gòu)為“好用” 150
5.5.1 ID生成器需求背景 150
5.5.2 “湊合能用”的代碼實現(xiàn) 151
5.5.3 如何發(fā)現(xiàn)代碼的質(zhì)量問題 152
5.5.4 第 一輪重構(gòu):提高代碼的可讀性 153
5.5.5 第二輪重構(gòu):提高代碼的可測試性 155
5.5.6 第三輪重構(gòu):編寫單元測試代碼 156
5.5.7 第四輪重構(gòu):重構(gòu)異常處理邏輯 158
5.5.8 思考題 165
第6章創(chuàng)建型設(shè)計模式 166
6.1 單例模式(上):為什么不推薦在項目中使用單例模式 167
6.1.1 單例模式的定義 167
6.1.2 單例模式的實現(xiàn)方法 167
6.1.3 單例模式的應(yīng)用:日志寫入 170
6.1.4 單例模式的弊端 173
6.1.5 單例模式的替代方案 175
6.1.6 思考題 176
6.2 單例模式(下):如何設(shè)計實現(xiàn)一個分布式單例模式 177
6.2.1 單例模式的性 177
6.2.2 線程的單例模式 177
6.2.3 集群環(huán)境下的單例模式 178
6.2.4 多例模式 179
6.2.5 思考題 180
6.3 工廠模式(上):如何解耦復(fù)雜對象的創(chuàng)建和使用 180
6.3.1 簡單工廠模式(Simple Factory Pattern) 181
6.3.2 工廠方法模式(Factory Method Pattern) 183
6.3.3 抽象工廠模式(Abstract Factory Pattern) 186
6.3.4 工廠模式的應(yīng)用場景總結(jié) 187
6.3.5 思考題 187
6.4 工廠模式(下):如何設(shè)計實現(xiàn)一個依賴注入容器 188
6.4.1 DI容器與工廠模式的區(qū)別 188
6.4.2 DI容器的核心功能 188
6.4.3 DI容器的設(shè)計與實現(xiàn) 190
6.4.4 思考題 194
6.5 建造者模式:什么情況下必須用建造者模式創(chuàng)建對象 194
6.5.1 使用構(gòu)造函數(shù)創(chuàng)建對象 194
6.5.2 使用setter方法為成員變量賦值 195
6.5.3 使用建造者模式做參數(shù)校驗 196
6.5.4 建造者模式在Guava中的應(yīng)用 198
6.5.5 建造者模式與工廠模式的區(qū)別 200
6.5.6 思考題 200
6.6 原型模式:如何快速復(fù)制(clone)一個哈希表 200
6.6.1 原型模式的定義 200
6.6.2 原型模式的應(yīng)用舉例 201
6.6.3 原型模式的實現(xiàn)方式:深拷貝和淺拷貝 203
6.6.4 思考題 206
第7章結(jié)構(gòu)型設(shè)計模式 208
7.1 代理模式:代理模式在RPC、緩存和監(jiān)控等場景中的應(yīng)用 209
7.1.1 基于接口實現(xiàn)代理模式 209
7.1.2 基于繼承實現(xiàn)代理模式 211
7.1.3 基于反射實現(xiàn)動態(tài)代理 211
7.1.4 代理模式的各種應(yīng)用場景 212
7.1.5 思考題 213
7.2 裝飾器模式:剖析Java IO類庫的底層設(shè)計思想 213
7.2.1 Java IO類庫的“奇怪”用法 213
7.2.2 基于繼承的設(shè)計方案 215
7.2.3 基于裝飾器模式的設(shè)計方案 215
7.2.4 思考題 219
7.3 適配器模式:如何利用適配器模式解決代碼的不兼容問題 219
7.3.1 類適配器和對象適配器 219
7.3.2 適配器模式的5種應(yīng)用場景 221
7.3.3 適配器模式在Java日志中的應(yīng)用 224
7.3.4 Wrapper設(shè)計模式 226
7.3.5 思考題 230
7.4 橋接模式:如何將M×N的繼承關(guān)系簡化為M+N的組合關(guān)系 230
7.4.1 橋接模式的定義 230
7.4.2 橋接模式解決繼承“爆炸”問題 230
7.4.3 思考題 231
7.5 門面模式:如何設(shè)計接口以兼顧接口的易用性和通用性 231
7.5.1 門面模式和接口設(shè)計 231
7.5.2 利用門面模式提高接口易用性 232
7.5.3 利用門面模式提高接口性能 232
7.5.4 利用門面模式解決事務(wù)問題 232
7.5.5 思考題 233
7.6 組合模式:一種應(yīng)用在樹形結(jié)構(gòu)上的特殊設(shè)計模式 233
7.6.1 組合模式的應(yīng)用一:目錄樹 233
7.6.2 組合模式的應(yīng)用二:人力樹 237
7.6.3 思考題 239
7.7 享元模式:如何利用享元模式降低系統(tǒng)的內(nèi)存開銷 239
7.7.1 享元模式在棋牌游戲中的應(yīng)用 239
7.7.2 享元模式在文本編輯器中的應(yīng)用 242
7.7.3 享元模式在Java Integer中的應(yīng)用 244
7.7.4 享元模式在Java String中的應(yīng)用 247
7.7.5 享元模式與單例模式、緩存、對象池的區(qū)別 248
7.7.6 思考題 248
第8章行為型設(shè)計模式 249
8.1 觀察者模式:如何實現(xiàn)一個異步非阻塞的EventBus框架 250
8.1.1 觀察者模式的定義 250
8.1.2 觀察者模式的代碼實現(xiàn) 250
8.1.3 觀察者模式存在的意義 251
8.1.4 觀察者模式的應(yīng)用場景 253
8.1.5 異步非阻塞觀察者模式 254
8.1.6 EventBus框架功能介紹 255
8.1.7 從零開始實現(xiàn)EventBus框架 257
8.1.8 思考題 261
8.2 模板方法模式(上):模板方法模式在JDK、Servlet、JUnit中的應(yīng)用 261
8.2.1 模板方法模式的定義與實現(xiàn) 261
8.2.2 模板方法模式的作用一:復(fù)用 262
8.2.3 模板方法模式的作用二:擴展 264
8.2.4 思考題 266
8.3 模板方法模式(下):模板方法模式與回調(diào)有何區(qū)別和聯(lián)系 267
8.3.1 回調(diào)的原理與實現(xiàn) 267
8.3.2 應(yīng)用示例一:JdbcTemplate 268
8.3.3 應(yīng)用示例二:setClickListener() 270
8.3.4 應(yīng)用示例三:addShutdownHook() 271
8.3.5 模板方法模式與回調(diào)的區(qū)別 272
8.3.6 思考題 273
8.4 策略模式:如何避免冗長的if-else和switch-case語句 273
8.4.1 策略模式的定義與實現(xiàn) 273
8.4.2 利用策略模式替代分支判斷 275
8.4.3 策略模式的應(yīng)用舉例:對文件中的內(nèi)容進行排序 277
8.4.4 避免策略模式誤用 281
8.4.5 思考題 281
8.5 職責鏈模式:框架中的過濾器、攔截器和插件是如何實現(xiàn)的 282
8.5.1 職責鏈模式的定義和實現(xiàn) 282
8.5.2 職責鏈模式在敏感詞過濾中的應(yīng)用 286
8.5.3 職責鏈模式在Servlet Filter中的應(yīng)用 288
8.5.4 職責鏈模式在Spring Interceptor中的應(yīng)用 290
8.5.5 職責鏈模式在MyBatis Plugin中的應(yīng)用 293
8.5.6 思考題 297
8.6 狀態(tài)模式:游戲和工作流引擎中常用的狀態(tài)機是如何實現(xiàn)的 297
8.6.1 什么是有限狀態(tài)機 298
8.6.2 狀態(tài)機實現(xiàn)方式一:分支判斷法 300
8.6.3 狀態(tài)機實現(xiàn)方式二:查表法 301
8.6.4 狀態(tài)機實現(xiàn)方式三:狀態(tài)模式 303
8.6.5 思考題 306
8.7 迭代器模式(上):為什么要用迭代器遍歷集合 306
8.7.1 迭代器模式的定義和實現(xiàn) 307
8.7.2 遍歷集合的3種方法 309
8.7.3 迭代器遍歷集合的問題 310
8.7.4 迭代器遍歷集合的問題的解決方案 311
8.7.5 思考題 315
8.8 迭代器模式(下):如何實現(xiàn)一個支持快照功能的迭代器 315
8.8.1 支持快照功能的迭代器 315
8.8.2 設(shè)計思路一:基于多副本 316
8.8.3 設(shè)計思路二:基于時間戳 317
8.8.4 思考題 319
8.9 訪問者模式:為什么支持雙分派的編程語言不需要訪問者模式 320
8.9.1 “發(fā)明”訪問者模式 320
8.9.2 雙分派(Double Dispatch) 328
8.9.3 思考題 330
8.10 備忘錄模式:如何優(yōu)雅地實現(xiàn)數(shù)據(jù)防丟失、撤銷和恢復(fù)功能 330
8.10.1 備忘錄模式的定義與實現(xiàn) 331
8.10.2 優(yōu)化備忘錄模式的時間和空間開銷 333
8.10.3 思考題 334
8.11 命令模式:如何設(shè)計實現(xiàn)基于命令模式的手游服務(wù)器 334
8.11.1 命令模式的定義 334
8.11.2 命令模式的應(yīng)用:手游服務(wù)器 335
8.11.3 命令模式與策略模式的區(qū)別 336
8.11.4 思考題 337
8.12 解釋器模式:如何設(shè)計實現(xiàn)一個自定義接口告警規(guī)則的功能 337
8.12.1 解釋器模式的定義 337
8.12.2 解釋器模式的應(yīng)用:表達式計算 337
8.12.3 解釋器模式的應(yīng)用:規(guī)則引擎 340
8.12.4 思考題 343
8.13 中介模式:什么時候使用中介模式?什么時候使用觀察者模式? 343
8.13.1 中介模式的定義和實現(xiàn) 343
8.13.2 中介模式與觀察者模式的區(qū)別 344
8.13.3 思考題 344