3、Nest.js 中的依賴注入與提供者

什么是依賴注入?

依賴注入(Dependency Injection,簡稱DI) 是實現 控制反轉(Inversion of Control,縮寫為IoC) 的一種常見方式。

那什么是控制反轉呢?

這里摘抄一下百科上的解釋,控制反轉,是面向對象編程中的一種設計原則,可以用來減低計算機代碼之間的耦合度。通過控制反轉,對象在被創建的時候,由一個調控系統內所有對象的外界實體,將其所依賴的對象的引用傳遞給它。也可以說,依賴被注入到對象。

現在說人話:
假設你是一個想開公司的富二代,開公司首先需要一間辦公室。那么你不用自己去買,你只需要在你的清單列表上寫上辦公室這么一項,OK! 你老爸已經派人給你安排好了辦公室,這間辦公室長什么樣?多大?在哪里?是租的?還是買的?你根本不知道,你也不需要知道。 現在你又在清單上寫了需要80臺辦公電腦,你老爸又給你安排好了80臺 Alienware, 你自己并不需要關心這些電腦是什么配置,買什么樣的CPU性價比更高,只要他們是能辦公的電腦就行了。

那么你的老爸就是所謂的 IoC 容器,你在編寫 Company 這個 class 的時候,你內部用到的 Office 、Computers
對象不需要你自己導入和實例化,你只需要在 Company 這個類的 Constructor (構造函數) 中聲明你需要的對象,IoC 容器會幫你把所依賴的對象實例注入進去。

代碼可能類似于下面這樣:

import { IOffice } from './interfaces/IOffice.interface';
import { IComputers } from './interfaces/IComputers.interface';

export class Company {
  constructor(
    private readonly office: IOffice,
    private readonly computers: IComputers[]
  ) {}
}

這里有必要解釋一下 readonly 關鍵字,它表示被修飾的對象只能在聲明的時候或構造函數中初始化。

Nest 中的依賴注入

Nest 就是建立在依賴注入這種設計模式之上的,所以它在框架內部封裝了一個IoC容器來管理所有的依賴關系。

在 MVC 設計模式中, Controller 只負責對請求的分發,并不處理實際的業務邏輯。所以我們的 UsersController 不應該自己處理對 用戶 的增、刪、改、查。 需要把這些工作交給 Service 層。

按照標準的 面向接口編程 我們在 UserController 構造函數里應該聲明實現 IUserService 接口的 UserService 對象,如下:

src/users/interfaces/user-service.interface.ts

import { User } from './user.interface';

export interface IUserService {

   findAll(): Promise<User[]>;

   findOne(id: number): Promise<User>;

   create();

   edit();

   remove();

}

src/users/users.controller.ts

import { Controller, Param, Get, Post, Delete, Put } from '@nestjs/common';
import { User } from './interfaces/user.interface';
import { IUsersService } from './interfaces/user-service.interface';

@Controller('users')
export class UsersController {

    constructor(private readonly usersService: IUsersService) {

    }

    @Get()
    async findAll(): Promise<User[]> {

        return await this.usersService.findAll();
    }

    @Get(':id')
    async findOne(@Param() params): Promise<User> {
        
        return await this.usersService.findOne(params.id);
    }

    @Post()
    async create() {
        return await this.usersService.create();
    }

    @Put()
    async edit() {
        return await this.usersService.edit();
    }

    @Delete()
    async remove() {
        return await this.usersService.remove();
    }
}

但是 TypeScript 只是一門轉譯語言, 最終生成并執行的是 JavaScript, 所以 TypeScript 的 Interface 會在轉譯成 Javascript 后去除,Nest 無法引用這些接口,在Nest中我們需要為IoC容器指定 提供者。

提供者(Provider)

一個提供者就是一個可以被IoC容器注入依賴關系的對象。

現在我們來新建一個提供者 UserService:

$ nest g s users/services/users

CREATE /src/users/services/users.service.spec.ts (449 bytes)
CREATE /src/users/services/users.service.ts (89 bytes)
UPDATE /src/app.module.ts (858 bytes)

生成的代碼如下:

src/users/services/users.service.ts

import { Injectable } from '@nestjs/common';

@Injectable()
export class UsersService  { }

在Nest中不僅僅只有 Service 能作為提供者,repository、 factory、 helper 等等,都可以作為提供者,也可以相互依賴。
所謂 提供者 不過就是一個簡單的 JavaScript 類, 然后使用 @Injectable 裝飾器修飾。

現在我們讓 UserService 實現 IUserService 接口,并給框架指定提供者:

src/users/services/user.service.ts

import { Injectable } from '@nestjs/common';
import { User } from '../interfaces/user.interface';
import { IUserService } from '../interfaces/user-service.interface';

@Injectable()
export class UsersService implements IUserService {
    async findAll(): Promise<User[]> {
        return [];
    }

    async findOne(id: number): Promise<User> {
        return {
            id,
            name: '小明',
            age: 18
        };
    }

    async create() {
        
    }

    async edit() {

    }

    async remove() {

    }
}

目前我們在 app.module.ts 中指定:

src/app.module.ts

import { Module } from '@nestjs/common';
import { AppController } from 'app.controller';
import { AppService } from 'app.service';
import { UsersController } from 'users/users.controller';
import { UsersService } from './users/services/users.service';

@Module({
  imports: [],
  controllers: [AppController, UsersController],
  providers: [AppService, UsersService],
})
export class AppModule { }

在 @Module 裝飾器中我們給 providers 數組新增一個 UsersService 提供者

如果你一直跟著前面的教程做,現在訪問 http://127.0.0.1:3000/users/33,會得到下面這樣預期的結果:

{"id":"33","name":"小明","age":18}

得益于依賴注入設計模式,假設我們現在想要更換 UserService 的實現, 我們只需要專注于修改這個提供者,然后對這個提供者進行單元測試。
如果能夠實現標準的面向接口編程一切將會變得更好,我們的程序耦合度將會更低。

上一篇:2、Nest.js 創建基本的REST控制器
下一篇:4、Nest.js 中的模塊化設計

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。