在 Rust 中實現依賴注入

依賴注入簡單地說,就是類應該依賴於抽象,例如數據源的抽象,而不是具體的實現。這意味着類將依賴於接口而不是真正的類。

這有幾個好處:

1,在不更改客戶端代碼的情況下,很容易將一個實現轉換爲另一個實現。

2,由於接口通常只覆蓋類的總 API 的一小部分,因此可以精確地確定哪個類可以訪問哪些方法和功能。

例如,如果有一個數據源類,可以有一個讀接口和一個寫接口,也可以有兩個接口的組合。然後,每個客戶端可以根據需要擁有其中的任意一個接口,這是最小特權原則的一個例子。

它看起來像這樣:

該模式的總結是:應該依賴於接口,而不是具體的實現,這使得交換相同功能的不同實現變得更加容易。

下面在 Rust 中實現這個模式:

在這個例子中,將建立一個虛構的汽車工廠,它只需要輪子:大輪子和小輪子。由於生產這種輪子的機器具有相同的界面或特性,我們可以使用它:

trait WheelProducer {
    fn produce_wheel(&self) -> String;
}

然後,定義一個 CarFactory 結構體,它擁有一個 WheelProducer 的實例:

struct CarFactory {
    wheel_producer: Box<dyn WheelProducer>
}

impl CarFactory {
    fn change_wheel_producer(&mut self, wheel_producer: Box<dyn WheelProducer>) {
        self.wheel_producer = wheel_producer;
    }
}

因爲 WheelProducer 是一個 trait 對象,所以我們必須使用 dyn 關鍵字。此外,由於事先不知道生產者的大小,我們必須把它放在一個 Box 裏。

現在來看看我們的第一個 WheelProducer:

struct SmallWheelProducer;

impl WheelProducer for SmallWheelProducer {
    fn produce_wheel(&self) -> String {
        "Small wheel".to_string()
    }
}

SmallWheelProducer 實現了 WheelProducer 接口。接下來我們對 BigWheelProducer 做同樣的事情:

struct BigWheelProducer;

impl WheelProducer for BigWheelProducer {
    fn produce_wheel(&self) -> String {
        "Big wheel".to_string()
    }
}

現在可以測試我們的設置:

fn main() {
    let small_wheel_producer = SmallWheelProducer;
    let mut factory = CarFactory {
        wheel_producer: Box::new(small_wheel_producer)
    };

    // 生產大輪子的代碼與生產小輪子的代碼一致
    let wheel = factory.wheel_producer.produce_wheel();
    println!("Wheel produced: {}",wheel);

    let big_wheel_producer = Box::new(BigWheelProducer);
    factory.change_wheel_producer(big_wheel_producer);

    // 生產大輪子的代碼與生產小輪子的代碼一致
    let wheel = factory.wheel_producer.produce_wheel();
    println!("Wheel produced: {}",wheel);
}

在我們的簡單示例中,手動執行此操作非常容易,然而,在更高級的示例中,它可能變得容易出錯且麻煩。下面的 lib 庫爲我們實現了依賴注入和控制反轉。

實現依賴注入的 crate:

Inject:實現了用於依賴性解析的宏,體現了控制反轉 (IoC) 原則。

Syrette:方便的 Rust 依賴注入和控制反轉框架。

實現這種模式並不十分困難,需要記住的主要事情是面向接口編程而不是具體的實現。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/h7Wn_Sb-ioPaa1r597L6nQ