3

我正在学习设计模式和它周围的东西(特别是SOLID依赖倒置原则),看起来我失去了一些东西:

遵循DIP规则,我应该能够通过不在类中创建对象(组合)而是将对象引用/指针发送到类构造函数(聚合)来使类不那么脆弱。但这意味着我必须在其他地方创建一个实例:所以一个具有聚合的类越灵活,另一个就越脆弱。

请解释我哪里错了。

4

3 回答 3

5

您只需要按照这个想法得出合乎逻辑的结论。是的,您必须在其他地方创建实例,但这可能不仅仅是在您的类上方一级的类中,它需要不断地被推出和推出,直到仅在应用程序的最外层创建对象。

理想情况下,您在一个地方创建所有对象,这称为组合根(从工厂创建的对象除外,但工厂是在组合根中创建的)。确切的位置取决于您正在构建的应用程序的类型。

  • 在桌面应用程序中,这将在 Main 方法中(或非常接近)
  • 在 ASP.NET(包括 MVC)应用程序中,这将在 Global.asax
  • 在 WCF 中,这将在 ServiceHostFactory 中
  • 等等

这个地方最终可能会变得“脆弱”,但您只有一个地方可以更改内容以便能够重新配置您的应用程序,然后所有其他类都是可测试和可配置的。

看到这个优秀的答案(上面引用)

于 2015-02-24T21:21:58.523 回答
0

是的,您需要在某处实例化该类。如果您正确地遵循 DIP,您最终将在一个地方创建所有实例。我称这个地方为班级构成。阅读我的博客以更深入地了解此主题

于 2016-06-03T02:11:18.663 回答
0

您错过的一种重要可能性是注入工厂,而不是类本身。这样做的一个优点是它可以让您对实例的所有权更加清晰。请注意,代码有点难看,因为我明确地赋予了容器组件的所有权。shared_ptr如果您使用, 而不是,事情可能会更整洁一些unique_ptr,但是所有权是模棱两可的。

因此,从如下所示的代码开始:

struct MyContainer {
  std::unique_ptr<IFoo> foo;
  MyContainer() : foo(new Foo() ) {};
  void doit() { foo->doit(); }
}

void useContainer() {
   MyContainer container;
   container.doit();
}

非出厂版本看起来像这样

struct MyContainer {
  std::unique_ptr<IFoo> foo;
  template<typename T>
  explicit MyContainer(std::unique_ptr<T> && f) :
     foo(std::move(f))
  {}
  void doit() { foo->doit(); }
}

void useContainer() {
   std::unique_ptr<Foo> foo( new Foo());
   MyContainer container(std::move(foo));
   container.doit();
}

工厂版本看起来像

struct FooFactory : public IFooFactory {
  std::unique_ptr<IFoo> createFoo() override {
    return std::make_unique<Foo>();
  }
};

struct MyContainer {
  std::unique_ptr<IFoo> foo;
  MyContainer(IFooFactory & factory) : foo(factory.createFoo()) {};
  void doit() { foo->doit(); }
}

void useContainer() {
   FooFactory fooFactory;
   MyContainer container(fooFactory);
   container.doit();
}

IMO 明确 C++ 中的所有权和生命周期很重要——它是 C++ 身份的关键部分——它位于 RAII 和许多其他 C++ 模式的核心。

于 2016-06-03T02:41:22.233 回答