C++中构造函数注入的高级配置

High Level Configuration of Constructor Injection in C++

本文关键字:高级 配置 注入 构造函数 C++      更新时间:2023-10-16

我的问题是专门处理通过构造函数的依赖项注入。我理解服务定位器模式、构造函数/setter注入的优缺点及其风格,但在选择纯构造函数注入后,我似乎无法克服一些问题。在阅读了许多可测试设计的材料后,包括仔细阅读Miško Hevery的博客(特别是这篇文章),我的处境如下:

假设我正在编写一个C++程序,并且我已经通过它们的构造函数正确地注入了我的依赖项。为了可读性,我给自己提供了一个高级对象,它有一个从main调用的Execute()函数:

int main(int argc, char* argv[]) {
    MyAwesomeProgramObject object(argc, argv);
    return object.Execute();
}

Execute()的职责是简单地连接所有需要的对象并启动最高级别的对象。最高级别的对象需要几个依赖项,而这些对象需要几个对象,依此类推,这意味着一个函数如下所示:

MyAwesomeProgramObject::Execute() {
    DependencyOne one;
    DependencyTwo two;
    DependencyThree three;
    MidLevelOne mid_one(one);
    MidLevelTwo mid_two(two, three);
    // ...
    MidLevelN mid_n(mid_dependencyI, mid_dependencyJ, mid_dependencyK);
    // ...
    HighLevelObject1 high_one(mid_one, mid_n);
    HighLevelObject2 high_two(mid_two);
    ProgramObject object(high_one, high_two);
    return object.Go();
}

根据我从Miško的博客中得到的信息(我会问他,但认为他没有时间回复我),这是满足依赖关系的纯构造函数注入的唯一方法。

在提到的博客文章中,他说我们应该在每个对象的生命周期级别上拥有工厂,但这本质上就是Execute正在做的,使我的代码看起来与他的示例完全相同:

AuditRecord audit = new AuditRecord();
Database database = new Database(audit);
Captcha captcha = new Captcha();
Authenticator authenticator =
    new Authenticator(database, captcha, audit);
LoginPage = new LoginPage(audit, authenticator);

问题:

  • 这是正确的方法吗
  • 这是我不知道的模式吗(似乎类似于Maven的context.xml)
  • 对于纯粹的构造函数注入,我只是承受"前期"分配的成本吗

请注意,您的不同示例是矛盾的。首先显示在堆栈上创建对象,最后一个示例分配对象。

堆栈上的对象有些危险,但在大多数情况下都很好。主要问题是,当另一个对象来自堆栈时,给它一个对象指针,这个对象的寿命比你的函数长。。。当这个长期存在的对象在函数返回后试图访问堆栈上的对象时,就会出现问题。如果所有对象都有一个堆栈生存期,那么您就可以了。

就我个人而言,我开始使用共享指针,我发现这是简化大量对象管理的终极方法。

std::shared_ptr<foo> foo_object(new foo);
std::shared_ptr<blah> foo_object(new blah(foo));

通过这种方式,blah可以永远保存foo共享指针的副本,并且一切都按预期工作,甚至在函数边界之间也是如此。不仅如此,共享指针在创建时为NULL,在删除时自动删除(当然,当最后一个共享指针被删除时)。您可以在不需要始终设置指针的对象中使用弱指针。。。

否则,我认为你试图做的事情在一定程度上是有效的。在我的世界里,事情往往是在以后创造的,所以我需要一个设定者。然而,构造函数注入对于强制用户正确初始化对象非常有用(即,我经常创建只读对象,没有setter,它们在构造时100%初始化,非常实用,非常安全!)