Javascript技术栈中的四种依赖注入详解(4)

@RadicalInject(Notebook, Pencil, Eraser) class Student { pencil: Pencil; eraser: Eraser; notebook: Notebook; public constructor() {} public constructor(notebook: Notebook, pencil: Pencil, eraser: Eraser) { this.notebook = notebook; this.pencil = pencil; this.eraser = eraser; } public write() { if (!this.notebook || !this.pencil) { throw new Error('Dependencies not provided!'); } console.log('writing...'); } public draw() { if (!this.notebook || !this.pencil || !this.eraser) { throw new Error('Dependencies not provided!'); } console.log('drawing...'); } } // 不再出现injector,直接调用构造函数 var student = new Student(); console.log(student instanceof Student); // true student.notebook.printName(); // this is a notebook student.pencil.printName(); // this is a pencil student.eraser.printName(); // this is an eraser student.draw(); // drawing student.write(); // writing

由于class Student的constructor方法需要接收三个参数,直接无参调用new Student()会造成TypeScript编译器报错。当然这里只是分享一种思路,大家可以暂时忽略这个错误。有兴趣的同学也可以使用类似的思路尝试代理一个工厂方法,而非直接代理构造函数,以避免这类错误,这里不再展开。

AngularJS2团队为了获得更好的装饰器和反射语法的支持,一度准备另起炉灶,基于AtScript(AtScript中的"A"指的就是Annotation)来进行新框架的开发。但最终却选择拥抱TypeScript,于是便有了微软和谷歌的奇妙组合。

当然,需要说明的是,在缺少相关标准和浏览器厂商支持的情况下,TypeScript在运行时只是纯粹的Javascript,下节中出现的例子会印证这一点。

四. inversify.js——Javascript技术栈中的IoC容器

其实从Javascript出现各种支持高级语言特性的方言就可以预见到,IoC容器的出现只是早晚的事情。比如博主今天要介绍的基于TypeScript的inversify.js,就是其中的先行者之一。
inversity.js比上节中博主实现的例子还要进步很多,它最初设计的目的就是为了前端工程师同学们能在Javascript中写出符合SOLID原则的代码,立意可谓非常之高。表现在代码中,就是处处有接口,将"Depend upon Abstractions. Do not depend upon concretions."(依赖于抽象,而非依赖于具体)表现地淋漓尽致。继续使用上面的例子,但是由于inversity.js是面向接口的,上面的代码需要进一步重构:

interface NotebookInterface { printName(): void; } interface PencilInterface { printName(): void; } interface EraserInterface { printName(): void; } interface StudentInterface { notebook: NotebookInterface; pencil: PencilInterface; eraser: EraserInterface; write(): void; draw(): void; } class Notebook implements NotebookInterface { public printName() { console.log('this is a notebook'); } } class Pencil implements PencilInterface { public printName() { console.log('this is a pencil'); } } class Eraser implements EraserInterface { public printName() { console.log('this is an eraser'); } } class Student implements StudentInterface { notebook: NotebookInterface; pencil: PencilInterface; eraser: EraserInterface; constructor(notebook: NotebookInterface, pencil: PencilInterface, eraser: EraserInterface) { this.notebook = notebook; this.pencil = pencil; this.eraser = eraser; } write() { console.log('writing...'); } draw() { console.log('drawing...'); } }

由于使用了inversity框架,这次我们就不用自己实现injector和Inject装饰器啦,只需要从inversify模块中引用相关对象:

import { Inject } from "inversify"; @Inject("NotebookInterface", "PencilInterface", "EraserInterface") class Student implements StudentInterface { notebook: NotebookInterface; pencil: PencilInterface; eraser: EraserInterface; constructor(notebook: NotebookInterface, pencil: PencilInterface, eraser: EraserInterface) { this.notebook = notebook; this.pencil = pencil; this.eraser = eraser; } write() { console.log('writing...'); } draw() { console.log('drawing...'); } }

这样就行了吗?还记得上节中提到TypeScript中各种概念只是语法糖吗?不同于上一节中直接将constructor引用传递给Inject的例子,由于inversify.js是面向接口的,而诸如NotebookInterface、PencilInterface之类的接口只是由TypeScript提供的语法糖,在运行时并不存在,因此我们在装饰器中声明依赖时只能使用字符串形式而非引用形式。不过不用担心,inversify.js为我们提供了bind机制,在接口的字符串形式和具体的构造函数之间搭建了桥梁:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wgypgg.html