ioc控制反转_IoC && DI(控制反转和依赖注入)

77d1e8827ced3ad72397267dd0a8589e.png

IoC?

IoC 英文全称为 Inversion of Control,即 控制反转。控制反转是面向对象编程中的一种原则,用于降低代码之间的耦合度。传统应用程序都是在类的内部主动创建依赖对象,这样将导致类与类之间耦合度非常高,并且不容易测试。有了 IoC 容器之后,可以将创建和查找依赖对象的控制权交给了容器,这样对象与对象之间就是松散耦合了,方便测试与功能复用,整个程序的架构体系也会变得非常灵活。

DI?

DI 英文全称为 Dependency Injection,即 依赖注入。依赖注入是控制反转最常见的一种应用方式,即通过控制反转,在对象创建的时候,自动注入一些依赖对象。

早先接触到上述概念的时候还是在spring中,框架利用注解(@)实现依赖注入和控制反转,当然在Nestjsmidway中我们也能看到,一方面省去了大量的代码实现,另一方面也让各功能逻辑实现了解耦,最重要的是,省去了一系列的实例化及各种绑定代码写起来真的很爽,如下:

// spring
@Controller
public class TestController {
    @RequestMapping("/test")
    public String test(Map<String,Object> map){
        return "hello";
    }
}

//Nestjs
import { Controller, Get } from '@nestjs/common';
​
@Controller('cats')
export class CatsController {
  @Get()
  findAll(): string {
    return 'This action returns all cats';
  }
}

//midway
import { Context, inject, controller, get, provide } from 'midway';
​
@provide()
@controller('/test')
export class TestController {
  @inject()
  ctx: Context;
  @get('/')
  async index() {
    this.ctx.body = `test`;
  }
}

想像这样一个场景, A 被 B 和 C 所依赖,而且在构造器需要进行实例化操作,这样的依赖关系在测试中会非常麻烦。这个依赖,一般被叫做 "耦合",而耦合度过高的系统,必然会出现牵一发而动全身的情形。

import {A} from './A';
import {B} from './B';
class C {
  consturctor() {
    this.a = new A();
    this.b = new B(this.a);
  }
}

如果使用一个容器来管理我们的模块,这样模块之间的耦合性就降低了,如下:

// injection 是midway官方实现的一个包
import {Container} from 'injection';
import {A} from './A';
import {B} from './B';
const container = new Container();
container.bind(A);
container.bind(B);
​
class C {
  consturctor() {
    this.a = container.get('A');
    this.b = container.get('B');
  }
}
// container 就是 IoC 容器,是依赖注入这种设计模式的一种实现,使得 C 和 A, B 没有了强耦合关系
IoC 容器就像是一个对象池,管理这每个对象实例的信息(Class Definition),所以用户无需关心什么时候创建,当用户希望拿到对象的实例 (Object Instance) 时,可以直接拿到实例,容器会 自动将所有依赖的对象都自动实例化

对象A获得依赖对象B的过程,获得依赖对象的过程被反转了,当对象A运行到需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方,对象的主动行为变为了被动行为,控制权颠倒过来了,这也是“控制反转”这个名称的由来,封面图也形象的表达了这个行为,甭管是求职者还是招聘者,需要什么信息平台直接给到

下面两张图也阐述了这个过程:

d71265d6b73da8bee46db48d1d6aa31e.png

11dd75e8b02b374db3581c301405f62d.png

再来看一段代码,如下:

import { Component, NgZone, OnInit } from '@angular/core';
import { SelfService } from './self.service.ts';
​
@Component({
  selector: 'app-component-test',
  templateUrl: './component-test.component.html',
  styleUrls: ['./component-test.component.scss']
})
export class TestComponent implements OnInit {
​
  constructor(
    private ngZone: NgZone,
    private selfService: SelfService 
  ) {}
  
  ngOnInit(): void {
    this.update()
  }
​
  update (): void {
    this.ngZone.run( _ => {
      console.log('update')
    })
  }
}

如果您使用过angular 2及以上的版本,您肯定见过上述代码,在angular中用 服务与依赖注入来实现各个模块、功能逻辑的解耦

private selfService: SelfService

这段代码就是依赖注入的一种形式,这样我们就能在TestComponent这个类中访问到注入的 NgZone 及自定义的 selfServiceAngular在底层使用了IoC设计模式,并利用TypeScript强大的装饰器特性,完成了依赖注入,最大化重用各功能模块,降低代码之间的耦合度

关于IoCDI之间的关系也有人说,DI就是IoC,我更同意 DIIoC 的一种实现,因为还有一种实现叫DL,依赖查找,用户自己去是使用 API 进行查找资源和组装对象,有侵入性,已经被抛弃

动手实现一个简单的DI

tip:在typerscript中,类可以这样简写
class Parent { 
  private name: string;
  constructor( name: string ){ 
    this.name = name 
  } 
}
//简写形式如下
class Parennt { 
  constructor( private name: string ){} 
}

前置知识点:

装饰器​www.typescriptlang.org reflect-metadata​rbuckton.github.io

实现:

import 'reflect-metadata';

type Constructor <T = any> = new (...args: any[]) => T;
class LogService {
  log(args):void {
    console.log(...args)
  }
}
class WarnService {
  warn(args):void {
    console.warn(...args)
  }
}
class ErrorService {
  error(args):void {
    console.error(...args)
  }
}
function enject(): ClassDecorator {
  return target => {}
}
@enject()
class Demo {
  constructor(
    private logService: LogService,
    private warnService: WarnService,
    private errorService: ErrorService
  ) {
    this.test()
  }
  test () {
    this.logService.log('-----log-----')
    this.warnService.warn('-----warn-----')
    this.errorService.error('-----error-----')
  }
}

// IOC容器
function EnjectFactory<T>(target: Constructor<T>): T {
  const providers = Reflect.getMetadata('design:paramtypes', target)
  const args = providers.map((Provider:Constructor) => new Provider())
  return new target(...args)
}
EnjectFactory(Demo)

// 成功打印,即已现实自动注入依赖的实例,当然大家也可以自行实现其他形式的依赖注入,例如:路由、属性等
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值