Last active
December 15, 2022 12:47
-
-
Save eczn/cc10f3deb1c1c73a9919baa239453176 to your computer and use it in GitHub Desktop.
TypeScript IoC(DI) 设计模式实践, 具体实现见 ioc.tsx, 用法见 usage.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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