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