切換語言為:簡體
依賴倒置原則,反向思維會給開發帶來什麼收益?

依賴倒置原則,反向思維會給開發帶來什麼收益?

  • 爱糖宝
  • 2024-06-30
  • 2073
  • 0
  • 0

當我們需要某個類A中使用到另外一個類B時,最直接的方式就是在A中直接依賴B,但是,今天我們要講解的主角卻是反其道而行之,它就是依賴倒置原則,那麼,什麼是依賴倒置原則?這種反向思維可以帶來什麼收益?這篇文章就來聊一聊。

什麼是依賴倒置?

依賴倒置原則,英文為:Dependency inversion principle(簡稱DIP),也是 Robert C. Martin提出的 SOLID原則中的一種,老規矩,還是先看看作者 Robert C. Martin 對介面依賴倒置原則是如何定義的:

The Dependency Inversion Principle (DIP) states that high-level
modules should not depend on low-level modules; both should 
depend on abstractions. Abstractions should not depend on details.
Details should depend upon abstractions.


透過作者對依賴倒置的定義,可以總結出其核心思想是:高層模組不應該依賴低層模組,兩者都應該依賴於抽象。抽象不應該依賴於細節,細節應該取決於抽象。

直接依賴的問題

對於上述依賴倒置的定義,如何理解呢?我們先來看下傳統這種直接依賴會存在什麼問題?如下為一張直接依賴的關係圖:

依賴倒置原則,反向思維會給開發帶來什麼收益?

在上圖中,高層元件 ObjectA直接依賴於低層元件 ObjectB,高層元件的重用機會受到限制,因為任何對低層元件的更改都會直接影響高層元件。

爲了更好的說明直接依賴的問題,這裏以一個真實的電商場景為例進行說明,其中有一個高層模組 OrderService用於處理訂單,這個高層模組依賴於一個低層模組 OrderRepository來儲存和檢索訂單資料。示例程式碼如下:

// 高層模組:OrderService
public class OrderService {
    private MySQLOrderRepository mySQLRepository;

    public OrderService(MySQLRepository mySQLRepository) {
        this.mySQLRepository = mySQLRepository;
    }

    public void createOrder(Order order) {
        // 一些業務邏輯
        mySQLRepository.save(order);
    }
}

// 低層模組:MySQLRepository
public class MySQLRepository {
    public void save(Order order) {
        // 使用 MySQL資料庫儲存訂單
    }
}


在上述例子中,OrderService直接依賴於 OrderRepository,這種設計存在幾個缺點:

  • 緊耦合:如果要把資料庫從 MySQL切換到其他的資料庫,我們需要修改 OrderService,因為它直接依賴於 OrderRepository。

  • 難以測試:在進行單元測試時,我們無法輕鬆地對 OrderService 進行模擬,因為它直接依賴於具體實現 MySQLRepository。

  • 重用性差:如果在另一個專案中我們需要使用 OrderService 但儲存訂單的方式不同,例如使用檔案系統或遠端服務,我們將無法直接重用 OrderService。

那麼,對於這些缺點,該如何解決呢?接下來我們將重點講解。

如何實現依賴倒置?

這裏提供兩種主流的解決方案。

方案一

透過低階元件實現高階元件的介面,要求低階元件包依賴於高階元件進行編譯,從而顛倒了傳統的依賴關係,如下圖:

依賴倒置原則,反向思維會給開發帶來什麼收益?

圖1中,高層物件A依賴於底層物件B的實現;圖2中,把高層物件A對底層物件的需求抽象為一個介面A,底層物件B實現了介面A,這就是依賴反轉。

因此,上面的問題我們也可以透過引入一個抽象層 OrderRepository來解耦高層模組和低層模組,整個關係圖如下:

依賴倒置原則,反向思維會給開發帶來什麼收益?

透過這種方式,OrderService依賴於 OrderRepository介面而不是具體實現 MySQLRepository。這樣,我們可以輕鬆替換低層實現而無需修改高層模組,修改後的程式碼如下:

// 高層模組:OrderService
public class OrderService {
    private OrderRepository orderRepository;

    public OrderService(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }

    public void placeOrder(Order order) {
        // 一些業務邏輯
        orderRepository.save(order);
    }
}

// 抽象層:OrderRepository介面
public interface OrderRepository {
    void save(Order order);
}

// 低層模組:MySQLRepository實現
public class MySQLRepository implements OrderRepository {
    public void save(Order order) {
        // 使用MySQL資料庫儲存訂單
    }
}

// 另一個低層模組:PostgreSQLRepository實現
public class PostgreSQLRepository implements OrderRepository {
    public void save(Order order) {
        // 使用PostgreSQL資料庫儲存訂單
    }
}

在應用程式中,我們可以靈活選擇使用哪種具體實現,也可以把資料庫的選擇做成配置:

OrderRepository orderRepository = new MySQLRepository(); // 或 new PostgreSQLRepository();
OrderService orderService = new OrderService(orderRepository);

透過這種方式,OrderService變得更具重用性、可測試性更強,並且與具體的儲存實現解耦,滿足依賴倒置原則的要求。

方案二

儘管方式一也實現了依賴倒置,但是這種實現方式高層元件以及元件是封裝在一個包中,對低層元件的重用會差一些,因此,另一種更靈活的解決方案是將抽象元件提取到一組獨立的包/庫中,如下圖:

依賴倒置原則,反向思維會給開發帶來什麼收益?

因此,上述電商示例的依賴關係會變成下圖:

依賴倒置原則,反向思維會給開發帶來什麼收益?

這種實現方式將每一層分離成自己的封裝,鼓勵任何層的再利用,提供穩健性和移動性。

兩種方案的核心思想都是一樣的,只是在靈活性和元件複用的考慮上略有差異。

依賴倒置的例項

在 Java語言中,使用依賴倒置原則的框架或者技術點有很多,這裏列舉 2個比較較常用的例子:

Spring

Spring框架的核心之一是依賴注入(Dependency Injection, DI),這是依賴倒置原則的一個實現。透過Spring容器管理物件的建立和依賴關係,可以使得高層模組和低層模組都依賴於抽象。Spring支援構造器注入、setter注入和介面注入等多種方式。

Java SPI

Java SPI(Service Provider Interface)機制也體現了依賴倒置原則,SPI機制透過定義介面和服務提供者(Service Providers),使得高層模組(使用者)和低層模組(提供者)之間的依賴關係可以透過介面進行解耦。具體來說,高層模組依賴於抽象(介面),而不是具體的實現,從而實現了依賴倒置原則。

JDBC(Java Database Connectivity)就是使用 SPI機制來載入和註冊資料庫驅動程式,使得應用程式可以動態地使用不同的資料庫而無需修改程式碼。

JDBC SPI的工作原理:

  • 定義服務介面:JDBC API定義了一組介面,如 java.sql.Driver。

  • 實現服務介面:每個資料庫廠商實現這些介面,例如,MySQL的驅動實現了 java.sql.Driver介面。

  • 宣告服務提供者:資料庫驅動的JAR包中包含一個檔案,宣告實現類。

  • 載入服務提供者:透過 ServiceLoader或 JDBC API動態載入並例項化驅動實現。

總結

本文透過一個電商示例分析了什麼是依賴倒置原則,並且提出了依賴倒置的兩種實現風格,透過引入抽象層,可以降低系統的耦合度,提升系統的擴充套件性和可維護性。因此,在實際開發中,我們應當始終遵循依賴倒置原則,設計靈活、可擴充套件的系統架構,從而應對複雜多變的業務需求。

0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.