iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 10
2
Software Development

保持前進、持續優化程式碼內涵系列 第 10

10. 從被動變主動—依賴反轉

在前文物件導向設計原則—SOLID,我們簡單的回顧 Robert.C.Martin 提出的 SOLID 原則,單一職責開放封閉里氏替換接口隔離以及依賴反轉

其中,依賴反轉(Dependency inversion principle, DIP)是筆者認為相當重要的原則,實務上使用的機率相當的高,不管是單元測試的實作,功能模組的替換。

1. 定義

A. High-level modules should not depend on low-level modules. Both should depend on abstractions.
高階模組不應該依賴於低階模組,兩者都該依賴抽象。

B. Abstractions should not depend on details. Details should depend on abstractions.
抽象不應該依賴於具體實作方式。具體實作方式則應該依賴抽象。

這兩條定義,說白一點,類別中,不應該直接使用另一個具有實作類別,而是使用抽象的介面,去承接繼承該介面的實作類別。它的目標就是解除物件與物件間,兩者的直接相依關係。

光看文字說明,就跟天書一樣,所以下面用一些例子來說明。

1.1 高階模組依賴低階模組

public class ReportStatistic()
{
    private SQLAccess _access = new SQLAccess();
    
    public double Sum()
    {
        var items = _access.GetAllCost();
        ...
    }
}

public class SQLAccess
{
    public List<CostItem> GetAllCost()
    {
        ...
    }
}

從上面的程式碼可以看到,ReportStatistic (高階模組)緊緊依賴與 SQLAccess (低階模組),筆者習慣稱這種情況為高耦合

1.2 解除物件與物件間,兩者的直接相依關係

當我們依據 DIP 的原則,將兩個物件均改為依賴抽象,那麼程式碼會變成……

public class ReportStatistic()
{
    private IAccess _access = new SQLAccess();
    
    public double Sum()
    {
        var items = _access.GetAllCost();
        ...
    }
}

public interfalce IAccess
{
    List<CostItem GetAllCost();
}

public class SQLAccess : IAccess
{
    public List<CostItem> GetAllCost()
    {
        ...
    }
}

2. 將依賴的物件,交給外部指定

雖然,我們使用抽象介面 IAccess 來承接低階模組 SQLAccess,達到使用依賴反轉,解決高階模組直接依賴低階模組的情況。但是,假若有一天,我們要替換低階模組,只好去變動原本程式碼,去達到替換的目標。

在上面的程式碼,可以猜到資料來源是使用 SQLAccess 這個類別,但是,如果今天有需求要求要從 csv 存取資料,我們也依據 DIP 原則,繼承 IAccess 實作了一個 CsvAccess。那程式碼應該會變成……

public class ReportStatistic()
{
    private IAccess _access = new CsvAccess();
    
    public double Sum()
    {
        var items = _access.GetAllCost();
        ...
    }
}

public class CsvAccess : IAccess
{
    public List<CostItem> GetAllCost()
    {
        ...
    }
}

這就很明顯的違背開放封閉原則了,那麼……要在不變動原本程式碼的前提下,去達到替換低階模組的目標,要怎麼做呢?

於是乎,就有人想到,那麼就不要讓高階模組,自行控制低階模組的建立。而是**將低階模組建立的控制權移到高階模組外部。**再將建立好的低階模組放到高階模組中,讓高階模組使用。

public class ReportStatistic()
{
    private IAccess _access = null
    
    ReportStatistic(IAccess access)
    {
        _access = access;
    }
    
    public double Sum()
    {
        var items = _access.GetAllCost();
        ...
    }
}

這種方式,就是我們常說的控制反轉(Inversion of Control ,IoC)依賴注入(Dependency Injection, DI) 這兩個名詞。

3. 小結

針對依賴反轉控制反轉依賴注入這個主題,筆者盡可能將自己所知的寫出來,但深深覺得還有許多的不足。

不過,己經許多前輩,針對這個主題撰寫許多很優良的文章,分享給大家。筆者將自己知道的文章連結分享於下方,讓各位看倌可以更深入的了解依賴反轉

4. 推薦

4.1 文章


上一篇
09. 物件導向設計原則—SOLID
下一篇
11. 斷開鎖鏈! 低耦合、高內聚
系列文
保持前進、持續優化程式碼內涵24
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言