依赖注入在C++中是否有用

Mar*_*ann 24 c# c++ dependency-injection inversion-of-control loose-coupling

C#使用依赖注入(DI)来构建无损可测试的平台.为此,我需要一个interface也许是DIInversion of Control(IoC)容器来解析我的实例.

但是你如何在C++中做到这一点? 我已经阅读了一些关于这一点,似乎C++中的依赖注入并不像C#那样大.在C++中,您使用对象引用 - 这是在C++中使用DI的方法,对吧?

如果我的参考理论是正确的,是否有类似容器的东西,我可以解决所有的参考?在C#中,我有一个"bad class/bad project/assembly"在程序启动时将我的所有实例注册到静态容器中.然后,在每个类中,我能够实例化静态容器并且可以解析特定实例,这在C++中是否可行?

您是否在C++中使用依赖注入(或其他任何名称)?如果是的话,你是如何使用它的?与C#有相似之处吗?

utn*_*tim 26

为此,我需要一个接口,也许是一个用于解析我的实例的容器.但是你如何用C++做到这一点?

以同样的方式.不同之处在于,在C#中"编程到接口"的位置,您在C++中"编程到基类".此外,您在C++中拥有C#中没有的额外工具(例如,基于策略的模板实现在编译时选择的依赖项注入).

在C++中你使用对象的引用,这是在C++中使用DI的方法,对吧?

没有; 这不是使用DI 方法,这是在C++中使用DI的一种方法.

还要考虑:

  • 使用指向对象的指针(或智能指针,具体取决于具体情况)
  • 为策略使用模板参数(例如,请参阅智能指针中的std :: default_delete)
  • 使用lambda calcullus和注入的函子/谓词.

在C#中我有一个"坏类/坏项目/程序集",它在程序启动时将我的所有实例注册到一个静态容器中.

如果我理解正确,您可以在此静态容器中设置所有数据,并在整个应用程序中使用它.如果是这种情况,那么就不要正确使用依赖注入,因为这会破坏Demeter定律.

这可能在C++中?

是的,这是完全可能的(但你不应该这样做,因为它破坏了得墨忒耳的定律).看一下boost :: any(这将允许您将异构对象存储在容器中,类似于通过objectC#中的引用存储对象).

您是否使用依赖注入或C++中的任何调用?

是的(它被称为依赖注入:)).

如果是的话,你是如何使用它的?

如上所述(策略模板参数,注入的仿函数和谓词作为可重用组件,通过引用注入对象,指针智能指针或值).


Eri*_*pää 17

在C++中使用依赖注入非常简单.只需定义一个接口(一个纯抽象基类),您可以将其用作要依赖注入的类的构造函数或init函数的引用或指针(或智能指针)参数.

然后,在单元测试中,注入一个模拟对象(一个继承自抽象接口类的类的实例),并在实际代码中,注入一个真实类的实例(也继承自相同的接口类).

十分简单.


ste*_*eve 9

随着C++ 11作为项目​​限制我最终滚动自己.我松散地将它基于.NET Ninject API而没有反射过程.

服务定位

注意,虽然它被称为ServiceLocator(因为它不执行Dependancy Injection本身),如果你使用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;
}
Run Code Online (Sandbox Code Playgroud)

  • 该死的,这东西很性感。感谢您制作和发布它。 (2认同)
  • @johnildergleidisson是的,我真的应该重命名该项目,因为正确使用时(使用SLModule)它不是服务定位器,SLModule与其具体类“亲密”,因此知道它们需要什么依赖项并从SLContext请求这些依赖项 - 它的依赖注入,但没有运行时反射来自动计算依赖关系 - 在我看来,它工作得非常好,我使用它的最后一个项目是一个绝对令人愉快的增强和构建,因为它是如何工作的 (2认同)

dev*_*fan 5

是的,依赖注入在 C++ 中也很有用。没有理由不应该,因为它不需要特定的语言或语法,而只需要面向对象的类架构(至少这可能是最常见的情况)。

虽然在 C# 中只有动态分配对象的“指针”,但 C++ 有多种变体,如“普通”局部变量、多种指针、引用……此外,移动语义的概念与此非常相关。

在 C++ 中,您使用对对象的引用,这是在 C++ 中使用 DI 的方式,对吗?

不仅。你可以使用任何你想要的东西,只要你可以向类方法传递一些东西,只要类对象存在,这个东西就会存在。以上三种可能性都可以做到(每个都有一定的限制)

有没有类似容器的东西,我可以解析所有这些引用吗?在 C# 中,我有一个“坏类/坏项目/程序集”,它将我的所有实例注册到一个静态容器中

也许你错过了依赖注入的重点。它与一堆“全局”变量不同。但是是的,当然这在 C++ 中也是可能的。有课程,有,这就是static所需要的一切。


Emm*_*ace 5

如果我的参考理论是正确的,是否有类似容器之类的东西可以解析所有参考?在 C# 中,我有一个“坏类/坏项目/程序集”,它在程序启动时将我的所有实例注册到静态容器中。然后,在每个类中,我都能够实例化静态容器并解析特定实例,这在 C++ 中可能吗?

这不是 DI 应该使用的方式,您不会将容器传递给所有“消费者”类。在设计良好的应用程序中,您只需在入口点进行一些解析即可。大多数时候,“解析”的需要可以通过使用工厂来代替,工厂将被注册然后注入。

根据静态类测试代码会遇到很多麻烦。如果您真的想将容器注入到客户端类中,至少实例化并注入它,我建议您这样做,静态依赖关系很糟糕,更容易模拟单元测试。