程式者的胡言亂語

pageicon 星期二 六月 24, 2008

漫談程式碼的相依性 - 4

前文中所提到的資訊隱藏及封裝,是控制程式碼相依性的重要手段之一。許多物件導向程式語言,都提供一些權限存取飾詞,例如publicprotectedprivate等等的飾詞,允許你控制類別成員、類別本身、或類別package對外的存取權限。過度開放的權限,容易招致過多不必要的相依關係。因為當一個類別、或類別package擁有過多對外開放的元素,便成了門戶洞開的局面,其他類別便更容易和它發生依賴的關係。對於存取權限的控制,應該採取盡可能封閉的原則及態度,除非是介面中之必要,否則一律將其封閉,如此一來才能降低外界對此類別的認識程度,以及可能發生的相依關係數量。

除了存取權限控制之外,妥善的決定你類別對外的介面長相,同樣也是另一件重要的事情。我們可以利用程式語言提供的介面(interface)機制,為實作類別築起一道防火牆。而這道防火牆若要有效的隔離因為改變而造成的火勢,就必須把握讓此介面保有「最小相依性、最大穩定性」的原則。想要讓介面相依性最小,介面所含的成員必須要盡可能的少,例如讓介面中的函式愈少愈好。想要讓介面穩定性最大,那麼介面中的函式以及其外貌式的設計,就必須盡可能的做到高階的抽象化,因為愈是抽象、愈是高階的概念,其包容範圍就愈廣,也就愈不容易發生改變。

此外,組織你設計中類別之間的相依性當然也是一件重要的事,在之前的討論中,我們已經從類別的相依圖中見到了倘若能妥善安排、調整類別間的關係,也能改變設計中各類別相依的特質,同時大大的降低改變所造成的衝擊。

我們偶而會看到在一些設計中出現了所謂的循環相依關係(circular dependency)。顧名思義,所謂的循環相依關係,就是當類別間以相依關係相互連接後構成了一個循環。循環的相依關係是一種很糟糕的局面。舉例來說,當A相依於BB相依於CC又相依於A時,它們之間的相依關係,就形成了一個循環。這種局面之所以糟糕,在於相依關係代表著變動傳遞的方向,當相依關係本身為一環狀的結構時,在環狀結構中的每個類別發生變化時,變化會沿著這個環狀結構傳遞,最終甚至可能還傳回原先引發變動的類別。當A發生了變化時,B可能得跟著修改、因而引發C又跟著被迫修改。而當C發生改變時,你或許還會發現,A也受到了C的更動而必須改變。於是,修改的動作便有可能持續的在這個環狀結構中連動的發生。這有可能成為一場惡夢,因為每次的修改都有可能造成連鎖反應,而且或許很難休止。許多新手設計者有時便會設計出循環相依的結構,因而為自己招致苦果。

利用一些常見的架構,可以有效的約束相依關係形成的結構。而其中最為常見、也相當有效的一種架構,便是所謂的分層架構(Layered Architecture)。例如:


在分層架構中,將類別劃分成為不同的層次。例如,在上圖中所���的,便是依據類別所代表的概念的抽象程度區分為高階層、中階層、以及低階層。分層架構是如何的約束類別間的相依關係呢?在分層架構中,僅有位於高階層中的類別,能夠存取較低階層中的類別。有些嚴格的分層架構,甚至不允許跨層的存取,僅允許高階層中的類別存取相接之較低階層中的類別。由於在分層架構下對於類別間的關係有著如此的限制,類別之間的相依關係變得更加井然有序。就上圖中所描述的架構而言,中階層中的類別,扮演高階層類別與低階層類別間的緩衝。當低階的事物發生變化時,或許只需調整中階層中的類別實作,但卻毋需更動高階層類與中階層類別之間的介面,因而杜絕了該變化影響到高階層類別的機會。

現今的 TCP/IP網路,同樣也是利用分層的架構,來將網路協定依其性質之不同,劃分成為不同的階層。在分層的架構下,即使較低階層的協定有所不同,也不會影響到高階層的協定。這正是分層架構有效約束相依關係所展現的威力所在。

除了一些限制、約束相依關係的軟體架構之外,還有一些設計時的守則,可用以規範相依關係發生的情況。例如被稱為Law of Demeter的守則。

在遵守所謂的Law of Demeter時,在某一物件的某一函式中所能呼叫函式的對象是受限制的,它只能是:該物件本身、傳入該函式的引數、在該函式中所產生的物件、以及該物件的資料成員。很明顯的,你不能在此函式中呼叫其他函式所回傳物件的函式,因為其他函式所回傳的物件,並不在上述的對象之中。

從這個守則你可以觀察到,它約束了類別間存取函式的動作,盡可能的消除類別間相識的機會,所以它也被稱為Principle of Least Knowledge。大原則上來說,正如前文中所提到的「相識即相依」,減少類別間相互認識、相互存取的機會,就能有效的降低類別間相依的程度。

除了利用一些軟體架構以及約束類別間存取的守則之外,有一些設計模式,其目標也放在降低程式組成之間的相依性。例如,我們之前已經提過的Façade模式將子系統的對外的相依關係發生處集中於一個地點、Factory Method模式則透過介面以及專職產生介面實作品的工廠函式來降低了客戶端程式碼與實作品之間的相依性。除此之外,像Mediator這樣的設計模式,同樣可以降低一組存在通訊需求之物件的相依性。

我們的程式中,有時會存在多個高度相依的類別,這些類別的物件在發生了某些事件之後,會引發其他類別發生連動的變化。例如某個類別物件在狀態值改變時,得讓它所認識的若干個類別物件之函式被呼叫,以便同步這些物件進行協同的改變。一種常見的設計,便是呼叫其他物件函式的動作分散的擺放在每個類別的物件中。可是這麼一來,這一組類別間便會構成複雜而且高度交錯的相依關係,因為它們之間往往都存在著這種必須發生連動的關係。如此複雜的相依性,不僅難以閱讀(因為連動的邏輯分散在各個類別中),而且也難以維護,因為對某個類別的修改,可能招來對許多類別的修改。

Mediator模式便著眼於此。套用Mediator模式後,加入了一個Mediator類別做為居中協調的角色。所有的類別不再直接進行錯綜複雜的通訊,而是僅將自己狀態的變化或發生的事件通知Mediator類別。由於所有通告的邏輯皆集中封裝於Mediator類別之中,所以Mediator類別會知道應該呼叫那些類別的物件來反映此一狀態變化或事件的發生。這麼一來,所有的類別不再彼此相依,而是僅相依於Mediator類別,大大的降低了相依關係的個數,最後自然得到了一個更易於閱讀也易於維護的設計。

你可以留意還有其他的設計模式,其主用作用或次要作用皆著眼於相依性的降低。

最後我想提醒的是,在設計中的層次愈高,相依性愈是應該降低。例如,子系統間的相依性其影響層面,就大過於子系統中類別的相依性。在考量相依關係時,應該擔心的是重點地帶,毋需一開始就過度重視每個細節處,因為這可能會讓你浪費不必要的時間及心力。


Blogged with Flock

迴響:

hi 你好,
小弟目前正在工研院機械所工作,因為製作msn機器人的需要,上網收尋了許多文章,剛好看到你有hook msn的文章,可否跟你請教一下,如何完成這樣的工作,如果您有看到,還請您email給我好嗎?
謝謝哩!

小弟email: billgas@itri.org.tw

MSN:
taiwan_pili_fire@msn.com

由...發表 楊筑鈞 on 六月 26, 2008 at 02:08 下午 CST #

請教一下(^o^)

由...發表 特殊符號 on 七月 10, 2008 at 07:45 下午 CST #

發表迴響:
  • HTML 語法: 關閉
把對母乳媽媽的感謝與支持傳出去

« 九月 2010
星期日星期一星期二星期三星期四星期五星期六
   
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  
       
今日

Search this blog

Links

Weblog menu

Today's referrers

Feeds