簡體   English   中英

依賴注入在 C++ 中有用嗎

[英]Is dependency injection useful in C++

C# 大量使用依賴注入 (DI)來擁有一個無損可測試的平台。 為此,我需要一個interface ,可能還需要一個DI控制反轉 (IoC) 容器來解析我的實例。

但是你如何在 C++ 中做到這一點? 我已經閱讀了一些關於此的內容,似乎 C++ 中的依賴注入不像 C# 那樣是一個大話題。 在 C++ 中,您使用對對象引用- 這是在 C++ 中使用 DI 的方式,對嗎?

如果我的參考理論是正確的,是否有類似容器的東西可以解析所有參考? 在 C# 中,我有一個"bad class/bad project/assembly" ,它在程序啟動時將我的所有實例注冊到一個靜態容器中。 然后,在每個類中,我都可以實例化靜態容器並解析特定實例,這在 C++ 中可能嗎?

您是否在 C++ 中使用依賴注入(或其他任何名稱)? 如果是,你如何使用它? 與 C# 有相似之處嗎?

為此,我需要一個接口,也許還需要一個容器來解析我的實例。 但是你如何在 C++ 中做到這一點?

以同樣的方式。 不同之處在於,您在 C# 中“編程到接口”,而在 C++ 中“編程到基類”。 此外,您在 C++ 中擁有 C# 中沒有的額外工具(例如,基於策略的模板實現在編譯時選擇的依賴注入)。

在 C++ 中,您使用對對象的引用,這是在 C++ 中使用 DI 的方式,對嗎?

不; 這不是使用 DI方式,這是在 C++ 中使用 DI方式。

還要考慮:

  • 使用指向對象的指針(或智能指針,視情況而定)
  • 為策略使用模板參數(例如,請參閱智能指針中的 std::default_delete 使用)
  • 將 lambda 演算與注入的函子/謂詞一起使用。

在 C# 中,我有一個“壞類/壞項目/程序集”,它在程序啟動時將我的所有實例注冊到一個靜態容器中。

如果我理解正確,您將所有數據設置在這個靜態容器中並在整個應用程序中使用它。 如果是這種情況,那么您沒有正確使用依賴注入,因為這違反了德米特定律。

這在 C++ 中可能嗎?

是的,這是完全可能的(但你不應該這樣做,因為它違反了德米特定律)。 看看 boost::any (這將允許您在容器中存儲異構對象,類似於在 C# 中通過object引用存儲對象)。

您使用的是依賴注入還是在 C++ 中調用的任何內容?

是的(它被稱為依賴注入:))。

如果是,你如何使用它?

如上所述(策略模板參數、注入的函子和謂詞作為可重用組件、通過引用注入對象、指針智能指針或值)。

在 C++ 中使用依賴注入非常簡單。 只需定義一個接口(純抽象基類),您將其用作要依賴注入的類的構造函數或 init 函數的引用或指針(或智能指針)參數。

然后,在單元測試中,注入一個mock對象(從抽象接口類繼承的一個類的實例),在實際代碼中,注入一個真實類的實例(也從同一個接口類繼承)。

十分簡單。

以 C++11 作為項目限制,我最終自己動手。 我松散地基於 .NET Ninject API 而沒有 Reflection ofcourse。

服務定位器

請注意,盡管它稱為 ServiceLocator(因為它本身不執行依賴注入),但如果您使用 lambda 函數綁定,並且最好使用 ServiceLocator::Module 類,您將獲得注入(不是基於反射的)並且它的效果非常好(IMO)

#include <iostream>
#include <vector>
#include "ServiceLocator.hpp"

template <class T>
using sptr = std::shared_ptr<T>;

// Some plain interfaces
class IFood {
public:
    virtual std::string name() = 0;
};

class IAnimal {
public:
    virtual void eatFavouriteFood() = 0;
};


// Concrete classes which implement our interfaces, these 2 have no dependancies
class Banana : public IFood {
public:
    std::string name() override {
        return "Banana";
    }
};

class Pizza : public IFood {
public:
    std::string name() override {
        return "Pizza";
    }
};

// Monkey requires a favourite food, note it is not dependant on ServiceLocator
class Monkey : public IAnimal {
private:
    sptr<IFood> _food;

public:
    Monkey(sptr<IFood> food) : _food(food) {
    }

    void eatFavouriteFood() override {
        std::cout << "Monkey eats " << _food->name() << "\n";
    }
};

// Human requires a favourite food, note it is not dependant on ServiceLocator
class Human : public IAnimal {
private:
    sptr<IFood> _food;

public:
    Human(sptr<IFood> food) : _food(food) {
    }

    void eatFavouriteFood() override {
        std::cout << "Human eats " << _food->name() << "\n";
    }
};

/* The SLModule classes are ServiceLocator aware, and they are also intimate with the concrete classes they bind to
   and so know what dependancies are required to create instances */
class FoodSLModule : public ServiceLocator::Module {
public:
    void load() override {
        bind<IFood>("Monkey").to<Banana>([] (SLContext_sptr slc) { 
            return new Banana();
        });
        bind<IFood>("Human").to<Pizza>([] (SLContext_sptr slc) { 
            return new Pizza();
        });
    }
};

class AnimalsSLModule : public ServiceLocator::Module {
public:
    void load() override {
        bind<IAnimal>("Human").to<Human>([] (SLContext_sptr slc) { 
            return new Human(slc->resolve<IFood>("Human"));
        });
        bind<IAnimal>("Monkey").to<Monkey>([] (SLContext_sptr slc) { 
            return new Monkey(slc->resolve<IFood>("Monkey"));
        });
    }
};

int main(int argc, const char * argv[]) {
    auto sl = ServiceLocator::create();

    sl->modules()
        .add<FoodSLModule>()
        .add<AnimalsSLModule>();

    auto slc = sl->getContext();

    std::vector<sptr<IAnimal>> animals;
    slc->resolveAll<IAnimal>(&animals);

    for(auto animal : animals) {
        animal->eatFavouriteFood();
    }

    return 0;
}

是的,依賴注入在 C++ 中也很有用。 沒有理由不應該,因為它不需要特定的語言或語法,而只需要面向對象的類架構(至少這可能是最常見的情況)。

雖然在 C# 中只有動態分配對象的“指針”,但 C++ 有多種變體,如“普通”局部變量、多種指針、引用……此外,移動語義的概念與此非常相關。

在 C++ 中,您使用對對象的引用,這是在 C++ 中使用 DI 的方式,對嗎?

不僅。 你可以使用任何你想要的東西,只要你可以向類方法傳遞一些東西,只要類對象存在,這個東西就會存在。 以上三種可能性都可以做到(每個都有一定的限制)

有沒有類似容器的東西,我可以解析所有這些引用嗎? 在 C# 中,我有一個“壞類/壞項目/程序集”,它將我的所有實例注冊到一個靜態容器中

也許你錯過了依賴注入的重點。 它與一堆“全局”變量不同。 但是是的,當然這在 C++ 中也是可能的。 有類,有static ,這就是所需要的一切。

如果我的參考理論是正確的,是否有類似容器的東西可以解析所有參考? 在 C# 中,我有一個“壞類/壞項目/程序集”,它在程序啟動時將我的所有實例注冊到一個靜態容器中。 然后,在每個類中,我都可以實例化靜態容器並解析特定實例,這在 C++ 中可能嗎?

這不是 DI 的使用方式,您不會將容器傳遞給所有“消費者”類。 在設計良好的應用程序中,您只需在入口點做一些解析,僅此而已。 大多數情況下,“解決”的需要可以通過使用工廠來代替,工廠將被注冊然后注入。

根據靜態類測試代碼會遇到很多麻煩。 我建議如果你真的想在你的客戶端類中注入你的容器至少實例並注入它,靜態依賴是地獄,會更容易模擬單元測試。

這是一篇舊帖子,但我用 C++ 編寫了一個 di 容器的示例實現。 也許你有興趣。

https://github.com/dirkwinkhaus/widic-dicontainer

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM