控制反转IOC 依赖注入DI
控制反转IOC:高层模块不应该依赖底层模块,二者都应该依赖其抽象,抽象不应该依赖细节,细节应该依赖抽象
依赖注入DI:和IOC是同根生,两个本来是一个东西,只不过由于控制反转概念比较含糊,(可能只是理解为容器控制对象,很难让人想到谁来维护对象关系),2004年Martin Fowler又给出了一个新的名字:依赖注入,类A依赖类B的常规表现是在A中使用B的实例。
依赖注入就是将B的实例注入到A中,这样A就不需要知道如何创建B的实例了,而是由外部来决定B的实例,这样就降低了类之间的耦合度,也降低了类之间的依赖关系,所以依赖注入是控制反转的一种实现方式,而控制反转是一种设计模式,依赖注入是一种实现方式
- 未使用控制反转、依赖注入的代码:
typescript
⭕️未使用
class A {
name: string
constructor(name: string) {
this.name = name
}
}
class B {
age:number
entity:A
constructor (age:number) {
this.age = age;
this.entity = new A('小满')
}
}
const c = new B(18)
c.entity.name
B 中代码的实现是需要依赖 A 的,两者的代码耦合度非常高。 当两者之间的业务逻辑复杂程度增加的情况下,维护成本与代码可读性都会随着增加,并且很难再多引入额外的模块进行功能拓展。
- 使用IOC容器依赖注入
typescript
class A1 {
name: string
constructor(name: string) {
this.name = name
}
}
class C1 {
name: string
constructor(name: string) {
this.name = name
}
}
//中间件用于解耦
class Container {
modeuls: any
constructor() {
this.modeuls = {}
}
provide(key: string, modeuls: any) {
this.modeuls[key] = modeuls
}
get(key) {
return this.modeuls[key]
}
}
const mo = new Container()
mo.provide('a', new A1('小满1'))
mo.provide('c', new C1('小满2'))
class B1 {
a: any
c: any
constructor(container: Container) {
this.a = container.get('a')
this.c = container.get('c')
}
}
new B1(mo)
在引入IoC容器container之后,B与A的代码逻辑已经解耦,可以单独拓展其他功能,也可以方便地加入其他模块C。所以在面对复杂的后端业务逻辑中,引入IoC可以降低组件之间的耦合度,实现系统各层之间的解耦,减少维护与理解成本。
装饰器
需要开启装饰器: tsc --init
生成tsconfig.json文件,配置
{
"compilerOptions": {
"experimentalDecorators": true,
}
}
类装饰器 ClassDecorator
主要是通过@符号添加装饰器
他会自动把class的构造函数传入到装饰器的第一个参数 target
然后通过prototype可以自定义添加属性和方法
typescript
let decotators: ClassDecorator = (target:any) =>{
target.prototype.name = 'jack'
}
// @decotators是一个语法糖,本质上是一个函数,可以通过decotators(DOCA)直接使用
@decotators
class DOCA {
constructor () {
}
}
const a:any = new DOCA()
属性装饰器PropertyDecorator
使用@符号给属性添加装饰器
他会返回两个参数给属性装饰器PropertyDecorator
1.原形对象
2.属性的名称
typescript
const currency: PropertyDecorator = (target: any, key: string | symbol) => {
console.log(target, key) // 输入两次:target是类,key是属性名docb_name1、docb_name2
}
class DOCB {
@currency
public docb_name1: string
@currency
public docb_name2: string
constructor() {
this.docb_name1 = 'jackb1'
this.docb_name2 = 'jackb2'
}
getName() {
return this.docb_name1
}
}
方法装饰器
使用@符号给方法添加装饰器
返回3个参数
1.原形对象
2.方法的名称
3.该方法的属性描述符 可写对应writable,可枚举对应enumerable,可配置对应configurable
typescript
const docM: MethodDecorator = (target: any, key: string | symbol,descriptor:any) => {
console.log(target, key,descriptor)
/*
target类的原型对象, 方法名getName, 方法getName的属性描述符{
value: [Function (anonymous)],
writable: true,
enumerable: true,
configurable: true
}
*/
}
class DOCD {
public name: string
constructor() {
this.name = ''
}
@docM
getName(name:string,age:number) {
return this.name
}
}
参数装饰器
使用@符号给属性添加装饰器
他会返回两个参数给装饰器
1.原形对象
2.方法的名称
3.参数的位置从0开始
typescript
const docP: ParameterDecorator = (target: any, key: string | symbol, index: number) => {
console.log(target, key, index) // target类的原型对象, 方法名getName, age参数的index位置1
}
class DOCC {
public name: string
constructor() {
this.name = ''
}
getName(name: string, @docP age: number) {
return this.name
}
}
实现一个GET装饰器请求
typescript
import axios from 'axios'
/*
定义装饰器,这里需要实现装饰器工厂接收装饰器传入的参数,如url
*/
const GET = (url: string): MethodDecorator => {
return (target, propertyName, descriptor) => {
// 获取当前使用装饰器的函数
const fnc: any = descriptor.value
axios
.get(url)
.then((res) => {
// 把axios的结果返回给当前使用装饰器的函数
fnc(res, { status: 200 })
})
.catch((err) => {
fnc(err, { status: 404 })
})
}
}
// 定义控制器
class MyGetController {
constructor() {}
// 把getList方法放到GET装饰器里面回调执行
@GET('https://api.apiopen.top/api/getHaoKanVideo?page=0&size=10')
getList(res, status) {
// 获取axios返回的数据
console.log(status, 'status')
console.log(res.data.result.list, 'res')
}
}