包的設計原則

 

包的設計原則
 

2009-12-23 來源:lifevv.com

 

重用發佈等價原則

前言

Robert C. Martin氏爲我們總結了在面向對象的設計(OOD)中應該遵循的原則,這些原則被稱爲“Principles of OOD”,關於“Principles of OOD”的相關文章可以從Object Menter得到。

本文介紹The Release Reuse Equivalency Principle (REP):重用發佈等價原則。

概要

The granule of reuse is the granule of release.

重用粒度等價於發佈粒度。

重用的概念

重用主要是從用戶的觀點來看的。

對用戶來說,使用某個發佈單位(組件,類,類羣等),如果作者因爲某種原因對其作了修改而發佈了一個新的版本,用戶會期望在升級爲新版本之後,不會影響到原系統的正常運作。

也就是說,對於一個可重用(能供其它用戶或系統使用)的元素(組件,類,類羣等),作者應該承諾新版本能夠兼容舊版本。否則,用戶將拒絕使用該元素。

Robert C. Martin氏給出的對重用的定義:

I prefer to define reuse as follows. I reuse code if, and only if, I never need to look at the source code (other than the public portions of header files). I need only link with static libraries or include dynamic libraries. Whenever these libraries are fixed or enhanced, I receive a new version which I can then integrate into my system when opportunity allows.

代碼可以看作爲可重用的代碼,當且僅當:

- 它的使用者(下稱用戶)無需看它的源代碼

- 用戶只需聯結靜態庫或包含動態庫

- 當庫發生改變(錯誤糾正,功能增強)時,用戶只需要得到一個新的版本便能集成到原有的系統

怎麼做到重用呢?

一個組件要做到能夠重用,它必須有一個得到良好設計的結構,它所包含所有元素必須也是可以重用的。

因爲如果一個爲重用而設計的發佈單位裏,包含了不可重用的元素,當不可重用的元素髮生改變時,用戶也不得不改變原有系統以適應新的版本。這顯然違反了重用的定義規則。

也就是說,一個爲重用目的而設計的發佈單位裏,不能包含不可重用的元素;如果包含了不可重用的元素,它將變得不可重用。

發佈單位

當用戶使用的重用組件被作者修改後,用戶希望得到通知,然後決定是否升級或升級的時機。爲了能使升級後的系統也能正常運作,用戶也希望作者有一個規範的發佈,包括版本號,類庫的說明等等。

一旦用戶決定升級新版本,不管這些修改是否影響到用戶,用戶也不得不包含新版本所包含的所有類庫。

REP規定重用粒度不能小於發佈粒度,所有重用元素也必須被一起發佈。

發佈粒度可以爲包(組件),或類等實體,但一個應用往往包含了很多類,所以,具有更大的尺度的包(組件)更加適合作爲發佈粒度。

重用發佈等價原則爲我們指明瞭包的設計方針:

一個包中的元素(類)要麼都可重用,要麼都不可重用。

小結

重用發佈等價原則(REP)從用戶觀點的角度上爲我們規範了包設計的原則:在設計包時,包中應該包含的元素要麼都可以重用,要麼都不可以重用。

參考資料

Granularity by Robert C. Martin。

Principles and Patterns by Robert C. Martin。

無環依賴原則

前言

Robert C. Martin氏爲我們總結了在面向對象的設計(OOD)中應該遵循的原則,這些原則被稱爲“Principles of OOD”,關於“Principles of OOD”的相關文章可以從Object Mentorr得到。

本文介紹The Acyclic Dependencies Principle (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)。也就是說,在依賴結構中不允許出現環(循環依賴)。

換成另一個說法是:

The dependencies between packages must not form cycles.

包之間的依賴不能是一個環狀形式。

包的依賴

如果一個包A 中的類引用了包B中的類,我們稱包A依賴包B。

“依賴”在具體的程序語言中表現爲,如果A依賴B,C/C++語言則在A包的文件/類中通過#include語句包含B包中的文件/類;Java語言則A包的類中通過import語句引入B包中的類。

圖1(包A依賴包B)

虛線表示一種依賴關係,箭頭表示依賴的方向,箭頭所在的一側就是被依賴的包。

包的循環依賴

我們上面討論了並用圖形表示了包之間的依賴關係。如果存在2個或2個以上的包,它們之間的依賴關係圖出現了環狀,我們就稱包之間存在循環依賴關係。
也就是說它們的依賴結構圖根據箭頭的方向形成了一個環狀的閉合圖形。如圖:

圖2:包的循環依賴

如圖:A依賴B,B依賴C,C依賴A,形成了一個環狀依賴。

包的非循環依賴原則

包是一個比較合適的發佈粒度,當修改了包中的代碼(類,模塊等)併發布新的版本時,我們需要把該包以及它所依賴的其它包一起發佈。發佈之後,還需要驗證系統是否能在新發布的版本下正常運作。

如果多個包之間形成了循環依賴,比如如圖2,A依賴B,B依賴C,C依賴A,我們修改了B並需要發佈B的一個新的版本,因爲B依賴C,所以發佈時應該包含C,但C同時又依賴A,所以又應該把A也包含進發布版本里。

也就是說,依賴結構中,出現在環內的所有包都不得不一起發佈。它們形成了一個高耦合體,當項目的規模大到一定程度,包的數目變多時,包與包之間的關係便變得錯綜複雜,各種測試也將變得非常困難,常常會因爲某個不相關的包中的錯誤而使得測試無法繼續。而發佈也變得複雜,需要把所有的包一起發佈,無疑增加了發佈後的驗證難度。

循環依賴的打破方法

如果包的依賴形成了環狀結構,怎麼樣打破這種循環依賴呢?

有2種方法可以打破這種循環依賴關係:第一種方法是創建新的包,第二種方法是使用DIP(依賴倒置原則)和ISP(接口分隔原則)設計原則。

方法一:創建新的包

比如對於圖2這種依賴結構:

圖2:包的循環依賴

包C要依賴包A,必定A中包含有A,C共通使用的類,把這些共同類抽出來放在一個新的包D裏。這樣就把C依賴A變成了C依賴D以及A依賴D,從而打破了循環依賴關係。如圖:

這樣,包的依賴關係就從A->B->C->A變成了:

A->B->C->D

A->D

方法二:DIP與ISP設計原則

ISP(接口分隔原則)可以剔除美用到的接口。DIP(依賴倒置原則)在類的調用之間引入抽象層。

如圖,,包A依賴包B(因爲包A中的類U使用了包B中的類X);反過來,包B又依賴包A(因爲包B中的類Y使用了包A中的類V)

包A,包B之間就形成了一種循環依賴。

我們使用DIP設計原則爲V抽象一個接口IVforY,並把該接口放在B包中。

這樣就把Y對V的調用轉化爲:

V繼承IVforY

Y調用IVforY

如圖:

這樣一來,包B中的類就不依賴任何包A中的類了。

小結

無環依賴原則(ADP)爲我們解決包之間的關係耦合問題。在設計包結構時,不能有循環依賴。

參考資料

Granularity by Robert C. Martin。

Principles and Patterns by Robert C. Martin。

穩定抽象等價原則

前言

Robert C. Martin氏爲我們總結了在面向對象的設計(OOD)中應該遵循的原則,這些原則被稱爲“Principles of OOD”,關於“Principles of OOD”的相關文章可以從Object Mentor得到。

本文介紹The Stable Abstractions Principle (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.

最穩定的包應該是最抽象的包。不穩定的包應該是具體的包。包的抽象程度跟它的穩定性成正比。

換成另一個說法是:

Stable packages should be abstract packages.

穩定的包應該是抽象的包。

包的穩定抽象等價原則

我們在The Stable Dependencies Principle (SDP) - OO設計的穩定依賴原則 一文中談到了包的穩定性:不容易改變的包應該具有更好的穩定性。

一個包的抽象程度越高,它的穩定性就越高。反之,它的穩定性就越低。一個穩定的包必須是抽象的,反之,不穩定的包必須是具體的。

穩定的包的構成

抽象類或接口通過子類繼承擴展行爲,這表示抽象類或接口比它們的子類更具有穩定性。總之,爲了構成穩定的包,應該提高包內的抽象類或接口的比率;它們的子類可以放在另一個不穩定的包內,該包依賴上述穩定的包,從而遵循了穩定依賴原則(SDP)。

理想的體系結構應該是:

不穩定的(容易改變的)包處於上層

- 它們是具體的包實現

穩定的(不容易改變的)包處於下層

- 不容易改變,但容易擴展

- 接口比實現(具體的運行代碼)在內在特性上更具有穩定性

圖1:遵循穩定依賴原則(SDP)的理想的體系結構

小結

穩定抽象等價原則(SAP)爲我們解決包之間的關係耦合問題。在設計包結構時,穩定的包應該是抽象的(由抽象類或接口構成),不穩定的包應該是具體的(由具體的實現類構成)。

參考資料

Principles and Patterns by Robert C. Martin。

Principles and Patterns

穩定依賴原則

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.

一個設計中的包之間的依賴應該朝着穩定的方向進行。一個包只應該依賴那些比自己更穩定的包。

前言

Robert C. Martin氏爲我們總結了在面向對象的設計(OOD)中應該遵循的原則,這些原則被稱爲“Principles of OOD”,關於“Principles of OOD”的相關文章可以從Object Mentor得到。

本文介紹The Stable Dependencies Principle (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.

朝着穩定的方向進行依賴。

包的依賴

如果一個包A 中的類引用了包B中的類,我們稱包A依賴包B。

“依賴”在具體的程序語言中表現爲,如果A依賴B,C/C++語言則在A包的文件/類中通過#include語句包含B包中的文件/類;Java語言則A包的類中通過import語句引入B包中的類。

圖1(包A依賴包B)

虛線表示一種依賴關係,箭頭表示依賴的方向,箭頭所在的一側就是被依賴的包。

包的穩定依賴原則

包應該依賴比自己更穩定的包。因爲如果依賴一個不穩定的包,那麼當這個不穩定的包發生變化時,本身穩定的包也不得不發生變化,變得不穩定了。

所謂穩定,在現實生活中是指一個物體具有穩固不變的屬性使它很難發生變化。應用到軟件概念上,我們認爲一個軟件是穩定的,是因爲這個軟件很難發生改變,或更確切地說,是不需要發生改變。一個設計良好,能應對各種變化不需要修改的軟件當然是穩定的了,但事實上,往往一個軟件常常需要對應某個事先沒有預測到的用戶需求而不得不發生改變,當這種改變發生時,能把修改控制在最小的範圍之內,並能穩定的工作(包括軟件本身以及依賴它的其它軟件實體等),我們也會認爲該軟件是相對穩定的。

怎麼樣讓一個軟件足夠穩定呢?一個確切的方法是,讓大量其它軟件的包依賴它。一個包被很多其他包依賴是非常穩定的,這是因爲被依賴的包爲了協調其他包必須做很多的工作來對應各種變化(責任的負擔者)。

圖1:穩定的包X

我們認爲X是穩定的,因爲:

- X被很多其他包依賴。相當於責任擔當着。

- X沒有依賴別的包,它是獨立的。

相反,下面列出了一個非常不穩定的包Y,如圖:

圖2:不穩定的包Y

我們認爲Y是不穩定的,因爲:

- Y沒有被其他的包所依賴。不是責任的擔當着。

- Y依賴很多別的包。

包的穩定性的判斷原則

可以通過下面的方法來判斷一個包的穩定係數:

Ca:Afferent Coupling。向心耦合。依賴該包(包含的類)的外部包(類)的數目(i.e. incoming dependencies)。

Ce: Efferent Coupling。離心耦合。被該包依賴的外部包的數目(i.e. outgoing dependencies)。

I: Instability。不穩定性。I=Ce/(Ce+Ca)。它的值處於[0,1]之間。

如圖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)爲我們解決包之間的關係耦合問題。在設計包結構時,包應該只依賴比自己更穩定的包。

參考資料

Granularity by Robert C. Martin。

Principles and Patterns by Robert C. Martin。

共同封閉原則

前言

Robert C. Martin氏爲我們總結了在面向對象的設計(OOD)中應該遵循的原則,這些原則被稱爲“Principles of OOD”,關於“Principles of OOD”的相關文章可以從Object Mentor得到。

本文介紹The Common Closure Principle (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原則就是把因爲某個同樣的原因而需要修改的所有類組合進一個包裏。如果2個類從物理上或者從概念上聯繫得非常緊密,它們通常一起發生改變,那麼它們應該屬於同一個包。

CCP跟開閉原則(OCP: Open Closed Principle) 有着很深的淵源關係,CCP的“關閉”(closure)就是OCP所提倡的:classes should be closed for modification but open for extension. 類應該對修改關閉,對擴展開放。但我們知道,100%的“關閉”是不現實的,我們在設計系統時,只能儘量地保持對大多數可預見的修改關閉。

CCP延伸了OCP的“關閉”概念,當因爲某個原因需要修改時,把需要修改的範圍限制在一個最小範圍內的包裏。

CCP原則幫助我們決定哪些類應該被放到同一個包裏。

小結

共同封閉原則(CCP)從軟件功能的角度上爲我們規範了包設計的一個原則:在設計包時,相互之間緊密關聯的類應該放在同一包裏。

參考資料

Granularity by Robert C. Martin。

Principles and Patterns by Robert C. Martin。

全部重用原則

前言

Robert C. Martin氏爲我們總結了在面向對象的設計(OOD)中應該遵循的原則,這些原則被稱爲“Principles of OOD”,關於“Principles of OOD”的相關文章可以從Object Mentor得到。

本文介紹The The Common Reuse Principle (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原則幫助我們決定哪些類應該被放到同一個包裏。

依賴一個包就是依賴這個包所包含的一切。當一個包發生了改變,併發布新的版本,使用這個包的所有用戶都必須在新的包環境下驗證他們的工作,即使被他們使用的部分沒有發生任何改變。

因爲如果包中包含有未被使用的類,即使用戶不關心該類是否改變,但用戶還是不得不升級該包並對原來的功能加以重新測試。

我們在Interface Segregation Principle (ISP) - OO設計的接口分隔原則 /sysdesign/doc/20071205214949530.html 一文中介紹了一種針對類的設計原則:

Clients should not be forced to depend upon interfaces that they do not use.不能強迫用戶去依賴那些他們不使用的接口。

把這個概念應用到更廣範圍的包上,就是CRP的基本理念:不要把用戶不使用的類一起組合進包裏。

CRP與REP一樣,都是從方便用戶重用的角度去設計包,重用者是他們的受益者,CCP則讓系統的維護者受益。CCP讓包儘可能大(CCP原則加入功能相關的類),CRP則讓包儘可能小(CRP原則剔除不使用的類)。它們的出發點不一樣,但不相互衝突。

CRP保證了包的內部具有很高的聚合性。

小結

全部重用原則(CRP)從用戶的角度上爲我們規範了包設計的一個原則:在設計包時,相互之間沒有緊密關聯的類不應該放在同一包裏。

參考資料

Granularity by Robert C. Martin。

Principles and Patterns by Robert C. Martin。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章