Skip to content

Instantly share code, notes, and snippets.

@eczn
Last active December 15, 2022 12:47
Show Gist options
  • Select an option

  • Save eczn/cc10f3deb1c1c73a9919baa239453176 to your computer and use it in GitHub Desktop.

Select an option

Save eczn/cc10f3deb1c1c73a9919baa239453176 to your computer and use it in GitHub Desktop.
TypeScript IoC(DI) 设计模式实践, 具体实现见 ioc.tsx, 用法见 usage.tsx
import 'reflect-metadata';
/** 此类型可描述任意 class */
type AnyClass<Args extends any[] = any, Instance = any> = new (...args: Args) => Instance;
/**
* ## IOC 控制器
* 支持做 IOC 设计模式
* - IOC.Register 注册类到 IOC 控制器
* - IOC.RegisterOne 注册类到 IOC 控制器 (单例模式)
* - IOC.New 控制器帮忙 new 一个类 (控制反转)
*/
export class IOC {
/** 类存储池 */
private static CPOOL: AnyClass[] = [];
/** 类单例标记 */
private static SINGLE_FLAG = Symbol.for('IOC:SINGLE');
/** 注册到 IoC 控制器 */
public static Register<T extends AnyClass>(TheClass: T) {
const paramsTypes: AnyClass[] = Reflect.getMetadata('design:paramtypes', TheClass);
// 忽略重复注入的情况
if (IOC.CPOOL.includes(TheClass)) return console.warn(`${TheClass.name} 重复注入`);
if (TheClass.length === 0) { // 说明构造函数不需要入参
IOC.CPOOL.push(TheClass);
return;
}
paramsTypes.forEach((v, i) => {
if (v === TheClass) throw new Error(`${TheClass.name} 不可以自己依赖自己`);
if (IOC.CPOOL.indexOf(v) === -1) throw new Error(`${TheClass.name} 不能 Register, 因为找不到这个 class 的依赖`);
});
IOC.CPOOL.push(TheClass);
}
/** IOC 注册为单例 */
public static RegisterOne<T extends AnyClass>(TheClass: T) {
// 注册类到 IOC
IOC.Register(TheClass);
// 这块需要 hack 一下, 单例存储回原来的 class 上去
const single = IOC.New(TheClass);
// @ts-ignore
TheClass[IOC.SINGLE_FLAG] = single;
}
/** 实施依赖注入构造实例 */
public static New<T>(TheClass: AnyClass<any, T>): T {
if (!IOC.CPOOL.includes(TheClass)) throw new Error(`${TheClass.name} 并没有注册在 IOC 容器内, 请检查声明`);
// 通过反射拿到参数列表
const paramsTypes: AnyClass[] = Reflect.getMetadata('design:paramtypes', TheClass);
// @ts-ignore 构造单例的情况
const theOne = TheClass[IOC.SINGLE_FLAG];
if (theOne) return theOne;
// TheClass 本身没有依赖
if (!paramsTypes || TheClass.length === 0) return new TheClass();
// 获取 TheClass 的依赖项
const classesInstanceToBeInjected = paramsTypes.map((DepClass, i) => {
if (!IOC.CPOOL.includes(DepClass)) throw new Error(`${DepClass.name} 并没有注册在 IOC 容器内, 请检查构造函数`);
// DepClass 也可能有自己的依赖, 要递归的处理
return IOC.New(DepClass);
});
// 注入依赖项实例来构造 TheClass
return new TheClass(...classesInstanceToBeInjected);
}
}
import { IOC } from './type-ioc';
@IOC.RegisterOne
class Conf {
public name = 'varEczn';
public constructor() {
console.log('构造 Conf', this);
}
}
@IOC.Register
class HelloWorld {
public readonly conf: Conf;
public constructor(conf: Conf) {
this.conf = conf;
console.log('构造 HelloWorld');
}
public sayHello() {
console.log('hello,', this.conf.name);
}
}
IOC.New(HelloWorld).sayHello();
IOC.New(HelloWorld).sayHello();
IOC.New(HelloWorld).sayHello();
IOC.New(HelloWorld).sayHello();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment