ReactNative进阶(三十一): IoC 框架 InversifyJS解读

举报
SHQ5785 发表于 2021/05/22 02:54:32 2021/05/22
【摘要】 文章目录 简介为什么要有 InversifyJS?目标安装应用示例步骤 1: 声明接口和类型步骤 2: 使用 @injectable 和 @inject 装饰器声明依赖步骤 3: 创建和配置容器步骤 4: 解析依赖 拓展阅读 简介 InversifyJS ,一个强大又轻量的控制反转容器,提供给JavaScript 和 Node.js 应用使用...


在这里插入图片描述

简介

InversifyJS ,一个强大又轻量的控制反转容器,提供给JavaScriptNode.js 应用使用,使用TypeScript编写。

InversifyJS 是一个轻量的 (4KB) 控制反转容器 (IoC),可用于编写 TypeScriptJavaScript 应用。 它使用类构造函数去定义和注入它的依赖。InversifyJS API 友好易懂, 鼓励对 OOPIoC 最佳实践的应用.

为什么要有 InversifyJS?

JavaScript 现在支持面向对象编程,基于类的继承。 这些特性不错但事实上它们也是危险的。 我们需要一个优秀的面向对象设计(比如 SOLID),Composite Reuse等)来保护我们避免这些威胁。然而,面向对象的设计是复杂的,所以InversifyJS应运而生。

InversifyJS 是一个工具,它能帮助 JavaScript 开发者,写出出色的面向对象设计的代码。

目标

InversifyJS有4个主要目标:

  • 允许JavaScript开发人员编写遵循 SOLID 原则的代码。

  • 促进并鼓励遵守最佳的面向对象编程(OOP)和依赖注入(IoC)实践。

  • 尽可能少的运行时开销。

  • 提供艺术编程体验和生态。

安装

可以使用npm获得最新的版本和类型定义:

$ npm install inversify reflect-metadata --save

  
 
  • 1

注:由于 InversifyJS 通过反射来获取装饰器的相关元数据,所以需要额外安装库
reflect-metadata

Inversify npm 包已经包含了 InversifyJS 的类型定义

:警示: 重要! InversifyJS 需要 TypeScript 的版本 >= 2.0 还有
experimentalDecorators, emitDecoratorMetadata, types and lib 在 tsconfig.json 中 compilerOptions 的配置如下:

{ "compilerOptions": { "target": "es5", "lib": ["es6"], "types": ["reflect-metadata"], "module": "commonjs", "moduleResolution": "node", "experimentalDecorators": true, "emitDecoratorMetadata": true }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

inversifyjs需要现代JavaScript引擎,支持以下特性

  • Reflect metadata
  • Map
  • Promise (Only required if using provider injection)
  • Proxy (Only required if using activation handlers)

如果运行环境不支持这些特性,可能需要导入 shimpolyfill.

:警示: reflect-metadata polyfill 应该在您整个应用中只导入一次, 因为 Reflect 对象需要成为一个全局的单例。

应用示例

inversifyjs 基本用法和 API应用示例如下:

步骤 1: 声明接口和类型

目标是编写遵循依赖倒置原则的代码。

这意味着我们应该 ”依赖于抽象而不依赖于具体实现“ 。

先声明一些接口(抽象)。

// file interfaces.ts

interface Warrior { fight(): string; sneak(): string;
}

interface Weapon { hit(): string;
}

interface ThrowableWeapon { throw(): string;
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

Inversifyjs 需要在运行时使用类型标记作为标识符。接下来将使用 Symbol 作为标识符,也可以使用类或字符串。

// file types.ts

const TYPES = { Warrior: Symbol.for("Warrior"), Weapon: Symbol.for("Weapon"), ThrowableWeapon: Symbol.for("ThrowableWeapon")
};

export { TYPES };

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

警示: 推荐使用 Symbol,但 InversifyJS 也支持使用类和字符串字面值。

步骤 2: 使用 @injectable 和 @inject 装饰器声明依赖

接下来,声明一些类,实现刚刚声明的接口。需要使用 @injectable 装饰器去注解。

当一个类依赖于某个接口时,我们需要使用 @inject 装饰器,来定义在运行时可用的接口标识。在这种情况下,将使用 Symbol, 如 Symbol.for("Weapon")Symbol.for("ThrowableWeapon") 作为运行时的标识。

// file entities.ts

import { injectable, inject } from "inversify";
import "reflect-metadata";
import { Weapon, ThrowableWeapon, Warrior } from "./interfaces"
import { TYPES } from "./types";

@injectable()
class Katana implements Weapon { public hit() { return "cut!"; }
}

@injectable()
class Shuriken implements ThrowableWeapon { public throw() { return "hit!"; }
}

@injectable()
class Ninja implements Warrior { private _katana: Weapon; private _shuriken: ThrowableWeapon; public constructor( @inject(TYPES.Weapon) katana: Weapon, @inject(TYPES.ThrowableWeapon) shuriken: ThrowableWeapon ) { this._katana = katana; this._shuriken = shuriken; } public fight() { return this._katana.hit(); } public sneak() { return this._shuriken.throw(); }

}

export { Ninja, Katana, Shuriken };

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

如果更喜欢使用属性注入而不是构造函数注入,那就可以不用声明类的构造函数了,属性注入方式如下:

@injectable()
class Ninja implements Warrior { @inject(TYPES.Weapon) private _katana: Weapon; @inject(TYPES.ThrowableWeapon) private _shuriken: ThrowableWeapon; public fight() { return this._katana.hit(); } public sneak() { return this._shuriken.throw(); }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

步骤 3: 创建和配置容器

推荐在命名为 inversify.config.ts 的文件中创建和配置容器。这是唯一有耦合的地方。 在项目其余部分中的类,不应该包含对其他类的引用。

// file inversify.config.ts

import { Container } from "inversify";
import { TYPES } from "./types";
import { Warrior, Weapon, ThrowableWeapon } from "./interfaces";
import { Ninja, Katana, Shuriken } from "./entities";

const myContainer = new Container();
myContainer.bind<Warrior>(TYPES.Warrior).to(Ninja);
myContainer.bind<Weapon>(TYPES.Weapon).to(Katana);
myContainer.bind<ThrowableWeapon>(TYPES.ThrowableWeapon).to(Shuriken);

export { myContainer };

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

步骤 4: 解析依赖

可以使用方法 get<T>Container 中获得依赖。记得应该在项目根结构(尽可能靠近应用程序的入口点的位置)去解析依赖,避免服务器定位反模式

import { myContainer } from "./inversify.config";
import { TYPES } from "./types";
import { Warrior } from "./interfaces";

const ninja = myContainer.get<Warrior>(TYPES.Warrior);

expect(ninja.fight()).eql("cut!"); // true
expect(ninja.sneak()).eql("hit!"); // true

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

正如我们所看到的,Katana and Shuriken 被成功的解析和注入进 Ninja。

InversifyJS 支持 ES5ES6 ,而且可以在没有 TypeScript 环境下使用。

不做知识的搬运工,只做知识的汇聚者!

注:本文转载于https://doc.inversify.cloud/zh_cn/,学习InversifyJS,看此文足矣!!墙裂推荐!!

拓展阅读

文章来源: shq5785.blog.csdn.net,作者:No Silver Bullet,版权归原作者所有,如需转载,请联系作者。

原文链接:shq5785.blog.csdn.net/article/details/117108216

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。