在上一節中,主要寫了類的設計原則。然后在網上找了一些包的設計原則和包之間的耦合性原則的介紹,均比較散,只是一個大概上的介紹,今天在網上搜索到一篇不錯的文章,現摘抄如下:
1 晨后綜合癥和包的發布
你曾有過這樣的經歷嗎?工作了一整天,終于完成了某項功能后回家,不料第二天早晨一來卻發現那項功能不再工作了。原因是什么呢?因為有人比你走得更晚,并且更改了你所依賴的某些東西!這就是所謂的“晨后綜合癥"。受“晨后綜合癥”困擾的團隊常常幾周時間都無法構建出一個穩定的版本,每個人都忙于一遍遍更改他們的代碼,試圖使之能夠相容與其他人所做的最近更改,從而造成團隊士氣低落,效率不高,陷入了可怕的集成地獄中。為此有的團隊在集成期間禁止 check in ,這顯然不是一個好辦法,一方面,如果不用技術手段很難避免有意或無意的 check in ,另一方面開發者為了進行后續開發而又不影響當前集成的版本,可能不得不手工進行版本管理, 再有,它使團隊成員間的協作也變得困難。
對包進行發布能有效避免晨后綜合癥的發生。造成晨后綜合癥的原因在于依賴者所依賴的包是變化的,這就使依賴者工作于一個不穩定的基礎之上,對被依賴者的改變,依賴者必須作出相容的改變。采用包的發布機制,依賴者必須選擇被依賴包發布的一個特定版本,而發布后的包,其內容是不允許變化的,因此依賴者所依賴的東西就不會改變;同時,被依賴包的任何改變都必須作為一個新版本發布,而依賴者有權決定是否采用這個新版本,換句話說,是否接受被依賴者的改變是由依賴者決定的,而在此之前,依賴者是不得不被動地接受被依賴者的改變。
2 包的設計原則
要對包進行發布,首先要先設計好包,對規模較大的應用來說,劃分包的組合很多,僅僅把看起來像是適合在一起的類放進相同的包中,得到的往往是一種不好的包結構:發布很困難、不容易重用、難于更改等等,這種包結構帶來的可能是更多的麻煩。顯然我們需要一些原則來指導包的劃分,以下列出這些原則,前三個原則用來指導把類劃分到包中,關注包的內聚性,后三個原則用來處理包之間的相互關系,關注包的耦合性。
2.1 REP 重用發布等價原則
重用的粒度就是發布的粒度。
如果一個包中的軟件是用來重用的,那么它就不能再包含不是為了重用目的而設計的軟件。換句話說,一個包中的軟件要么都是可重用的,要么都不是可重用的。簡單地聲明一個類是可重用的做法是不現實的,我們所重用的任何東西都必須同時被發布和跟蹤。如果一個包同時包含了可重用的類和不可重用的類,那么當不可重用的類發生變化時,就要進行一次包的發布,而原本不受影響的重用者就需要決定是否采用新版本,以及采用新版本后可能的編譯,連接和測試工作。這些內耗操作是應該避免的。
2.2 CRP 共同重用原則
一個包中的所有類應該是共同重用的。如果重用了包中的一個類,那么就要重用包中的所有類。
在大多數情況下,一個可重用抽象需要多個類來表達,該原則規定這些類應該在一個包中,而屬于不同抽象的類不應該在一個包中。乍看起來,這條原則和 REP 有點相似,但實際上還是不同的,比如,抽象 A 包括類 A1、A2、A3 ,抽象 B 包含類 B1、B2、B3 ,從 REP 原則來看,這個包沒有什么不對,因為該包的軟件都是可重用的,但它卻違反了 CRP 原則,因為重用者完全可以只重用 A 抽象的類或只重用 B 抽象的類,這樣當任一抽象的改變都會導致該包重新發布,雖然該發布對某一抽象的使用者是無意義的,但是他們仍然需要需要重新驗證和重新發布,這會白費相當數量的努力。
2.3 CCP 共同封閉原則
包中的所用類對于同一類性質的變化應該是共同封閉的。一個變化若對一個包產生影響,則將對該包中的所有類產生影響,而對其他的包不造成任何影響。
REP 和 CRP 關注的都是重用性, CCP 關注的是可維護性。對大多數的應用來說,可維護性的重要性是超過可重用性的。一個變化(包括需求上的,設計上的等等)可能會引起多個類的更改, CCP 要求我們把這些類放在一個包中,同時把那些不受影響的類放到其他的包中,因此一個包只有一個引起變化的原因,一個變化只對一個包產生影響,這樣大大減少了重新發布的次數和重新發布對其他包的影響,從而提高了軟件的可維護性。
2.4 ADP 無環依賴原則
在包的依賴關系圖中不允許存在環。
如果包的依賴關系圖中存在環,就會導致環上的所有軟件包必須同時發布,如下圖, 
由于 P3 使用了 P1 中的一個類而形成了依賴環,若要發布 P1 ,必須先發布 P2 和 P3 ,而發布 P3 又要先發布 P1 和 P2,P1、P2、P3 實際上已經變成了同一個大包,于是工作于這些包上的開發人員不可避免地會遭受晨后綜合癥。
去掉依賴環的方法有兩個:
-
使用DIP。如圖,把Y的接口和實現分離,
-
增加新包。如圖,把 P1 和 P3 都依賴的類移到一個新包中
2.5 SDP 穩定依賴原則
朝著穩定的方向進行依賴。
如果一個包被很多包所依賴,那么它就是穩定的,因為要使所有依賴于它的包能夠相容于對它所做的更改,往往需要非常大的工作量。一個系統中的所有包并非都應該都是穩定的,因為如果那樣的話,系統將很難更改。 SDP 指導我們處理穩定包和不穩定包之間的關系:不穩定的包應該依賴于穩定的包,一個包應該依賴于比他更穩定的包。你設計了一個不穩定的包,期望它能隨變化容易地更改,可當它被一個穩定的包依賴后,它就再也不會易于更改了,這就使軟件難于修改和變化。
2.6 SAP 穩定抽象原則
包的穩定程度應該和其穩定程度一致。
一個系統的高層構架和設計決策應該被放進穩定的包中,因為這些構架決策不應該經常改變,然而穩定包的不易更改的特點會使這些架構決策不靈活,顯然只用穩定性來度量一個包是不夠的, SAP 告訴我們:穩定的包也應該是抽象的。它應該包含抽象類,系統通過在其他包中實現該穩定包中的抽象類來進行擴展,而該穩定包無需修改,從而在保持穩定的同時也不失靈活性。
3 包的設計過程
幾年前當我還不知道這些包的設計原則時,認為包就是系統的高層的功能分解,應該在項目開始時就設計好,結果遭受了失敗。實際上,包的依賴關系圖和描繪系統的功能之間幾乎沒有關系,它是系統可構建性的映射圖。項目開始時,我們還不知道系統中有哪些類,更不必說它們之間的關系,在這種情況下不僅很難創建出包依賴關系圖,即使創建出來也很可能是不合理的。包的依賴關系結構應該是和系統的邏輯設計一起增長和演化的,項目開始時不必考慮包,系統仍以類的粒度組織,隨著開發的進行,類越來越多,對依賴關系進行管理,避免項目開發中出現晨后綜合癥的需要也不斷增長,此時我們應用 CCP 原則對把可能一同變化的類組織成包進行發布,隨著系統的不斷增長,我們開始關注創建可重用的元素,于是開始使用 CRP 和 REP 來指導包的組合。最后使用 ADP、SDP、SAP 對包圖進行度量,去掉不好的依賴。
4 你真的采用了包的發布機制了嗎
如果說某個做產品的團隊沒有采用包的發布機制,它也許會大叫冤枉:我們明明是進行了發布的呀,你看,這不是我們發布的1.0、1.2版本嗎?事實上,任何一個產品都離不開包的發布,只不過對這樣的團隊而言,他們系統里真正意義上的包只有一個,那就是整個系統,而該包的發布常常是在經過了晨后綜合癥的洗禮之后,是在開發基本完畢后進行的,實際上,我們更應該在產品的開發過程中使用這一機制,這樣,產品的開發過程才會以合理,有序的方式進行,產品才能盡快地交付。