还是Angular官网讲的清楚
依赖注入 - ts - GUIDE
有一个函数,在实现的时候需要依赖别的方法一起作用,来实现功能,例如:
- function returnRandom () {
- return 1;
- }
-
- function returnUserName () {
- return 'TEST';
- }
-
- /** main函数依赖returnRandom和returnUserName来实现功能 */
- function main () {
- const random = returnRandom();
- const userName = returnUserName();
- return random + userName;
- }
-
- main();
此时main函数和returnRandom、returnUserName紧密耦合,当returnRandom需要传入一个参数时(即函数实现出现变更时)main函数的实现也需要变更,尤其是当这个参数需要从main接收再传入到returnRandom时,代码的变更变得更加复杂、容易出错。
- function returnRandom (param) {
- return 1 + param;
- }
-
- function returnUserName () {
- return 'TEST';
- }
-
- /** main函数依赖returnRandom和returnUserName来实现功能 */
- function main (param) {
- const random = returnRandom(param);
- const userName = returnUserName();
- return random + userName;
- }
-
- main(Math.random());
当returnRandom和returnUserName不在main函数内部实现,而是作为参数传入main函数时,main、returnRandom、returnUserName就解耦了,main的实现不再依赖returnRandom和returnUserName。
- function returnRandom () {
- return 1;
- }
-
- function returnUserName () {
- return 'TEST';
- }
-
- function main (random, userName) {
- return random + userName;
- }
-
- main(returnRandom(), returnUserName());
returnRandom和returnUserName的实现变更时,不会影响到main的实现。
- function returnRandom (param) {
- return 1 + param;
- }
-
- function returnUserName (nickName) {
- return 'TEST' + nickName;
- }
-
- function main (random, userName) {
- return random + userName;
- }
-
- main(returnRandom(Math.random()), returnUserName('nickName'));
这就是依赖注入,逻辑的实现有外部依赖时,将依赖通过外部传入的方式注入,而非在逻辑内部直接定义并使用外部依赖,这有助于代码的解耦和测试。为什么有助于测试呢?因为现在我们对它的依赖有了完全的控制权,我们可以直接创建一些mock数据测试逻辑。如果未使用依赖注入,我们可能需要使用一些难以维护的方式来测试逻辑(例如注释代码实现后填入mock数据,测试完成后再还原代码)。
- function returnRandom (param) {
- return 1 + param;
- }
-
- function returnUserName (nickName) {
- return 'TEST' + nickName;
- }
-
- function main (random, userName) {
- return random + userName;
- }
-
- /** 原代码实现 */
- main(returnRandom(Math.random()), returnUserName('nickName'));
-
- /** 当你需要测试main函数时 */
- const mockRandom = NaN;
- const userName = null;
- main(mockRandom, userName);
使用依赖注入后,代码优雅解耦,但是这对于编写和阅读代码的人来说就有点痛苦了,尤其是依赖多、依赖复杂的情况下,代码变得难以阅读。那我们能不能让依赖注入这件事自动完成?这就要使用控制反转了:借助于“第三方”实现具有依赖关系的对象之间的解耦。
简单来说就是把一些事交给框架来做,例如react、vue实现的更新机制,在数据变更时自动对真实dom进行最小化的更改,而没有控制反转的话,我们就需要使用vanilla js、jquery,在数据变更的同时,手动地查找dom、拼接数据、更新dom。
依赖注入也是可以如此,使用者只需要在代码中定义想要注入的依赖,由框架层完成依赖的创建、注入。具体实现不描述,很多库有示例:
- @Injectable
- class Food {}
-
- @Injectable
- class Bark {}
-
- /** Eat依赖Food */
- @Injectable
- class Eat {
- constructor (private food: Food) {}
- }
-
- /** Dog依赖Bark和Eat */
- @Injectable
- class Dog {
- /** 定义需要的依赖,由框架层自动注入 */
- constructor (private eat: Eat, private bark: Bark) {}
-
- wantSomeFood () {
- this.bark.call();
- }
- }