如果有人说的话,请原谅我不理解这一点。
为什么它的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() {}
我知道谷歌已经想到了这一点,我只是想了解推理和/或好处。也许我醒来时想到了愚蠢的事情,这很明显,只是我的头脑,但我错过了。
我希望我所要求的是有道理的,我只是想知道为什么。
以这种方式使用它会不会更容易或更合乎逻辑,更类似于我们经常看到但仍然在构造函数中传递它的DI?
您的示例中的模式的插图实际上称为a 服务定位器模式。许多人认为这种模式是一种反模式。
优点
- “服务定位器”可以充当简单的运行时链接器。这允许在运行时添加代码而无需重新编译应用程序,在某些情况下甚至无需重新启动它。
- 应用程序可以通过有选择地添加和删除服务定位器中的项目来在运行时优化自身。例如,应用程序可以检测到它有一个更好的库来读取可用的JPG图像而不是默认图像,并相应地更改注册表。
- 库或应用程序的大部分可以完全分开。它们之间的唯一联系就是注册表。
缺点
- 放在注册表中的内容实际上是关于系统其余部分的黑盒子。这使得检测和恢复错误变得更加困难,并且可能使整个系统的可靠性降低。
- 注册表必须是唯一的,这可能使其成为并发应用程序的瓶颈。
- 注册表可能是一个严重的安全漏洞,因为它允许外人将代码注入应用程序。
- 注册表隐藏了类的依赖项,在缺少依赖项时导致运行时错误而不是编译时错误。
- 注册表使代码更难维护(与使用依赖注入相反),因为不清楚何时引入重大更改。
- 注册表使代码更难测试,因为所有测试都需要与同一个全局服务定位器类交互以设置被测试类的伪依赖性。但是,通过使用单个服务定位器接口注入应用程序类可以轻松克服这一问题。
当前使用的(首选)模式 angular
(以及其他框架)。
优点
- 依赖注入允许客户端灵活地进行配置。只修复了客户端的行为。客户端可以对支持客户期望的内部接口的任何事情采取行动。
- 依赖注入可用于将系统的配置详细信息外部化为配置文件,从而允许在不重新编译的情况下重新配置系统。可以针对需要不同组件实现的不同情况编写单独的配置。这包括但不限于测试。
- 因为依赖注入不需要对代码行为进行任何更改,所以它可以作为重构应用于遗留代码。结果是客户端更加独立,并且使用存根或模拟对象模拟其他未测试的对象,更容易单独进行单元测试。这种易用性测试通常是使用依赖注入时注意到的第一个好处。
- 依赖注入允许客户端删除它需要使用的具体实现的所有知识。这有助于将客户端与设计更改和缺陷的影响隔离开来。它提高了可重用性,可测试性和可维护性。
- 减少应用程序对象中的样板代码,因为初始化或设置依赖项的所有工作都由提供程序组件处理。
- 依赖注入允许并发或独立开发。两个开发人员可以独立开发彼此使用的类,而只需要知道类将通过的接口。插件通常由第三方商店开发,甚至从未与创建使用插件的产品的开发人员交谈。
- 依赖注入减少了类与其依赖关系之间的耦合。
缺点
- 依赖注入创建客户端,要求配置细节由构造代码提供。当明显的默认值可用时,这可能很麻烦。
- 依赖注入可能使代码难以跟踪(读取),因为它将行为与构造分开。这意味着开发人员必须引用更多文件来跟踪系统的执行情况。
- 依赖注入通常需要更多的前期开发工作,因为人们无法在需要的时间和地点召唤出正确的东西,但必须要求它被注入,然后确保它已被注入。
- 依赖注入迫使复杂性从类中移出并进入类之间的链接,这可能并不总是令人满意或易于管理。
- 具有讽刺意味的是,依赖注入可以鼓励依赖于依赖注入框架。
您可以找到有关这些模式的文章,书籍,教程和其他信息。虽然这是一个偏好的问题,但是有一种共识是偏好的 依赖注入 过度 服务定位器模式。
关于这两者之间的差异还有其他类似问题,如下所示: 依赖注入和服务定位器模式之间有什么区别?。
构造函数注入是实现依赖注入的最简单,最安全的方法。
即使这对于用户来说并不总是最方便的,因为它具有某些限制,例如不支持循环依赖,并且超类需要从子类转发所有参数。
每隔一种方式都会让人很难知道确切的依赖关系何时可用并且可以访问。
在注入依赖项之后,将需要额外的生命周期回调。
使用继承进行对象创建非常困难,因此类是从内部(超类)out(子类)构造的,这样每个级别都有一个正确初始化的状态,其中有几个前提条件保持,就像没有子类在超类构造函数之前访问成员一样没完成。
例如,Typescript(ES6)强制执行调用 super()
在构造函数中,如果超类中存在非泛型构造函数。
如果子类中存在构造函数,则需要在使用“this”之前先调用super()。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
// 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() {}
}
但在这种情况下,组件执行注射器的工作,这在大多数情况下是不合理的。
AngularJS中的依赖注入非常有用,是制作易于测试的组件的关键。本文解释了Angular的依赖注入系统是如何工作的。
提供者($提供)
$ provide服务负责告诉Angular如何创建新的可注射物品;这些东西叫做服务。服务由称为提供程序的东西定义,这是您在使用$ provide时创建的。定义提供程序是通过$ provide服务上的提供程序方法完成的,您可以通过要求将其注入应用程序的配置函数来获取$ provide服务。我们可以将一个名为greeting的变量注入任何可注入函数(比如控制器,稍后会详细介绍),Angular将调用提供者的$ get函数以返回服务的新实例。