當(dāng)我們在進(jìn)行面向?qū)ο笤O(shè)計(jì)的時(shí)候,常常會(huì)聽到五大原則的說法,他們分別是
- 單一職責(zé)原則
- 開閉原則
- 里氏替換原則
- 依賴倒置原則
- 接口隔離原則
這五大原則對面向?qū)ο笤O(shè)計(jì)而言十分重要,它定義了一個(gè)優(yōu)良的設(shè)計(jì)所應(yīng)該具有的特點(diǎn)。首先我們來討論單一職責(zé)原則。
定義
單一職責(zé)原則的定義如下
每個(gè)類都應(yīng)該有一個(gè)單一的功能,并且該功能應(yīng)該由這個(gè)類完全封裝起來。所有它的(這個(gè)類的)服務(wù)都應(yīng)該嚴(yán)密的和該功能平行
這個(gè)原則由羅伯特·C·馬丁首次提出
這個(gè)定義中包含兩層含義
-
一個(gè)類只應(yīng)該有一個(gè)功能。
所謂功能(職責(zé)),馬丁的定義是改變的原因。 -
一個(gè)功能應(yīng)該完全由一個(gè)類/模塊封裝起來。
一個(gè)功能(職責(zé)),不應(yīng)該散落在多個(gè)類中,否則,一方面不利于理解;另一方面也不便于修改。
理解
如何理解這兩層含義,下面通過一個(gè)例子來進(jìn)行說明。
考慮我們正在編寫一個(gè)繪圖程序,支持繪制各種圖形,比如圓形,三角形等。
下面是圓形的一種實(shí)現(xiàn)方案

這個(gè)方案很常見,但是存在一個(gè)明顯的問題——繪制函數(shù)實(shí)現(xiàn)在Circle類內(nèi)部。
在這個(gè)設(shè)計(jì)中一旦我們要修改圖形繪制的樣式,將不得不修改Circle類的實(shí)現(xiàn),如果只是這一個(gè)類還好,但是繪圖程序中,可能有很多這樣的類(比如方形,三角形,扇形等),這樣的修改將極其繁瑣且具有風(fēng)險(xiǎn)。
產(chǎn)生這個(gè)問題的根源在于設(shè)個(gè)設(shè)計(jì)違背了單一職責(zé)原則。
一方面,Circle類包含有兩個(gè)職責(zé)(維護(hù)圖形的幾何特征和繪制圖形),這違背了一個(gè)類只應(yīng)該有一個(gè)功能的要求;另一方面,也違背了一個(gè)功能應(yīng)該完全由一個(gè)類/模塊封裝起來的要求,因?yàn)閳D形繪制的職責(zé)不可能在Circle.draw方法中完全實(shí)現(xiàn),勢必需要調(diào)用者的輔助(例如,可能需要的坐標(biāo)變換操作等)。
要解決這個(gè)問題,一個(gè)更好的實(shí)現(xiàn)方案應(yīng)該是將繪制的操作放到另外一個(gè)單獨(dú)的類中處理,讓那個(gè)單獨(dú)類來負(fù)責(zé)所有圖形的繪制,當(dāng)需要修改圓形的樣式時(shí)我們只需要修改對應(yīng)的這個(gè)類既可。
對職責(zé)的思考
說到這里,有一個(gè)問題就出現(xiàn)了,Circle中還剩下兩個(gè)成員——圓心和半徑,誠然,從描述幾何圖形的角度上講他們是一個(gè)職責(zé),但是更細(xì)化的看,這也是兩個(gè)變化的原因,是不是也能理解成這是兩個(gè)職責(zé)呢?
這是一個(gè)引人深思的問題,看問題的粒度對單一職責(zé)的定義影響巨大。
例如,從一個(gè)龐大系統(tǒng)的視角來考慮,假設(shè)需要一個(gè)繪制模塊,那么繪制模塊的職責(zé)是提供各種圖形的繪制功能;但是如果我們聚焦到繪制模塊上,有能看到其中各個(gè)模塊又有不同的職責(zé)。假設(shè)子模塊是單一職責(zé)的,那么包含子模塊的父模塊可能做到單一職責(zé)嗎?這種情況下,我們要如何理解單一職責(zé)原則呢?
對于這個(gè)問題,我個(gè)人的觀點(diǎn)是單一職責(zé)的評判需要針對不同的模塊,在定義這個(gè)模塊的粒度下進(jìn)行。
例如,我們說繪制模塊,一定是從整個(gè)系統(tǒng)的粒度上來看的,從這個(gè)角度上來看,繪制模塊如果只是做了繪制相關(guān)的功能,那么就是單一職責(zé)的。如何把粒度定義為到Circle這樣的類上,實(shí)際上是看不到繪制系統(tǒng)的,那么繪制系統(tǒng)是否是單一職責(zé)的問題也就無從談起了。
總結(jié)
我個(gè)人的觀點(diǎn),在軟件設(shè)計(jì)的過程中,我們要盡量要保證我們定義的模塊/類在它所處的粒度下是單一職責(zé)的。但是,在一些極端的場景下,一味要求所有類100%的都是單一職責(zé),也可能帶來類數(shù)量的爆炸和程序性能的降低。因此單一職責(zé)原則不是刻板的教條,而是應(yīng)該以高內(nèi)聚,低耦合為目標(biāo),靈活運(yùn)用。
PS: 面向?qū)ο笤O(shè)計(jì)五大原則的其它文章
面向?qū)ο笤O(shè)計(jì)五大原則(2)—— 開閉原則