依赖注入(Dependency Injection,DI)是一种设计模式,主要用于实现控制反转(Inversion of Control,IoC)。它通过将对象的依赖关系从内部管理转移到外部容器来解耦组件,从而提高代码的可测试性、可维护性和灵活性。
之前在使用nest.js中做开发的时候,被这种模式的简单性吸引,今天自己来使用TS简单实现一下依赖注入的能力。
首先在tsconfig.json中开启装饰器能力
"experimentalDecorators": true
然后我需要提供一个装饰器,来标识这个类是提供服务的类,也就是说可以被注入的类,代码如下
type Services = { [key: string]: any }; const services: Services = new Map(); function injectable(target: any) { services.set(target.name, target); }
我定义了一个map来存放这些可以注入的类,然后我们使用一下这个装饰器
@injectable class MapServices { getLocation() { console.log("返回当前用户的经纬度..."); } }
OK,接下来,我们定义加载装饰器,用来加载这些提供依赖的服务
function inject(className: string) { return function (target: any, propertyKey: string) { if (services.get(className)) { target[propertyKey] = new (services.get(className))(); } else throw new ReferenceError(`${className}的依赖无法找到!`); }; }
然后,我们使用这个装饰器来将提供地图服务的依赖加载进来
class UserController { @inject("MapServices") mapServices?: MapServices; getUserLocation() { this.mapServices!.getLocation(); } }
最后让我们来实验一下效果
至此,一个简单的依赖注入模式就实现了
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2024-08-05
突然想到一个问题,我这里所有的服务都不是单例,这样多个类注入的时候会产生多个实例,不是很好,那我来优化下,让他们注入的都是同一个对象就好了
首先将@injectable装饰器进行修改
function injectable(target: any) { services.set(target.name, new target()); }
然后,修改@inject装饰器
function inject(className: string) { return function (target: any, propertyKey: string) { if (services.get(className)) { target[propertyKey] = services.get(className); } else throw new ReferenceError(`${className}的依赖无法找到!`); }; }
这样,多个类注入的服务就是同一个实例了,Nice