前言 :
寫程式的時候都會聽到說,要降低程式之間的相依性。
程式之間的「相依性」,可以用下面簡單的範例來理解。FunctionA裡面使用了FunctionB,當FunctionB功能變更的時候,FunctionA就必須跟著做修改。這也就是說,「FunctionA相依FunctionB」。
static void FunctionA(){ FunctionB();}static void FunctionB(){}
以上面這個範例看起來,相依性不會是很大的問題,改就是了。但是當我們把問題放大,假設系統裡有1000個Function。Function之間互相相依。當要更改1個Function內容並且維持整個系統正確運作,就必須要去檢查其他999個Function是否需要跟著修改。這樣的修改會是一場災難,也是造成很多軟體系統,改這邊壞那邊的主要原因。所以減少相依性,是提高程式碼品質很重要的一個環節。
在「結構化導向程式設計」Function與Function之間的相依性,可以透過追蹤程式碼的方式來識別。但到了「物件導向程式設計」的環境下,雖然說也是可以使用追蹤程式碼的方式,來識別對象之間的相依性。但卻會受到繼承、介面等等物件導向特性的影響,讓識別對象之間相依性的這件工作變得非常複雜。
本篇使用UML類別圖裡的「關係」當作工具,闡述該如何透過UML類別圖裡的「關係」,來輔助開發人員識別物件導向程式的對象相依性。
說明 :
識別對象相依性一個簡單的方法,就是「當兩個類別A與B,分別存在不同的項目內。大幅修改了B之後,A如果需要跟著重新編譯,那就是A相依B」。我們依照這個原則,來檢視UML類別圖裡所定義的各種關係:
關聯關係(Association)
using Association.ProjectB;namespace Association.ProjectA{ public class ClassA { private readonly ClassB _b; public ClassA(ClassB b) { _b = b; } public ClassB B { get { return _b; } } }}
namespace Association.ProjectB{ public class ClassB { }}
與範常式序是UML類別圖裡定義的關聯關係,代表圖形與範常式序。依照原則去檢視程式碼可以看出,當變更ClassB的時候,ClassA需要跟著重新編譯。所以ClassA相依於ClassB。
依賴關係(Dependency)
using Dependency.ProjectB;namespace Dependency.ProjectA{ public class ClassA { public void MethodXXX(ClassB b) { // TODO } }}
namespace Dependency.ProjectB{ public class ClassB { }}
與範常式序是UML裡定義的依賴關係,代表圖形與範常式序。依照原則去檢視程式碼可以看出,當變更ClassB的時候,ClassA需要跟著重新編譯。所以ClassA相依於ClassB。
概括關係(Generalization)
using Generalization.ProjectB;namespace Generalization.ProjectA{ public class ClassA : ClassB { public ClassA() : base() { } }}
namespace Generalization.ProjectB{ public class ClassB { }}
與範常式序是UML裡定義的概括關係,代表圖形與範常式序。依照原則去檢視程式碼可以看出,當變更ClassB的時候,ClassA需要跟著重新編譯。所以ClassA相依於ClassB。
實現關係(Realization)
using Realization.ProjectB;namespace Realization.ProjectA{ public class ClassA : IntefaceB { }}
namespace Realization.ProjectB{ public interface IntefaceB { }}
與範常式序是UML裡定義的實現關係,代表圖形與範常式序。依照原則去檢視程式碼可以看出,當變更IntefaceB的時候,ClassA需要跟著重新編譯。所以ClassA相依於IntefaceB。
彙總關係(Aggregate)
using Aggregate.ProjectB;namespace Aggregate.ProjectA{ public class ClassA { private readonly ItemB[] _itemBCollection; public ClassA() { _itemBCollection = new ItemB[2]; _itemBCollection[0] = new ItemB(); _itemBCollection[1] = new ItemB(); } public ItemB[] ItemBCollection { get { return _itemBCollection; } } }}
namespace Aggregate.ProjectB{ public class ItemB { }}
與範常式序是UML裡定義的彙總關係,代表圖形與範常式序。依照原則去檢視程式碼可以看出,當變更ItemB的時候,ClassA需要跟著重新編譯。所以ClassA相依於ItemB。
*彙總關係語意較為複雜,範常式序只是簡單示意。
組成關係(Composition)
using Composition.ProjectB;namespace Composition.ProjectA{ public class ClassA { private readonly ItemB[] _itemBCollection; public ClassA(ItemB[] itemBCollection) { _itemBCollection = itemBCollection; } public ItemB[] ItemBCollection { get { return _itemBCollection; } } }}
namespace Composition.ProjectB{ public class ItemB { }}
與範常式序是UML裡定義的組成關係,代表圖形與範常式序。依照原則去檢視程式碼可以看出,當變更ItemB的時候,ClassA需要跟著重新編譯。所以ClassA相依於ItemB。
*組成關係語意較為複雜,範常式序只是簡單示意。
後記 :
本篇的文章描述了,如何透過UML類別圖裡的「關係」,來輔助開發人員識別物件導向程式的對象相依性。當將對象依照職責分類成為獨立Package的時候,對象之間的相依性也需要考慮進去,避免在Package之間有相依性雜亂的問題。當發現相依性雜亂時,則可以透過IoC、Facade等等手法來整理相依性。透過不斷的整理相依性,就能慢慢提高程式碼的品質。
參考資料 :
維基百科 - 類別圖