实现装饰器
// 定义装饰器
function Inject(serviceClass: new () => any) {
return function (target: any, propertyKey: string) {
const serviceInstance = new serviceClass();
Object.defineProperty(target, propertyKey, {
value: serviceInstance,
writable: true,
enumerable: true,
configurable: true
});
};
}
// 示例服务类
class ExampleService {
sayHello() {
return 'Hello from ExampleService';
}
}
// 使用装饰器注入服务的目标类
class TargetClass {
@Inject(ExampleService)
exampleService: ExampleService;
doSomething() {
return this.exampleService.sayHello();
}
}
优势
- 代码简洁:使用装饰器可以在目标类属性定义处直接声明依赖,代码更紧凑,可读性更高。例如在
TargetClass
中,通过@Inject(ExampleService)
一行代码就完成了依赖注入,而传统方式可能需要更多的初始化代码。
- 解耦依赖关系:依赖注入的本质就是解耦,装饰器模式使得依赖关系在类的定义层面清晰呈现,便于理解和维护。修改依赖时,只需修改装饰器参数即可。
- 提高可测试性:在测试
TargetClass
时,可以通过模拟ExampleService
并使用装饰器注入,轻松隔离测试TargetClass
的业务逻辑,而不需要复杂的手动构建依赖对象。
劣势
- 性能开销:装饰器每次运行时都会执行注入逻辑,例如每次创建
TargetClass
实例时,都会重新创建ExampleService
实例(如果未做单例处理),相较于传统方式提前创建好所有依赖并传入,可能存在一定性能损耗。
- 调试困难:由于装饰器是在编译阶段和运行阶段都有作用,当依赖注入出现问题时,例如注入失败或注入的对象不符合预期,调试相对复杂,因为错误可能出现在装饰器执行的各个阶段,且装饰器逻辑相对隐蔽,不如传统的构造函数注入等方式直观。
- 兼容性问题:TypeScript装饰器虽然强大,但并非所有JavaScript运行环境都原生支持,可能需要使用特定的转译工具或配置,在一些老旧项目或对兼容性要求较高的场景下使用受限。