1. 使用属性装饰器实现依赖注入到类属性上
// 定义一个简单的依赖注入容器,用于存储依赖实例
const dependencyContainer: { [key: string]: any } = {};
// 注册依赖到容器
function registerDependency(key: string, instance: any) {
dependencyContainer[key] = instance;
}
// 定义属性装饰器
function inject(key: string) {
return function (target: any, propertyKey: string) {
Object.defineProperty(target, propertyKey, {
get() {
return dependencyContainer[key];
},
enumerable: true,
configurable: true
});
};
}
// 示例依赖类
class Logger {
log(message: string) {
console.log(`[LOG] ${message}`);
}
}
// 注册依赖
registerDependency('Logger', new Logger());
// 使用依赖注入的类
class MyService {
@inject('Logger')
private logger: Logger;
doSomething() {
this.logger.log('Doing something...');
}
}
// 使用MyService
const myService = new MyService();
myService.doSomething();
2. 优势
- 代码简洁:相比传统方式在构造函数中注入依赖,属性装饰器可以直接在属性上进行依赖注入,使得代码更加简洁直观,尤其是在类中有多个依赖时,构造函数不会变得臃肿。
- 解耦性增强:依赖注入的逻辑与类的业务逻辑分离,提高了代码的可维护性和可测试性。在测试时,可以轻松地替换依赖的实现,而不需要修改类的构造函数逻辑。
- 更好的可读性:通过装饰器的使用,一眼就能看出哪些属性是通过依赖注入获取的,提高了代码的可读性。
3. 潜在问题
- 性能问题:属性装饰器是在运行时进行依赖注入的,每次访问被装饰的属性时,都会触发装饰器中定义的
get
函数,这可能会带来一定的性能开销,尤其是在频繁访问该属性的情况下。
- 调试困难:由于依赖注入的逻辑通过装饰器实现,相比传统构造函数注入方式,调试过程可能会更加复杂,因为装饰器逻辑可能分布在不同的文件中,并且依赖关系可能不够直观。
- 兼容性与可移植性:属性装饰器是ES7的特性,虽然TypeScript支持,但在一些不支持该特性的环境中,可能需要额外的转译或垫片(polyfill)。并且不同的JavaScript运行环境对装饰器的支持和实现细节可能存在差异,影响代码的可移植性。