"如果一個(gè)系統(tǒng)即復(fù)雜且高耦合, 則一定會(huì)發(fā)生許多意料之外的事情"
原則: 避免出現(xiàn)大型的復(fù)雜模塊, 因?yàn)槟K過(guò)大功能過(guò)多往往意味著模塊間的緊耦合.
解決之道: 為每個(gè)模塊安排單一的職責(zé), 且使用接口將實(shí)現(xiàn)進(jìn)行隱藏.
好處: 代碼會(huì)變得更易維護(hù). 且代碼更易進(jìn)行測(cè)試和使用.
之前的 4 個(gè)原則都是針對(duì)代碼單元而言的, 意味著在實(shí)踐中應(yīng)用它們可以讓獨(dú)立的代碼單元(方法/構(gòu)造函數(shù))更易維護(hù).
從這里開(kāi)始, 就轉(zhuǎn)而關(guān)注模塊(Module)層面上的內(nèi)容了.
注意: 這里的"模塊"在面向?qū)ο笳Z(yǔ)言中指的是一個(gè)類, 比如 Java 中的一個(gè) class. 即之后的模塊層面上的原則都是為了解決類之間的關(guān)系問(wèn)題的.
而本條原則的主旨是: 解決類之間的耦合問(wèn)題, 總地來(lái)說(shuō)就是分離關(guān)注點(diǎn).
設(shè)計(jì)一個(gè)復(fù)雜的軟件系統(tǒng)需要考慮很多,每一個(gè)需要考慮的方面可以稱之為一個(gè)關(guān)注點(diǎn)(Concern)邮屁,良好的設(shè)計(jì)需要把這些關(guān)注點(diǎn)分門別類整袁,劃分為若干模塊,讓程序開(kāi)發(fā)人員在處理一個(gè)關(guān)注點(diǎn)時(shí)可以盡可能少的被其他關(guān)注點(diǎn)的細(xì)節(jié)所干擾佑吝。模塊化軟件開(kāi)發(fā)就是一種分離關(guān)注點(diǎn)(Separation of Concerns)的手段坐昙,模塊化應(yīng)當(dāng)遵循高內(nèi)聚、低耦合的原則芋忿,提高模塊的獨(dú)立性炸客。
這里遇到一個(gè) fan-in
的概念, 軟件設(shè)計(jì)中,扇入和扇出的概念是指應(yīng)用程序模塊之間的層次調(diào)用情況盗飒。
按照結(jié)構(gòu)化設(shè)計(jì)方法,一個(gè)應(yīng)用程序是由多個(gè)功能相對(duì)獨(dú)立的模塊所組成陋桂。
扇入:是指直接調(diào)用該模塊的上級(jí)模塊的個(gè)數(shù)逆趣。扇入大表示模塊的復(fù)用度高。
扇出:是指該模塊直接調(diào)用的下級(jí)模塊的個(gè)數(shù)嗜历。扇出大表示模塊的復(fù)雜度高宣渗,需要控制和協(xié)調(diào)過(guò)多的下級(jí)模塊;但扇出過(guò)欣嬷荨(例如總是1)也不好痕囱。扇出過(guò)大一般是因?yàn)槿狈χ虚g層次,應(yīng)該適當(dāng)增加中間層次的模塊暴匠。扇出太小時(shí)可以把下級(jí)模塊進(jìn)一步分解成若干個(gè)子功能模塊鞍恢,或者合并到它的上級(jí)模塊中去。
設(shè)計(jì)良好的軟件結(jié)構(gòu)每窖,通常頂層扇出比較大帮掉,中間扇出小,底層模塊則有大扇入窒典。
1 一個(gè)實(shí)際的例子
某個(gè)類最開(kāi)始只有讀取用戶, ?修改用戶, 判斷用戶是否存在這三個(gè)功能. 但沒(méi)有使用接口進(jìn)行隱藏實(shí)現(xiàn). 而后隨著不斷添加新的需求, 不斷迭代, 導(dǎo)致類的功能不斷增加, 形成了一個(gè)大型類, 而且該類又被多個(gè)地方調(diào)用(超過(guò) 50 處), 即大扇入. 結(jié)果就是這個(gè)類變成一個(gè)緊耦合的類, 因?yàn)樗淮罅康纳蠈幽K調(diào)用, 且沒(méi)有接口, 而是直接在實(shí)現(xiàn)上進(jìn)行調(diào)用, 而且它也知道許多其他類的內(nèi)部情況.(比如它可以調(diào)用不同的數(shù)據(jù)訪問(wèn)層模塊來(lái)完成不同的功能, 而這些調(diào)用也是在實(shí)現(xiàn)上調(diào)用, 而非調(diào)用接口.)
模塊間的耦合指的是當(dāng)改變發(fā)生的時(shí)候, 卻發(fā)現(xiàn)兩個(gè)或多個(gè)系統(tǒng)部分是連接在一起的. 就像是你想拆掉發(fā)動(dòng)機(jī), 卻發(fā)現(xiàn)發(fā)動(dòng)機(jī)和變速箱焊到了一起解不開(kāi)了.
這種緊耦合的結(jié)果就是它們變?yōu)榱丝删S護(hù)性提升道路上的絆腳石. 而上面說(shuō)的這個(gè)大型類也就是沒(méi)有合理分離關(guān)注點(diǎn)才形成的.
而這樣的大型類后面也就越來(lái)越難理解, 甚至變得無(wú)法管理.
?總結(jié)一下, 為什么類之間的耦合需要高度重視, 主要是因?yàn)?
- 耦合本身就是一個(gè)模塊層面上的代碼問(wèn)題.
- 耦合是絕對(duì)存在的, 但它有程度的區(qū)別, 我們要做到的是盡量低耦合. 而耦合的程度是通過(guò)
number of call
和size of that class
共同來(lái)衡量, 即外界的調(diào)用數(shù)量(扇入), 以及這個(gè)類的大小. 比如外界若調(diào)用這個(gè)類的次數(shù)更多, 那這個(gè)類就應(yīng)該要更小.
從整個(gè)程序的維度來(lái)看, ?上層模塊的扇出(調(diào)用其他模塊的次數(shù))應(yīng)該是大的, 而底層模塊的扇入(被其他模塊調(diào)用的次數(shù))應(yīng)該是大的, 所以保證低耦合的手段就是盡量減小類的體積. 而減小類體積的方法就是對(duì)關(guān)注點(diǎn)進(jìn)行合理分離(?假設(shè)在代碼單元層面上的可維護(hù)原則已經(jīng)得到合理應(yīng)用), 另外就?是使用接口來(lái)隱藏實(shí)現(xiàn), 從而達(dá)到低耦合.
2 動(dòng)機(jī)
- 更小的, 更低耦合的模塊讓多人開(kāi)發(fā)更加容易, 并且變更的時(shí)候更加容易進(jìn)行.
- 更小的, 更低耦合的模塊更加容易定位.
- 更小的, 更低耦合的模塊對(duì)所有開(kāi)發(fā)者都更加友好.
3 做法
- 將關(guān)注點(diǎn)進(jìn)行合理分離, 從而減小類的體積. 即模塊的功能應(yīng)該是單一的, 這需要對(duì)關(guān)注點(diǎn)進(jìn)行合理分離.
- 利用接口來(lái)隱藏實(shí)現(xiàn).
- 將代碼替換為 Library 或 Framework, 這樣可以保證在一處維護(hù)代碼而讓多處使用, 這樣也可以保證模塊間的低耦合.