本書是設(shè)計模式領(lǐng)域公認(rèn)的3本經(jīng)典著作之一,“極具趣味,容易理解,但講解又極為嚴(yán)謹(jǐn)和透徹”是本書的寫作風(fēng)格和方法的最大特點。第1版2010年出版,暢銷至今,廣受好評,是該領(lǐng)域的里程碑著作。深刻解讀6大設(shè)計原則和28種設(shè)計模式的準(zhǔn)確定義、應(yīng)用方法和最佳實踐,全方位比較各種同類模式之間的異同,詳細講解將不同的模式組合使用的方法。第2版在第1版的基礎(chǔ)上有兩方面的改進,一方面結(jié)合讀者的意見和建議對原有內(nèi)容中的瑕疵進行了修正和完善,另一方面增加了4種新的設(shè)計模式,希望這一版能為廣大程序員們奉上一場更加完美的設(shè)計模式盛宴!
全書共38章,分為五部分:第一部分(第1~6章),以一種全新的視角對面向?qū)ο蟪绦蛟O(shè)計的6大原則進行了深刻解讀,旨在讓讀者能更深刻且準(zhǔn)確地理解這些原則,為后面的學(xué)習(xí)打下基礎(chǔ);第二部分(第7~29章)通過大量生動的案例講解和分析了23種最常用的設(shè)計模式,并進行了擴展講解,通俗易懂,趣味性極強而又緊扣模式的核心;第三部分(第30~33章)對同類型和相關(guān)聯(lián)的模式進行了深入分析和比較,旨在闡明各種設(shè)計模式之間的差別以及它們的理想應(yīng)用場景;第四部分(第34~36章)探討了如何在實際開發(fā)中將各種設(shè)計模式混合起來使用,以發(fā)揮設(shè)計模式的最大效用;第五部分(第37~38章)是本書的擴展篇,首先從實現(xiàn)的角度對MVC框架的原理進行了深入分析,然后講解了5種新的設(shè)計模式的原理、意圖和最佳實踐。本書最后附有一份精美的設(shè)計模式彩圖,可以裁剪,便于參考。
(1) 暢銷書全新升級,第1版廣受好評,被譽為設(shè)計模式領(lǐng)域最具趣味、最易理解且又講解極為透徹的一本書,程序員公認(rèn)的3本經(jīng)典設(shè)計模式著作之一(2) 深刻解讀6大設(shè)計原則和28種設(shè)計模式的準(zhǔn)確定義、應(yīng)用方法和最佳實踐,全方位比較各種同類模式之間的異同,詳細講解將不同的模式組合使用的方法
秦小波 資深軟件開發(fā)工程師、系統(tǒng)分析師和架構(gòu)師(獲Sun架構(gòu)師認(rèn)證),從事軟件開發(fā)工作10余年,實踐經(jīng)驗極其豐富。精通設(shè)計模式,對設(shè)計模式有深刻的認(rèn)識和獨到見解,經(jīng)過長期大量的實踐和總結(jié),創(chuàng)造性地提出新的設(shè)計模式。資深Java技術(shù)專家,精通Spring、Struts 2、Hibernate、iBatis、jBPM等Java技術(shù),在企業(yè)級Java應(yīng)用領(lǐng)域積累了大量經(jīng)驗,對基于ESB、BPEL的服務(wù)集成技術(shù)也有深入的認(rèn)識。此外,還是一位優(yōu)秀的DBA,具有IBM DB2 DBA資格認(rèn)證,對海量數(shù)據(jù)處理有深入的研究。著有暢銷書《編寫高質(zhì)量代碼:改善Java程序的151個建議》,廣受讀者好評!
"前 言
第一部分 大旗不揮,誰敢沖
鋒—6大設(shè)計原則全新解讀
第1章 單一職責(zé)原則 2
1.1 我是“!鳖,我可以擔(dān)任多職嗎 2
1.2 絕殺技,打破你的傳統(tǒng)思維 3
1.3 我單純,所以我快樂 6
1.4 最佳實踐 7
第2章 里氏替換原則 8
2.1 愛恨糾葛的父子關(guān)系 8
2.2 糾紛不斷,規(guī)則壓制 9
2.3 最佳實踐 18
第3章 依賴倒置原則 19
3.1 依賴倒置原則的定義 19
3.2 言而無信,你太需要契約 20 "前 言
第一部分 大旗不揮,誰敢沖
鋒—6大設(shè)計原則全新解讀
第1章 單一職責(zé)原則 2
1.1 我是“!鳖悾铱梢該(dān)任多職嗎 2
1.2 絕殺技,打破你的傳統(tǒng)思維 3
1.3 我單純,所以我快樂 6
1.4 最佳實踐 7
第2章 里氏替換原則 8
2.1 愛恨糾葛的父子關(guān)系 8
2.2 糾紛不斷,規(guī)則壓制 9
2.3 最佳實踐 18
第3章 依賴倒置原則 19
3.1 依賴倒置原則的定義 19
3.2 言而無信,你太需要契約 20
3.3 依賴的三種寫法 25
3.4 最佳實踐 26
第4章 接口隔離原則 28
4.1 接口隔離原則的定義 28
4.2 美女何其多,觀點各不同 29
4.3 保證接口的純潔性 33
4.4 最佳實踐 35
第5章 迪米特法則 36
5.1 迪米特法則的定義 36
5.2 我的知識你知道得越少越好 36
5.3 最佳實踐 43
第6章 開閉原則 44
6.1 開閉原則的定義 44
6.2 開閉原則的廬山真面目 44
6.3 為什么要采用開閉原則 49
6.4 如何使用開閉原則 51
6.5 最佳實踐 55
第二部分 真刀實槍—23種設(shè)計模式完美演繹
第7章 單例模式 58
7.1 我是皇帝我獨苗 58
7.2 單例模式的定義 59
7.3 單例模式的應(yīng)用 60
7.3.1 單例模式的優(yōu)點 60
7.3.2 單例模式的缺點 60
7.3.3 單例模式的使用場景 61
7.3.4 單例模式的注意事項 61
7.4 單例模式的擴展 62
7.5 最佳實踐 64
第8章 工廠方法模式 65
8.1 女媧造人的故事 65
8.2 工廠方法模式的定義 69
8.3 工廠方法模式的應(yīng)用 70
8.3.1 工廠方法模式的優(yōu)點 70
8.3.2 工廠方法模式的使用場景 71
8.4 工廠方法模式的擴展 71
8.5 最佳實踐 77
第9章 抽象工廠模式 78
9.1 女媧的失誤 78
9.2 抽象工廠模式的定義 83
9.3 抽象工廠模式的應(yīng)用 86
9.3.1 抽象工廠模式的優(yōu)點 86
9.3.2 抽象工廠模式的缺點 86
9.3.3 抽象工廠模式的使用場景 86
9.3.4 抽象工廠模式的注意事項 86
9.4 最佳實踐 87
第10章 模板方法模式 88
10.1 輝煌工程—制造悍馬 88
10.2 模板方法模式的定義 93
10.3 模板方法模式的應(yīng)用 94
10.3.1 模板方法模式的優(yōu)點 94
10.3.2 模板方法模式的缺點 95
10.3.3 模板方法模式的使用場景 95
10.4 模板方法模式的擴展 95
10.5 最佳實踐 99
第11章 建造者模式 100
11.1 變化是永恒的 100
11.2 建造者模式的定義 109
11.3 建造者模式的應(yīng)用 111
11.3.1 建造者模式的優(yōu)點 111
11.3.2 建造者模式的使用場景 111
11.3.3 建造者模式的注意事項 111
11.4 建造者模式的擴展 111
11.5 最佳實踐 112
第12章 代理模式 113
12.1 我是游戲至尊 113
12.2 代理模式的定義 116
12.3 代理模式的應(yīng)用 118
12.3.1 代理模式的優(yōu)點 118
12.3.2 代理模式的使用場景 119
12.4 代理模式的擴展 119
12.4.1 普通代理 119
12.4.2 強制代理 121
12.4.3 代理是有個性的 126
12.4.4 動態(tài)代理 128
12.5 最佳實踐 134
第13章 原型模式 135
13.1 個性化電子賬單 135
13.2 原型模式的定義 141
13.3 原型模式的應(yīng)用 142
13.3.1 原型模式的優(yōu)點 142
13.3.2 原型模式的使用場景 142
13.4 原型模式的注意事項 143
13.4.1 構(gòu)造函數(shù)不會被執(zhí)行 143
13.4.2 淺拷貝和深拷貝 144
13.4.3 clone與final兩個冤家 146
13.5 最佳實踐 146
第14章 中介者模式 147
14.1 進銷存管理是這個樣子的嗎 147
14.2 中介者模式的定義 156
14.3 中介者模式的應(yīng)用 159
14.3.1 中介者模式的優(yōu)點 159
14.3.2 中介者模式的缺點 159
14.3.3 中介者模式的使用場景 159
14.4 中介者模式的實際應(yīng)用 160
14.5 最佳實踐 161
第15章 命令模式 162
15.1 項目經(jīng)理也難當(dāng) 162
15.2 命令模式的定義 170
15.3 命令模式的應(yīng)用 173
15.3.1 命令模式的優(yōu)點 173
15.3.2 命令模式的缺點 173
15.3.3 命令模式的使用場景 173
15.4 命令模式的擴展 173
15.4.1 未講完的故事 173
15.4.2 反悔問題 174
15.5 最佳實踐 175
第16章 責(zé)任鏈模式 178
16.1 古代婦女的枷鎖—“三從四德” 178
16.2 責(zé)任鏈模式的定義 186
16.3 責(zé)任鏈模式的應(yīng)用 189
16.3.1 責(zé)任鏈模式的優(yōu)點 189
16.3.2 責(zé)任鏈模式的缺點 190
16.3.3 責(zé)任鏈模式的注意事項 190
16.4 最佳實踐 190
第17章 裝飾模式 192
17.1 罪惡的成績單 192
17.2 裝飾模式的定義 198
17.3 裝飾模式應(yīng)用 201
17.3.1 裝飾模式的優(yōu)點 201
17.3.2 裝飾模式的缺點 201
17.3.3 裝飾模式的使用場景 201
17.4 最佳實踐 201
第18章 策略模式 203
18.1 劉備江東娶妻,趙云他容易嗎 203
18.2 策略模式的定義 206
18.3 策略模式的應(yīng)用 208
18.3.1 策略模式的優(yōu)點 208
18.3.2 策略模式的缺點 208
18.3.3 策略模式的使用場景 209
18.3.4 策略模式的注意事項 209
18.4 策略模式的擴展 209
18.5 最佳實踐 214
第19章 適配器模式 215
19.1 業(yè)務(wù)發(fā)展—上帝才能控制 215
19.2 適配器模式的定義 221
19.3 適配器模式的應(yīng)用 223
19.3.1 適配器模式的優(yōu)點 223
19.3.2 適配器模式的使用場景 224
19.3.3 適配器模式的注意事項 224
19.4 適配器模式的擴展 224
19.5 最佳實踐 229
第20章 迭代器模式 230
20.1 整理項目信息—苦差事 230
20.2 迭代器模式的定義 236
20.3 迭代器模式的應(yīng)用 239
20.4 最佳實踐 239
第21章 組合模式 240
21.1 公司的人事架構(gòu)是這樣的嗎 240
21.2 組合模式的定義 253
21.3 組合模式的應(yīng)用 255
21.3.1 組合模式的優(yōu)點 255
21.3.2 組合模式的缺點 256
21.3.3 組合模式的使用場景 256
21.3.4 組合模式的注意事項 256
21.4 組合模式的擴展 256
21.4.1 真實的組合模式 256
21.4.2 透明的組合模式 257
21.4.3 組合模式的遍歷 259
21.5 最佳實踐 260
第22章 觀察者模式 262
22.1 韓非子身邊的臥底是誰派來的 262
22.2 觀察者模式的定義 271
22.3 觀察者模式的應(yīng)用 273
22.3.1 觀察者模式的優(yōu)點 273
22.3.2 觀察者模式的缺點 274
22.3.3 觀察者模式的使用場景 274
22.3.4 觀察者模式的注意事項 274
22.4 觀察者模式的擴展 275
22.4.1 Java世界中的觀察者模式 275
22.4.2 項目中真實的觀察者模式 276
22.4.3 訂閱發(fā)布模型 277
22.5 最佳實踐 277
第23章 門面模式 278
23.1 我要投遞信件 278
23.2 門面模式的定義 283
23.3 門面模式的應(yīng)用 284
23.3.1 門面模式的優(yōu)點 284
23.3.2 門面模式的缺點 285
23.3.3 門面模式的使用場景 285
23.4 門面模式的注意事項 285
23.4.1 一個子系統(tǒng)可以有多個門面 285
23.4.2 門面不參與子系統(tǒng)內(nèi)的業(yè)務(wù)邏輯 286
23.5 最佳實踐 288
第24章 備忘錄模式 289
24.1 如此追女孩子,你還不樂 289
24.2 備忘錄模式的定義 294
24.3 備忘錄模式的應(yīng)用 297
24.3.1 備忘錄模式的使用場景 297
24.3.2 備忘錄模式的注意事項 297
24.4 備忘錄模式的擴展 297
24.4.1 clone方式的備忘錄 297
24.4.2 多狀態(tài)的備忘錄模式 300
24.4.3 多備份的備忘錄 304
24.4.4 封裝得更好一點 305
24.5 最佳實踐 307
第25章 訪問者模式 308
25.1 員工的隱私何在 308
25.2 訪問者模式的定義 316
25.3 訪問者模式的應(yīng)用 320
25.3.1 訪問者模式的優(yōu)點 320
25.3.2 訪問者模式的缺點 320
25.3.3 訪問者模式的使用場景 320
25.4 訪問者模式的擴展 321
25.4.1 統(tǒng)計功能 321
25.4.2 多個訪問者 323
25.4.3 雙分派 326
25.5 最佳實踐 328
第26章 狀態(tài)模式 329
26.1 城市的縱向發(fā)展功臣—電梯 329
26.2 狀態(tài)模式的定義 341
26.3 狀態(tài)模式的應(yīng)用 343
26.3.1 狀態(tài)模式的優(yōu)點 343
26.3.2 狀態(tài)模式的缺點 344
26.3.3 狀態(tài)模式的使用場景 344
26.3.4 狀態(tài)模式的注意事項 344
26.4 最佳實踐 344
第27章 解釋器模式 346
27.1 四則運算你會嗎 346
27.2 解釋器模式的定義 352
27.3 解釋器模式的應(yīng)用 354
27.3.1 解釋器模式的優(yōu)點 354
27.3.2 解釋器模式的缺點 354
27.3.3 解釋器模式使用的場景 355
27.3.4 解釋器模式的注意事項 355
27.4 最佳實踐 355
第28章 享元模式 356
28.1 內(nèi)存溢出,司空見慣 356
28.2 享元模式的定義 361
28.3 享元模式的應(yīng)用 364
28.3.1 享元模式的優(yōu)點和缺點 364
28.3.2 享元模式的使用場景 364
28.4 享元模式的擴展 365
28.4.1 線程安全的問題 365
28.4.2 性能平衡 366
28.5 最佳實踐 369
第29章 橋梁模式 371
29.1 我有一個夢想 371
29.2 橋梁模式的定義 379
29.3 橋梁模式的應(yīng)用 381
29.3.1 橋梁模式的優(yōu)點 381
29.3.2 橋梁模式的使用場景 382
29.3.3 橋梁模式的注意事項 382
29.4 最佳實踐 382
第三部分 誰的地盤誰做主—設(shè)計模式PK
第30章 創(chuàng)建類模式大PK 384
30.1 工廠方法模式VS建造者模式 384
30.1.1 按工廠方法建造超人 384
30.1.2 按建造者模式建造超人 386
30.1.3 最佳實踐 389
30.2 抽象工廠模式VS建造者模式 390
30.2.1 按抽象工廠模式生產(chǎn)車輛 390
30.2.2 按建造者模式生產(chǎn)車輛 394
30.2.3 最佳實踐 399
第31章 結(jié)構(gòu)類模式大PK 400
31.1 代理模式VS裝飾模式 400
31.1.1 代理模式 400
31.1.2 裝飾模式 402
31.1.3 最佳實踐 403
31.2 裝飾模式VS適配器模式 404
31.2.1 用裝飾模式描述丑小鴨 404
31.2.2 用適配器模式實現(xiàn)丑小鴨 407
31.2.3 最佳實踐 410
第32章 行為類模式大PK 411
32.1 命令模式VS策略模式 411
32.1.1 策略模式實現(xiàn)壓縮算法 411
32.1.2 命令模式實現(xiàn)壓縮算法 414
32.1.3 小結(jié) 419
32.2 策略模式VS狀態(tài)模式 420
32.2.1 策略模式實現(xiàn)人生 420
32.2.2 狀態(tài)模式實現(xiàn)人生 423
32.2.3 小結(jié) 425
32.3 觀察者模式VS責(zé)任鏈模式 426
32.3.1 責(zé)任鏈模式實現(xiàn)DNS
解析過程 427
32.3.2 觸發(fā)鏈模式實現(xiàn)DNS
解析過程 432
32.3.3 小結(jié) 437
第33章 跨戰(zhàn)區(qū)PK 438
33.1 策略模式VS橋梁模式 438
33.1.1 策略模式實現(xiàn)郵件發(fā)送 439
33.1.2 橋梁模式實現(xiàn)郵件發(fā)送 442
33.1.3 最佳實踐 445
33.2 門面模式VS中介者模式 446
33.2.1 中介者模式實現(xiàn)工資計算 446
33.2.2 門面模式實現(xiàn)工資計算 451
33.2.3 最佳實踐 454
33.3 包裝模式群PK 455
33.3.1 代理模式 455
33.3.2 裝飾模式 457
33.3.3 適配器模式 459
33.3.4 橋梁模式 461
33.3.5 最佳實踐 464
第四部分 完美世界—設(shè)計模式混編
第34章 命令模式+責(zé)任鏈模式 466
34.1 搬移UNIX的命令 466
34.2 混編小結(jié) 481
第35章 工廠方法模式+策略模式 483
35.1 迷你版的交易系統(tǒng) 483
35.2 混編小結(jié) 493
第36章 觀察者模式+中介者模式 495
36.1 事件觸發(fā)器的開發(fā) 495
36.2 混編小結(jié) 508
第五部分 擴展篇<
" 大旗不揮,誰敢沖鋒
—6大設(shè)計原則全新解讀
第1章 單一職責(zé)原則
第2章 里氏替換原則
第3章 依賴倒置原則
第4章 接口隔離原則
第5章 迪米特法則
第6章 開閉原則
單一職責(zé)原則
1.1 我是“牛”類,我可以擔(dān)任多職嗎
單一職責(zé)原則的英文名稱是Single Responsibility Principle,簡稱是SRP。這個設(shè)計原則備受爭議,只要你想和別人爭執(zhí)、慪氣或者是吵架,這個原則是屢試不爽的。如果你是老大,看到一個接口或類是這樣或那樣設(shè)計的,你就問一句:“你設(shè)計的類符合SRP原則嗎?”保準(zhǔn)對方立馬“萎縮”掉,而且還一臉崇拜地看著你,心想:“老大確實英明”。這個原則存在爭議之處在哪里呢?就是對職責(zé)的定義,什么是類的職責(zé),以及怎么劃分類的職責(zé)。我們先舉個例子來說明什么是單一職責(zé)原則。
只要做過項目,肯定要接觸到用戶、機構(gòu)、角色管理這些模塊,基本上使用的都是RBAC模型(Role-Based Access Control,基于角色的訪問控制,通過分配和取消角色來完成用戶權(quán)限的授予和取消,使動作主體(用戶)與資源的行為(權(quán)限)分離),確實是一個很好的解決辦法。我們這里要講的是用戶管理、修改用戶的信息、增加機構(gòu)(一個人屬于多個機構(gòu))、增加角色等,用戶有這么多的信息和行為要維護,我們就把這些寫到一個接口中,都是用戶管理類嘛,我們先來看它的類圖,如圖1-1所示。
太Easy的類圖了,我相信,即使是一個初級的程序員也可以看出這個接口設(shè)計得有問題,用戶的屬性和用戶的行為沒有分開,這是一個嚴(yán)重的錯誤!這個接口確實設(shè)計得一團糟,應(yīng)該把用戶的信息抽取成一個BO(Business Object,業(yè)務(wù)對象),把行為抽取成一個Biz(Business Logic,業(yè)務(wù)邏輯),按照這個思路對類圖進行修正,如圖1-2所示。
重新拆封成兩個接口,IUserBO負(fù)責(zé)用戶的屬性,簡單地說,IUserBO的職責(zé)就是收集和反饋用戶的屬性信息;IUserBiz負(fù)責(zé)用戶的行為,完成用戶信息的維護和變更。各位可能要說了,這個與我實際工作中用到的User類還是有差別的呀!別著急,我們先來看一看分拆成兩個接口怎么使用。OK,我們現(xiàn)在是面向接口編程,所以產(chǎn)生了這個UserInfo對象之后,當(dāng)然可以把它當(dāng)IUserBO接口使用。也可以當(dāng)IUserBiz接口使用,這要看你在什么地方使用了。要獲得用戶信息,就當(dāng)是IUserBO的實現(xiàn)類;要是希望維護用戶的信息,就把它當(dāng)作IUserBiz的實現(xiàn)類就成了,如代碼清單1-1所示。
圖1-2 職責(zé)劃分后的類圖
代碼清單1-1 分清職責(zé)后的代碼示例
......
IUserInfo userInfo = new UserInfo();
//我要賦值了,我就認(rèn)為它是一個純粹的BO
IUserBO userBO = (IUserBO)userInfo;
userBO.setPassword(""abc"");
//我要執(zhí)行動作了,我就認(rèn)為是一個業(yè)務(wù)邏輯類
IUserBiz userBiz = (IUserBiz)userInfo;
userBiz.deleteUser();
......
確實可以如此,問題也解決了,但是我們來分析一下剛才的動作,為什么要把一個接口拆分成兩個呢?其實,在實際的使用中,我們更傾向于使用兩個不同的類或接口:一個是IUserBO,一個是IUserBiz,類圖如圖1-3所示。
以上我們把一個接口拆分成兩個接口的動作,就是依賴了單一職責(zé)原則,那什么是單一職責(zé)原則呢?單一職責(zé)原則的定義是:應(yīng)該有且僅有一個原因引起類的變更。
1.2 絕殺技,打破你的傳統(tǒng)思維
解釋到這里,估計你已經(jīng)很不屑了,“切!這么簡單的東西還要講?!”好,我們來講點復(fù)雜的。SRP的原話解釋是:
There should never be more than one reason for a class to change.
這句話初中生都能看懂,不多說,但是看懂是一碼事,實施就是另外一碼事了。上面講的例子很好理解,在實際項目中大家都已經(jīng)這么做了,那我們再來看看下面這個例子是否好理解。電話這玩意,是現(xiàn)代人都離不了,電話通話的時候有4個過程發(fā)生:撥號、通話、回應(yīng)、掛機,那我們寫一個接口,其類圖如圖1-4所示。
我不是有意要冒犯IPhone的,同名純屬巧合,我們來看一個這個過程的代碼,如代碼清單1-2所示。
代碼清單1-2 電話過程
public interface IPhone {
//撥通電話
public void dial(String phoneNumber);
//通話
public void chat(Object o);
//通話完畢,掛電話
public void hangup();
}
實現(xiàn)類也比較簡單,我就不再寫了,大家看看這個接口有沒有問題?我相信大部分的讀者都會說這個沒有問題呀,以前我就是這么做的呀,某某書上也是這么寫的呀,還有什么什么的源碼也是這么寫的!是的,這個接口接近于完美,看清楚了,是“接近”!單一職責(zé)原則要求一個接口或類只有一個原因引起變化,也就是一個接口或類只有一個職責(zé),它就負(fù)責(zé)一件事情,看看上面的接口只負(fù)責(zé)一件事情嗎?是只有一個原因引起變化嗎?好像不是!
IPhone這個接口可不是只有一個職責(zé),它包含了兩個職責(zé):一個是協(xié)議管理,一個是數(shù)據(jù)傳送。dial()和hangup()兩個方法實現(xiàn)的是協(xié)議管理,分別負(fù)責(zé)撥號接通和掛機;chat()實現(xiàn)的是數(shù)據(jù)的傳送,把我們說的話轉(zhuǎn)換成模擬信號或數(shù)字信號傳遞到對方,然后再把對方傳遞過來的信號還原成我們聽得懂的語言。我們可以這樣考慮這個問題,協(xié)議接通的變化會引起這個接口或?qū)崿F(xiàn)類的變化嗎?會的!那數(shù)據(jù)傳送(想想看,電話不僅僅可以通話,還可以上網(wǎng))的變化會引起這個接口或?qū)崿F(xiàn)類的變化嗎?會的!那就很簡單了,這里有兩個原因都引起了類的變化。這兩個職責(zé)會相互影響嗎?電話撥號,我只要能接通就成,甭管是電信的還是網(wǎng)通的協(xié)議;電話連接后還關(guān)心傳遞的是什么數(shù)據(jù)嗎?通過這樣的分析,我們發(fā)現(xiàn)類圖上的IPhone接口包含了兩個職責(zé),而且這兩個職責(zé)的變化不相互影響,那就考慮拆分成兩個接口,其類圖如圖1-5所示。
這個類圖看上去有點復(fù)雜了,完全滿足了單一職責(zé)原則的要求,每個接口職責(zé)分明,結(jié)構(gòu)清晰,但是我相信你在設(shè)計的時候肯定不會采用這種方式,一個手機類要把ConnectionManager和DataTransfer組合在一塊才能使用。組合是一種強耦合關(guān)系,你和我都有共同的生命期,這樣的強耦合關(guān)系還不如使用接口實現(xiàn)的方式呢,而且還增加了類的復(fù)雜性,多了兩個類。經(jīng)過這樣的思考后,我們再修改一下類圖,如圖1-6所示。
圖1-5職責(zé)分明的電話類圖
圖1-6 簡潔清晰、職責(zé)分明的電話類圖
這樣的設(shè)計才是完美的,一個類實現(xiàn)了兩個接口,把兩個職責(zé)融合在一個類中。你會覺得這個Phone有兩個原因引起變化了呀,是的,但是別忘記了我們是面向接口編程,我們對外公布的是接口而不是實現(xiàn)類。而且,如果真要實現(xiàn)類的單一職責(zé),這個就必須使用上面的組合模式了,這會引起類間耦合過重、類的數(shù)量增加等問題,人為地增加了設(shè)計的復(fù)雜性。
通過上面的例子,我們來總結(jié)一下單一職責(zé)原則有什么好處:
q 類的復(fù)雜性降低,實現(xiàn)什么職責(zé)都有清晰明確的定義;
q 可讀性提高,復(fù)雜性降低,那當(dāng)然可讀性提高了;
q 可維護性提高,可讀性提高,那當(dāng)然更容易維護了;
q 變更引起的風(fēng)險降低,變更是必不可少的,如果接口的單一職責(zé)做得好,一個接口修改只對相應(yīng)的實現(xiàn)類有影響,對其他的接口無影響,這對系統(tǒng)的擴展性、維護性都有非常大的幫助。
看過電話這個例子后,是不是想反思一下了,我以前的設(shè)計是不是有點問題了?不,不是的,不要懷疑自己的技術(shù)能力,單一職責(zé)原則最難劃分的就是職責(zé)。一個職責(zé)一個接口,但問題是“職責(zé)”沒有一個量化的標(biāo)準(zhǔn),一個類到底要負(fù)責(zé)那些職責(zé)?這些職責(zé)該怎么細化?細化后是否都要有一個接口或類?這些都需要從實際的項目去考慮,從功能上來說,定義一個IPhone接口也沒有錯,實現(xiàn)了電話的功能,而且設(shè)計還很簡單,僅僅一個接口一個實現(xiàn)類,實際的項目我想大家都會這么設(shè)計。項目要考慮可變因素和不可變因素,以及相關(guān)的收益成本比率,因此設(shè)計一個IPhone接口也可能是沒有錯的。但是,如果純從“學(xué)究”理論上分析就有問題了,有兩個可以變化的原因放到了一個接口中,這就為以后的變化帶來了風(fēng)險。如果以后模擬電話升級到數(shù)字電話,我們提供的接口IPhone是不是要修改了?接口修改對其他的Invoker類是不是有很大影響?
注意 單一職責(zé)原則提出了一個編寫程序的標(biāo)準(zhǔn),用“職責(zé)”或“變化原因”來衡量接口或類設(shè)計得是否優(yōu)良,但是“職責(zé)”和“變化原因”都是不可度量的,因項目而異,因環(huán)境而異。"