什么是IoC?在Nest中是如何实现的?

1,066 阅读4分钟

大家好,我是Ysh

什么是IoC

IoC 全称为 Inversion of Control, 控制反转 它是一种设计原则,负责将对象创建和依赖管理的控制权从应用程序代码内部转移到框架或容器当中。这样做的好处是可以减少代码之间的耦合,使得代码更灵活和易于维护。

为什么需要IoC

在传统的编程方式中,一个类B如果需要依赖另一个类A去完善逻辑,通常会自己创建类A的实例。这会导致代码紧密耦合,难以进行单元测试和维护。例如:

class Engine {
  start() {
    console.log("Engine started");
  }
}

class Car {
  constructor() {
    this.engine = new Engine(); // Car类自己创建了Engine的实例
  }

  start() {
    this.engine.start();
  }
}

const myCar = new Car();
myCar.start();

在上面的例子中,Car类自己创建了Engine的实例。如果我们想使用不同的引擎,就需要修改Car类创建实例部分的代码,这样会增加代码的复杂性,IoC解决了传统编程方式中的这个问题。

IoC的解决方法

通过使用IoC,我们可以将对象的创建和依赖关系的管理交给外部的IoC容器来处理,从而实现松耦合、高可用。

依赖注入(Dependency Injection)

依赖注入是实现IoC的一种常见方式,它将对象的依赖通过构造函数、属性或方法注入,而不是在对象内部创建。

依赖注入的示例

class Engine {
  start() {
    console.log("Engine started");
  }
}

class Car {
  constructor(engine) { // 自己声明的构造函数
    this.engine = engine; // 通过构造函数注入依赖
  }

  start() {
    this.engine.start();
  }
}

// 外部容器创建并注入依赖
const myEngine = new Engine();
const myCar = new Car(myEngine); // 将Engine实例注入到Car
myCar.start();

在这个例子中,Car类不再自己创建Engine实例,而是通过构造函数接收一个Engine实例。这样可以很容易地替换不同的引擎,而不需要修改Car类的代码。

使用Nest.js实现IoC

Nest.js是一个使用IoC的框架,它通过依赖注入来管理对象的创建和依赖关系。下面是一个详细的Nest.js示例:

1. 创建一个服务(Service)

//engine.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
class EngineService {
  start(): string {
    return 'Engine started';
  }
}

EngineService是一个服务类,使用@Injectable装饰器标记,表示它可以被注入到其他类中。

2. 创建一个控制器(Controller)

//car.controller.ts
import { Controller, Get } from '@nestjs/common';
import { EngineService } from './engine.service'; // 导入服务

@Controller('car')
class CarController {
  constructor(private readonly engineService: EngineService) {} // 依赖注入

  @Get('start')
  start(): string {
    return this.engineService.start(); // 使用注入的服务
  }
}

CarController是一个控制器类,使用@Controller装饰器标记。构造函数通过依赖注入接收EngineService实例,@Get('start')装饰器标记的方法处理GET /car/start请求。

3. 创建一个模块(Module)

//app.module.ts
import { Module } from '@nestjs/common';
import { CarController } from './car.controller'; // 导入控制器
import { EngineService } from './engine.service'; // 导入服务

@Module({
  controllers: [CarController],
  providers: [EngineService],
})
class AppModule {}

AppModule是一个模块类,使用@Module装饰器标记,定义了包含的控制器和服务。

4. 运行应用

//main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap();

这个代码创建并启动Nest.js应用,监听3000端口。

完整代码示例

// engine.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class EngineService {
  start(): string {
    return 'Engine started';
  }
}

// car.controller.ts
import { Controller, Get } from '@nestjs/common';
import { EngineService } from './engine.service';

@Controller('car')
export class CarController {
  constructor(private readonly engineService: EngineService) {}

  @Get('start')
  start(): string {
    return this.engineService.start();
  }
}

// app.module.ts
import { Module } from '@nestjs/common';
import { CarController } from './car.controller';
import { EngineService } from './engine.service';

@Module({
  controllers: [CarController],
  providers: [EngineService],
})
export class AppModule {}

// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap();

解释

  1. EngineService:这是一个服务类,包含业务逻辑,使用@Injectable装饰器标记。

  2. CarController:这是一个控制器类,负责处理HTTP请求,通过构造函数注入EngineService实例。

  3. AppModule:这是一个模块类,包含控制器和服务,使用@Module装饰器定义模块的元数据。

  4. main.ts:应用的入口文件,创建并启动Nest.js应用。

通过这种方式,Nest.js的IoC容器自动管理对象的创建和依赖关系,使得代码更加灵活、易于测试和维护,这也是Nest在实际使用时的标准开发写法。

总结

IoC是一个很好的将对象创建和依赖管理的控制权从应用程序代码转移到框架或容器的方案,Java的Spring框架与Node的Nest框架都使用IoC解决了对象间高耦合低可用的问题。