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

时间:2015-03-31 09:24:40

标签: c# c++ dependency-injection inversion-of-control loose-coupling

C#使用 依赖注入(DI) 来获得 无损 可测试的 平台。为此,我需要 interface ,可能需要 DI 控制反转(IoC)容器来解析我的实例。< / p>

但是你如何在C ++中做到这一点?我已经对此有所了解了,而且C ++中的依赖注入似乎并不像C#。在C ++中,您使用对象的引用 - 这是在C ++中使用DI的方法,对吧?

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

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

6 个答案:

答案 0 :(得分:22)

  

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

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

  

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

没有;这不是 使用DI的方式,这是 在C ++中使用DI的方式。

还要考虑:

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

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

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

  

这在C ++中是否可行?

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

  

您是在使用依赖注入还是在C ++中调用它?

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

  

如果是,您如何使用它?

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

答案 1 :(得分:14)

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

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

易peasy。

答案 2 :(得分:8)

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

ServiceLocator

注意,虽然它被称为ServiceLocator(因为它不依赖于Dependancy Injection),如果你使用lambda函数绑定,最好是ServiceLocator :: Module类,你会得到注入(不是基于反射),它真的很好用(IMO)< / p>

#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;
}

答案 3 :(得分:4)

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

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

  

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

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

  

是否有类似容器的东西我可以解决所有这些问题   参考?在C#中我有一个“糟糕的类/糟糕的项目/组装”   将我的所有实例注册到静态容器

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

答案 4 :(得分:1)

  

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

这不是DI应该如何使用,你不会将你的容器传递给你所有的“消费者”类。在一个设计良好的应用程序中,您只需在入口点解决几个问题就可以解决问题。大多数情况下,“解决”的需要可以通过使用工厂来取代,工厂将被注册然后注入。

根据静态类测试代码会遇到很多麻烦。我建议如果你真的想在你的客户端类中注入你的容器到至少实例并注入它,静态依赖是地狱,更容易模拟单元测试。

答案 5 :(得分:0)

这是一篇旧帖子,但我用 C++ 编写了一个 di 容器的示例实现。也许你有兴趣。

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