架构设计和包图
包組織原則
- 將在功能上有緊密聯(lián)系的、垂直或水平的切片打包
- 將一族接口打包
- 將一組不穩(wěn)定的類打包
- 提取獨(dú)立的類型
- 利用工廠(factory)來(lái)降低實(shí)體包之間的依賴
- 不要在包中出現(xiàn)回路
包設(shè)計(jì)原則
包的設(shè)計(jì)原則可分為兩大類: 一類是間隔尺度,規(guī)定了包的內(nèi)聚度,包括1)發(fā)布和重用等價(jià)原則,REP(The Release-Reuse Equivalency Principle);2)全部重用原則,CRP( The Common Reuse Principle);3)公共閉合原則,CCP(The Common Closure Principle)。 另一類是穩(wěn)定性,規(guī)定了包之間的耦合度,包括1)非循環(huán)依賴原則,ADP(The Acyclic Dependencies Principle);2)穩(wěn)定依賴原則,SDP(The Stable Dependencies Principle);3)穩(wěn)定抽象原則,SAP(The Stable Abstractions Principle)。
發(fā)布重用原則(REP)
REP規(guī)定,The granule of reuse is the granule of release,即重用粒度等于發(fā)布粒度。它包含了以下幾個(gè)意思:
- 包通常是一個(gè)發(fā)布版本中的基本單位;
- 為了提供重用所需要的保證,開(kāi)發(fā)人員必須將他們的軟件分成一些可重用的包,然后跟蹤這些包,再發(fā)布新的版本;
- 我們重用的任何東西都必須被發(fā)布并且跟蹤;
- 一個(gè)包中的元素或類要么都可重用,要么都不可重用。這是因?yàn)槿绻粋€(gè)包里包含了某個(gè)可以重用的軟件,它就不應(yīng)該再包括不是用于重用的軟件。
重用發(fā)布等價(jià)原則為我們指明了包的設(shè)計(jì)方針:
一個(gè)包中的元素(類)要么都可重用,要么都不可重用。
全部重用原則(CRP)
CRP規(guī)定,The classes in a package are reused together. If you reuse one of the classes in a package, you reuse them all.也就是說(shuō),包的所有類被一起重用。如果你重用了其中的一個(gè)類,就重用全部。換一種通俗的說(shuō)法:Classes that aren’t reused together should not be grouped together,沒(méi)有被一起重用的類不應(yīng)該被組合在一起。 CRP幫助我們決定哪些類應(yīng)該放在同一個(gè)包里:那些將會(huì)一起重用的類應(yīng)該放在同一個(gè)包里。 CRP更告訴我們哪些類不應(yīng)該放在同一個(gè)包里:那些不是緊密聯(lián)系的類不應(yīng)該放在同一個(gè)包里。可以獨(dú)立使用或在不同情形下使用的類型應(yīng)該放在不同的包里。
依賴一個(gè)包就是依賴這個(gè)包所包含的一切。當(dāng)一個(gè)包發(fā)生了改變,并發(fā)布新的版本,使用這個(gè)包的所有用戶都必須在新的包環(huán)境下驗(yàn)證他們的工作,即使被他們使用的部分沒(méi)有發(fā)生任何改變。因?yàn)槿绻邪形幢皇褂玫念?#xff0c;即使用戶不關(guān)心該類是否改變,但用戶還是不得不升級(jí)該包并對(duì)原來(lái)的功能加以重新測(cè)試。
公共閉合原則(CCP)
CCP規(guī)定,The classes in a package should be closed together against the same kinds of changes. a change that affects a package affects all the classes in that package,一個(gè)包中所有的類應(yīng)該對(duì)同一種類型的變化關(guān)閉。一個(gè)變化影響一個(gè)包,便影響了包中所有的類,而且不會(huì)影響其他包。 一個(gè)更簡(jiǎn)短的說(shuō)法是:Classes that change together, belong together.一起修改的類,應(yīng)該組合在一起(同一個(gè)包里)。 CCP跟開(kāi)閉原則(OCP: Open Closed Principle) 有著很深的淵源關(guān)系,CCP的“關(guān)閉”(closure)就是OCP所提倡的:classes should be closed for modification but open for extension. 類應(yīng)該對(duì)修改關(guān)閉,對(duì)擴(kuò)展開(kāi)放。但我們知道,100%的“關(guān)閉”是不現(xiàn)實(shí)的,我們?cè)谠O(shè)計(jì)系統(tǒng)時(shí),只能盡量地保持對(duì)大多數(shù)可預(yù)見(jiàn)的修改關(guān)閉。 CCP延伸了OCP的“關(guān)閉”概念,當(dāng)因?yàn)槟硞€(gè)原因需要修改時(shí),把需要修改的范圍限制在一個(gè)最小范圍內(nèi)的包里。
非循環(huán)依賴原則(ADP)
ADP規(guī)定,The dependency structure between packages must be a directed acyclic graph (DAG). That is, there must be no cycles in the dependency structure.包之間的依賴結(jié)構(gòu)必須是一個(gè)直接的無(wú)環(huán)圖形(DAG)。也就是說(shuō),在依賴結(jié)構(gòu)中不允許出現(xiàn)環(huán)(循環(huán)依賴)。
如果出現(xiàn)了包循環(huán)依賴問(wèn)題,如包A依賴包B,包B依賴包C,而包C又依賴包A,那么我們修改了B并需要發(fā)布B的一個(gè)新的版本,因?yàn)锽依賴C,所以發(fā)布時(shí)應(yīng)該包含C,但C同時(shí)又依賴A,所以又應(yīng)該把A也包含進(jìn)發(fā)布版本里。也就是說(shuō),依賴結(jié)構(gòu)中,出現(xiàn)在環(huán)內(nèi)的所有包都不得不一起發(fā)布。它們形成了一個(gè)高耦合體,當(dāng)項(xiàng)目的規(guī)模大到一定程度,包的數(shù)目變多時(shí),包與包之間的關(guān)系便變得錯(cuò)綜復(fù)雜,各種測(cè)試也將變得非常困難,常常會(huì)因?yàn)槟硞€(gè)不相關(guān)的包中的錯(cuò)誤而使得測(cè)試無(wú)法繼續(xù)。而發(fā)布也變得復(fù)雜,需要把所有的包一起發(fā)布,無(wú)疑增加了發(fā)布后的驗(yàn)證難度。
為了打破這種循環(huán)依賴,有兩種解決方法:
1)找出兩個(gè)包里參與這個(gè)循環(huán)的因素,并把它們組合成一個(gè)新的包。
例如,若存在以下依賴關(guān)系。
包C要依賴包A,必定A中包含有A,C共通使用的類,把這些共同類抽出來(lái)放在一個(gè)新的包D里。這樣就把C依賴A變成了C依賴D以及A依賴D,從而打破了循環(huán)依賴關(guān)系。如圖:
這樣,包的依賴關(guān)系就從A->B->C->A變成了:
A->B->C->D
A->D
2)利用接口。ISP(接口分隔原則)可以剔除沒(méi)用到的接口。DIP(依賴倒置原則)在類的調(diào)用之間引入抽象層。
例如,若存在左下圖的循環(huán)依賴。
那么A必然使用了B中的某個(gè)類,這樣我們可以吧這個(gè)類分離成一個(gè)接口,并讓B實(shí)現(xiàn)這個(gè)接口并依賴于它,同時(shí)讓包A也依賴于這個(gè)接口,就可以打破這種循環(huán)。
又例如,三個(gè)循環(huán)依賴的類Myapplication、MyTasks和MyDialoges,如左下圖。如果用方法一,我們可以將Myapplication和MyDialoges共同使用的類分離成一個(gè)新的包,如右下圖所示;如果用方法二,MyDialoges中的類X使用了Myapplication中的類Y,我們可以為類Y設(shè)計(jì)一個(gè)接口IX,并把它放在包MyDialogues里,再讓Myapplication中的Y實(shí)現(xiàn)并該包依賴于它。
穩(wěn)定依賴原則(SDP)
SDP規(guī)定,The dependencies between packages in a design should be in the direction of the stability of the packages. A package should only depend upon packages that are more stable that it is.一個(gè)設(shè)計(jì)中的包之間的依賴應(yīng)該朝著穩(wěn)定的方向進(jìn)行。一個(gè)包只應(yīng)該依賴那些比自己更穩(wěn)定的包。 換成另一個(gè)說(shuō)法是:Depend in the direction of stability.朝著穩(wěn)定的方向進(jìn)行依賴。
也就是說(shuō),包應(yīng)該依賴比自己更穩(wěn)定的包。因?yàn)槿绻蕾囈粋€(gè)不穩(wěn)定的包,那么當(dāng)這個(gè)不穩(wěn)定的包發(fā)生變化時(shí),本身穩(wěn)定的包也不得不發(fā)生變化,變得不穩(wěn)定了。
所謂穩(wěn)定,在現(xiàn)實(shí)生活中是指一個(gè)物體具有穩(wěn)固不變的屬性使它很難發(fā)生變化。應(yīng)用到軟件概念上,我們認(rèn)為一個(gè)軟件是穩(wěn)定的,是因?yàn)檫@個(gè)軟件很難發(fā)生改變,或更確切地說(shuō),是不需要發(fā)生改變。一個(gè)設(shè)計(jì)良好,能應(yīng)對(duì)各種變化不需要修改的軟件當(dāng)然是穩(wěn)定的了,但事實(shí)上,往往一個(gè)軟件常常需要對(duì)應(yīng)某個(gè)事先沒(méi)有預(yù)測(cè)到的用戶需求而不得不發(fā)生改變,當(dāng)這種改變發(fā)生時(shí),能把修改控制在最小的范圍之內(nèi),并能穩(wěn)定的工作(包括軟件本身以及依賴它的其它軟件實(shí)體等),我們也會(huì)認(rèn)為該軟件是相對(duì)穩(wěn)定的。
那么,怎么判斷一個(gè)包是否穩(wěn)定呢?我們可以通過(guò)下面的方法來(lái)判斷一個(gè)包的穩(wěn)定系數(shù):
- Ca:Afferent Coupling。向心耦合。依賴該包(包含的類)的外部包(類)的數(shù)目(i.e. incoming dependencies)。
- Ce: Efferent Coupling。離心耦合。被該包依賴的外部包的數(shù)目(i.e. outgoing dependencies)。
如圖1,X的Ce=0,所以不穩(wěn)定性I=0,它是穩(wěn)定的。相反,如圖2,Y的Ce=3,Ca=0,所以它的不穩(wěn)定性I=1,它是不穩(wěn)定的。
SDP要求一個(gè)包的不穩(wěn)定性I要大于它所依賴的包的不穩(wěn)定性。“Depend upon packages whose I metric is lower than yours.”
換句話說(shuō),沿著依賴的方向,包的不穩(wěn)定性應(yīng)該逐漸降低,穩(wěn)定性應(yīng)該逐漸升高。
例如,左下圖中一個(gè)穩(wěn)定的包依賴了一個(gè)不穩(wěn)定的包,就違反了SDP原則。
? ? ? ?
穩(wěn)定抽象原則(SAP)
SAP原則規(guī)定,Packages that are maximally stable should be maximally abstract. Instable packages should be concrete. The abstraction of a package should be in proportion to its stability.最穩(wěn)定的包應(yīng)該是最抽象的包。不穩(wěn)定的包應(yīng)該是具體的包。包的抽象程度跟它的穩(wěn)定性成正比。
一個(gè)包的抽象程度越高,它的穩(wěn)定性就越高。反之,它的穩(wěn)定性就越低。一個(gè)穩(wěn)定的包必須是抽象的,反之,不穩(wěn)定的包必須是具體的。
穩(wěn)定的包的構(gòu)成
抽象類或接口通過(guò)子類繼承擴(kuò)展行為,這表示抽象類或接口比它們的子類更具有穩(wěn)定性。總之,為了構(gòu)成穩(wěn)定的包,應(yīng)該提高包內(nèi)的抽象類或接口的比率;它們的子類可以放在另一個(gè)不穩(wěn)定的包內(nèi),該包依賴上述穩(wěn)定的包,從而遵循了穩(wěn)定依賴原則(SDP)。
理想的體系結(jié)構(gòu)應(yīng)該是:
不穩(wěn)定的(容易改變的)包處于上層
- 它們是具體的包實(shí)現(xiàn)
穩(wěn)定的(不容易改變的)包處于下層
- 不容易改變,但容易擴(kuò)展
- 接口比實(shí)現(xiàn)(具體的運(yùn)行代碼)在內(nèi)在特性上更具有穩(wěn)定性
因此,我們可以1)將具有功能性關(guān)聯(lián)的接口放在同一個(gè)包里,并與其實(shí)現(xiàn)分離開(kāi)來(lái);2)利用工廠來(lái)降低具體的包之間的依賴,一種提高包的穩(wěn)定性的方法是減少它對(duì)其他包中具體的包類的依賴。
我們通常使用工廠(factory)來(lái)降低包之間的耦合度。參考資料:包的設(shè)計(jì)原則?http://www.uml.org.cn/mxdx/200912233.asp
總結(jié)
- 上一篇: 回顾微信之父张小龙演说:超1亿人朋友圈设
- 下一篇: UE4 OpenGL坐标系