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