问题 角度依赖注入实现,为什么?


如果有人说的话,请原谅我不理解这一点。

为什么它的Dependency Injector的Angular实现通过a使用注入 constructor

我习惯看到一个 DI 以各种方式。即使是一种静态方法也是有意义的(如果存在对不起我还没挖到那么深,我到目前为止已经进入了一周)。

以这种方式使用它会不会更容易或更合乎逻辑,更类似于我们经常看到但仍然在构造函数中传递它的DI?

// Non-Angular Example
@Component({})
class FooComponent {
  public appState: AppState;

  constructor(DI: DependencyInjector) {
    this.appState = DI.get('AppState'); 
  }

  ngOnInit() {}
}

Angular更像是这样,我不确定它是仅仅是为了冗长,还是有其他原因。

// Angular 2/4 Example 
@Component({})
class BarComponent {
  public appState: AppState;

  constructor(appState: AppState, 
              router: Router,
              etc: EtcSomething) {

  }
  ngOnInit() {}

我知道谷歌已经想到了这一点,我只是想了解推理和/或好处。也许我醒来时想到了愚蠢的事情,这很明显,只是我的头脑,但我错过了。

我希望我所要求的是有道理的,我只是想知道为什么。


1969
2017-11-01 12:48


起源

因为那不再是依赖注入了。依赖注入的原则是将依赖注入到组件中。 BarComponent的依赖关系是路由器,appState等。唯一注入的东西是注入器,它将充当工厂。它不再是DI,并且让事情更难以测试。我不确定你在哪里看到一个依赖注入框架,它不会注入除注入器以外的任何东西。 - JB Nizet
你可以注入注入器,然后用它来手动注入,但这会掩盖依赖关系并使得打字稿编译器不那么有用,所以你只是毫无理由地自己在脚下射击。 - bryan60
谢谢大家,有人想把这些评论之一作为答案,所以我可以标记它吗?你能澄清一下这是否是针对Angular Specific的DI,而不是一般的DI?任何语言的一般DI都与我的经历大不相同;但是,这是一种原型语言,所以这可能有点不同。我并不反对它的使用,我试图理解其目的。 :) - JREAM
@JBNizet据我所知(虽然可能是错的),在Angular之外,第一个例子仍然被认为是依赖注入。我认为Angular的受欢迎程度让人们认为所有DI都像Angular DI。参考: stackoverflow.com/a/130862/846550 和那个问题的其他答案。 - Sideshow Bob
至于OP的第一个DI示例,其优点是不需要对语言功能进行任何修改,也不需要引入神奇的保留参数名称。我能看到的唯一缺点是更多的样板(并且可以隐藏更多内部DI而不是OP)。所以我也很好奇为什么Angular会这样做;这是一个很好的问题。 - Sideshow Bob


答案:


以这种方式使用它会不会更容易或更合乎逻辑,更类似于我们经常看到但仍然在构造函数中传递它的DI?

您的示例中的模式的插图实际上称为a 服务定位器模式。许多人认为这种模式是一种反模式。

服务定位器模式

优点

  • “服务定位器”可以充当简单的运行时链接器。这允许在运行时添加代码而无需重新编译应用程序,在某些情况下甚至无需重新启动它。
  • 应用程序可以通过有选择地添加和删除服务定位器中的项目来在运行时优化自身。例如,应用程序可以检测到它有一个更好的库来读取可用的JPG图像而不是默认图像,并相应地更改注册表。
  • 库或应用程序的大部分可以完全分开。它们之间的唯一联系就是注册表。

缺点

  • 放在注册表中的内容实际上是关于系统其余部分的黑盒子。这使得检测和恢复错误变得更加困难,并且可能使整个系统的可靠性降低。
  • 注册表必须是唯一的,这可能使其成为并发应用程序的瓶颈。
  • 注册表可能是一个严重的安全漏洞,因为它允许外人将代码注入应用程序。
  • 注册表隐藏了类的依赖项,在缺少依赖项时导致运行时错误而不是编译时错误。
  • 注册表使代码更难维护(与使用依赖注入相反),因为不清楚何时引入重大更改。
  • 注册表使代码更难测试,因为所有测试都需要与同一个全局服务定位器类交互以设置被测试类的伪依赖性。但是,通过使用单个服务定位器接口注入应用程序类可以轻松克服这一问题。

依赖注入

当前使用的(首选)模式 angular (以及其他框架)。

优点

  • 依赖注入允许客户端灵活地进行配置。只修复了客户端的行为。客户端可以对支持客户期望的内部接口的任何事情采取行动。
  • 依赖注入可用于将系统的配置详细信息外部化为配置文件,从而允许在不重新编译的情况下重新配置系统。可以针对需要不同组件实现的不同情况编写单独的配置。这包括但不限于测试。
  • 因为依赖注入不需要对代码行为进行任何更改,所以它可以作为重构应用于遗留代码。结果是客户端更加独立,并且使用存根或模拟对象模拟其他未测试的对象,更容易单独进行单元测试。这种易用性测试通常是使用依赖注入时注意到的第一个好处。
  • 依赖注入允许客户端删除它需要使用的具体实现的所有知识。这有助于将客户端与设计更改和缺陷的影响隔离开来。它提高了可重用性,可测试性和可维护性。
  • 减少应用程序对象中的样板代码,因为初始化或设置依赖项的所有工作都由提供程序组件处理。
  • 依赖注入允许并发或独立开发。两个开发人员可以独立开发彼此使用的类,而只需要知道类将通过的接口。插件通常由第三方商店开发,甚至从未与创建使用插件的产品的开发人员交谈。
  • 依赖注入减少了类与其依赖关系之间的耦合。

缺点

  • 依赖注入创建客户端,要求配置细节由构造代码提供。当明显的默认值可用时,这可能很麻烦。
  • 依赖注入可能使代码难以跟踪(读取),因为它将行为与构造分开。这意味着开发人员必须引用更多文件来跟踪系统的执行情况。
  • 依赖注入通常需要更多的前期开发工作,因为人们无法在需要的时间和地点召唤出正确的东西,但必须要求它被注入,然后确保它已被注入。
  • 依赖注入迫使复杂性从类中移出并进入类之间的链接,这可能并不总是令人满意或易于管理。
  • 具有讽刺意味的是,依赖注入可以鼓励依赖于依赖注入框架。

您可以找到有关这些模式的文章,书籍,教程和其他信息。虽然这是一个偏好的问题,但是有一种共识是偏好的 依赖注入 过度 服务定位器模式

关于这两者之间的差异还有其他类似问题,如下所示: 依赖注入和服务定位器模式之间有什么区别?


8
2018-03-09 16:43



对于任何指定良好的接口,不仅仅是DI?:“两个开发人员可以独立开发彼此使用的类,而只需要知道类将通过的接口进行通信。” - Sideshow Bob


构造函数注入是实现依赖注入的最简单,最安全的方法。

即使这对于用户来说并不总是最方便的,因为它具有某些限制,例如不支持循环依赖,并且超类需要从子类转发所有参数。

每隔一种方式都会让人很难知道确切的依赖关系何时可用并且可以访问。 在注入依赖项之后,将需要额外的生命周期回调。

使用继承进行对象创建非常困难,因此类是从内部(超类)out(子类)构造的,这样每个级别都有一个正确初始化的状态,其中有几个前提条件保持,就像没有子类在超类构造函数之前访问成员一样没完成。 例如,Typescript(ES6)强制执行调用 super() 在构造函数中,如果超类中存在非泛型构造函数。

如果子类中存在构造函数,则需要在使用“this”之前先调用super()。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes


2
2018-03-09 16:58





// Non-Angular Example

@Component({})
class FooComponent {
  public appState: AppState;

  constructor(DI: DependencyInjector) {
    this.appState = DI.get('AppState'); 
  }

  ngOnInit() {}
}

您可以使用 注射器 在您的构造函数中并手动注入您需要的内容:

@Component({})
class FooComponent {
  public appState: AppState;

  constructor(private injector: Injector) {
    this.appState = injector.get(AppState); 
  }

  ngOnInit() {}
}

但在这种情况下,组件执行注射器的工作,这在大多数情况下是不合理的。


1
2018-03-08 11:59





AngularJS中的依赖注入非常有用,是制作易于测试的组件的关键。本文解释了Angular的依赖注入系统是如何工作的。

提供者($提供) $ provide服务负责告诉Angular如何创建新的可注射物品;这些东西叫做服务。服务由称为提供程序的东西定义,这是您在使用$ provide时创建的。定义提供程序是通过$ provide服务上的提供程序方法完成的,您可以通过要求将其注入应用程序的配置函数来获取$ provide服务。我们可以将一个名为greeting的变量注入任何可注入函数(比如控制器,稍后会详细介绍),Angular将调用提供者的$ get函数以返回服务的新实例。


1
2018-03-13 06:43