- 高階模組不應相依於低階模組
- 高階模組和低階模組皆相依於抽象層
- 抽象層不應相依於實做細節
- 實做細節應相依於抽象層
所謂Dependency Inversion
假定我們有套件及類別如下:
套件 | 內含類別 | 用途 |
EIP | UpdateUserData | 更新使用者資料 |
EIPLibrary | WriteToDatabase | 寫入資料庫 |
EIP package定位為高階的模組,用於處理使用者相關資料;DbLibrary定位為低階模組,用於協助EIP package將使用者資料寫入資料庫。沒錯,這是典型傳統的結構化程序設計中,高階模組依賴於低階模組的情況,今日我們並沒有打算改變讓DbLibrary相依於EIP,但考慮以下情況:
今DbLibrary的部份定義為
class WriteToDatabase {
void WriteNow( UserData obj ) {
WriteName( obj.GetName() );
}
void WriteNow( string userName );
}
由於WriteToDatabase中使用了UserData,所以我們說WriteToDatabase相依於UserData,同時也意味著DbLibrary相依於EIP;但一開始我們已經說明,DbLibrary的目的,是協助EIP,也就是EIP本已相依於DbLibrary,今WriteToDatabase的撰寫方式,豈不是讓WriteToDatabase相依於EIP?循環相依,明顯的是一個設計上的錯誤,同時WriteToDatabase相依於EIP的情況,也讓WriteToDatabase的用途受限(引用WriteToDatabase時,勢必同時要引用EIP package)。有什麼方法可以fix這個窘境?有,答案就是interface,我們可以在package DbLibrary中定義一interface:
interface IUserData {
string GetName();
}
接著讓UserData implement IUserData:
class UserData implements IUserData {
string GetName();
}
再來我們修改WriteToDatabase的定義:
class WriteToDatabase {
void WriteNow( IUserData obj ) {
WriteName( obj.GetName() );
}
void WriteNow( string userName );
}
由於IUserData是定義在DbLibrary中,因此WriteToDatabase並不需要相依於EIP;而UserData因為參考到IUserData,所以反倒是依賴於DbLibrary了,如此即反轉原本程式的相依方向了。
沒有留言:
張貼留言