简体   繁体   English

C ++中的依赖注入

[英]Dependency injection in C++

This is also a question that I asked in a comment in one of Miško Hevery's google talks that was dealing with dependency injection but it got buried in the comments. 这也是我在MiškoHevery关于依赖注入处理谷歌会谈的评论中提出的一个问题,但它已被埋没在评论中。

I wonder how can the factory / builder step of wiring the dependencies together can work in C++. 我想知道将依赖关系连接在一起的工厂/构建器步骤如何才能在C ++中工作。

Ie we have a class A that depends on B. The builder will allocate B in the heap, pass a pointer to B in A's constructor while also allocating in the heap and return a pointer to A. 即我们有一个依赖于B的A类。构建器将在堆中分配B,在A的构造函数中传递指向B的指针,同时在堆中分配并返回指向A的指针。

Who cleans up afterwards? 谁事后清理? Is it good to let the builder clean up after it's done? 建造者在完成后清理它是否好? It seems to be the correct method since in the talk it says that the builder should setup objects that are expected to have the same lifetime or at least the dependencies have longer lifetime (I also have a question on that). 它似乎是正确的方法,因为在谈话中它说构建器应该设置预期具有相同生命周期的对象,或者至少依赖关系具有更长的生命周期(我也有一个问题)。 What I mean in code: 我在代码中的意思是:

class builder {
public:
    builder() :
        m_ClassA(NULL),m_ClassB(NULL) {
    }
    ~builder() {
        if (m_ClassB) {
            delete m_ClassB;
        }
        if (m_ClassA) {
            delete m_ClassA;
        }
    }
    ClassA *build() {
        m_ClassB = new class B;
        m_ClassA = new class A(m_ClassB);
        return m_ClassA;
    }
};

Now if there is a dependency that is expected to last longer than the lifetime of the object we are injecting it into (say ClassC is that dependency) I understand that we should change the build method to something like: 现在,如果有一个依赖项预计持续时间超过我们注入的对象的生命周期(比如ClassC就是依赖项)我明白我们应该将构建方法改为:

ClassA *builder::build(ClassC *classC) {
    m_ClassB = new class B;
    m_ClassA = new class A(m_ClassB, classC);
    return m_ClassA;
}

What is your preferred approach? 你最喜欢的方法是什么?

This talk is about Java and dependency injection. 这个讲座是关于Java和依赖注入的。

In C++ we try NOT to pass RAW pointers around. 在C ++中,我们尽量传递RAW指针。 This is because a RAW pointer have no ownership semantics associated with it. 这是因为RAW指针没有与之关联的所有权语义。 If you have no ownership then we don't know who is responsible for cleaning up the object. 如果您没有所有权,那么我们不知道谁负责清理对象。

I find that most of the time dependency injection is done via references in C++. 我发现大部分时间依赖注入是通过C ++中的引用完成的。
In the rare cases where you must use pointers, wrap them in std::unique_ptr<> or std::shared_ptr<> depending on how you want to manage ownership. 在极少数情况下,必须使用指针,将它们包装在std :: unique_ptr <>std :: shared_ptr <>中,具体取决于您希望如何管理所有权。
In case you cannot use C++11 features, use std::auto_ptr<> or boost::shared_ptr<>. 如果您不能使用C ++ 11功能,请使用std :: auto_ptr <>或boost :: shared_ptr <>。

I would also point out that C++ and Java styles of programming are now so divergent that applying the style of one language to the other will inevitably lead to disaster. 我还要指出,C ++和Java编程风格现在如此不同,以至于将一种语言的风格应用于另一种语言将不可避免地导致灾难。

This is interesting, DI in C++ using templates: 这很有意思,DI在C ++中使用模板:

http://adam.younglogic.com/?p=146 http://adam.younglogic.com/?p=146

I think the author is making the right moves as to not translate Java DI into C++ too literally. 我认为作者正在做出正确的举动,以至于没有将Java DI翻译成C ++。 Worth the read. 值得一读。

I've recently been bitten by the DI bug. 我最近被DI虫咬了。 I think it solves a lot of complexity problems, especially the automated part. 我认为它解决了很多复杂性问题,特别是自动化部分。 I've written a prototype which lets you use DI in a pretty C++ way, or at least I think so. 我写了一个原型,让你以漂亮的C ++方式使用DI,或者至少我是这么认为的。 You can take a look at the code example here: http://codepad.org/GpOujZ79 您可以在这里查看代码示例: http//codepad.org/GpOujZ79

The things that are obviously missing: no scoping, no binding of interface to implementation. 显然缺少的东西:没有范围,没有绑定到实现的接口。 The latter is pretty easy to solve, the former, I've no idea. 后者很容易解决,前者,我不知道。

I'd be grateful if anyone here has an opinion on the code. 如果有人对代码有任何意见,我将不胜感激。

Use RAII. 使用RAII。

Handing a raw pointer to someone is the same as handing them ownership. 将原始指针交给某人与交给他们所有权相同。 If that's not what you want to do, you should give them some kind of facade that also knows how to clean up the object in question. 如果这不是你想要做的,你应该给他们某种外观,也知道如何清理有问题的对象。

shared_ptr<> can do this; shared_ptr <>可以做到这一点; the second argument of its constructor can be a function object that knows how to delete the object. 其构造函数的第二个参数可以是一个知道如何删除对象的函数对象。

Based on my own experience, it is best to have clear ownership rules. 根据我自己的经验,最好有明确的所有权规则。 For small concrete objects, it is best to use direct copy to avoid cross dependency. 对于小型具体对象,最好使用直接复制以避免交叉依赖。

Sometimes cross dependency is unavoidable, and there is no clear ownership. 有时交叉依赖是不可避免的,并且没有明确的所有权。 For example, (m) A instances own (n) B instances, and certain B instances can be owned by multiple As. 例如,(m)A个实例拥有(n)个B实例,某些B实例可以由多个As拥有。 In this case, the best approach is to apply reference counting to B, in the way similar to COM reference counting. 在这种情况下,最好的方法是将引用计数应用于B,类似于COM引用计数。 Any functions that take possession of B* must increase reference count first, and decrease it when releasing the possession. 任何占有B *的函数必须首先增加引用计数,并在释放占有权时减少它。

I also avoid using boost::shared_ptr as it creates a new type (shared_ptr and B* become two distinct types). 我也避免使用boost :: shared_ptr,因为它创建了一个新类型(shared_ptr和B *成为两种不同的类型)。 I found that it brings more headaches when I add methods. 我发现添加方法时会带来更多麻烦。

Things get complicated if you don't settle on the question of ownership once and for all. 如果你不能一劳永逸地解决所有权问题,情况会变得复杂。 You will simply have to decide in your implementation if it's possible that dependencies live longer than the objects they are injected into. 您只需要在实现中决定依赖项是否可能比它们注入的对象的寿命更长。

Personally I'd say no: the object into which the dependency is injected will clean up afterwards. 我个人会说不:注入依赖关系的对象将在之后清理。 Trying to do it through the builder means that the builder will have to live longer than both the dependency and the object into which it is injected. 尝试通过构建器执行此操作意味着构建器必须比依赖项和注入它的对象更长寿。 This causes more problems than it solves, in my opinion, because the builder does not serve any more useful purpose after the construction with the dependency injection has been completed. 在我看来,这导致了比它解决的问题更多的问题,因为在完成依赖注入的构造之后,构建器不能用于任何更有用的目的。

In C++, normally, when you done things right, you don't need to write destructors at all in most cases. 在C ++中,通常情况下,当你做正确的事情时,在大多数情况下你根本不需要编写析构函数。 You should use smart pointers to delete things automatically. 您应该使用智能指针自动删除内容。 I think, builder don't looks like the owner of the ClassA and ClassB instances. 我认为,构建器看起来不像ClassA和ClassB实例的所有者。 If you don't like to use smart pointers, you should think about objects life time and their owners. 如果您不喜欢使用智能指针,您应该考虑对象的生命周期及其所有者。

You can also check the FFEAD Dependency Injection . 您还可以检查FFEAD依赖注入 It provides DI on the lines of Spring for JAVA and has a non-obtrusive way of dealing with things. 它为Spring提供了针对JAVA的DI,并且具有非突兀的处理方式。 It also has a lot of other important features like in-built AJAX Support,Reflection,Serialization,C++ Interpreter,Business Components For C++,ORM,Messaging,Web-Services,Thread-Pools and an Application Server that supports all these features. 它还有许多其他重要功能,如内置的AJAX支持,反射,序列化,C ++解释器,C ++业务组件,ORM,消息传递,Web服务,线程池和支持所有这些功能的应用服务器

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM