共计 6658 个字符,预计需要花费 17 分钟才能阅读完成。
Part1: What is Dependency injection
依赖注入定义为组件之间依赖关系由容器在运行期决定,形象的说即由容器动静的将某个依赖关系注入到组件之中在面向对象编程中,咱们常常解决的问题就是解耦,管制反转 (IoC) 就是罕用的面向对象编程的设计准则,其中依赖注入是管制反转最罕用的实现。指标解决以后类不负责被依赖类实例的创立和初始化。
Part2: What is Dependency
依赖是程序中常见的景象,假如有 A 和 B 都被 C 耦合依赖着,在 OOP 编程中依赖无处不在。依赖模式有多种表现形式,比方一个类向另一个类发消息,一个类是另一个类的成员,一个类是另一个类的参数。
class A {}
class B {
classA: A;
constructor() {
this.classA = new A();
}
}
class C {
classA: A;
classB: B;
constructor() {
this.classA = new A();
this.classB = new B();
}
}
Part3: When is use Dependency injection
eg: 以用户调用 API 层打印日志来阐明
LoggerService 被 ApiService 和 UserService 所依赖
ApiService 被 UserService 所依赖
class LoggerService {
constructor() {}
log(args) {console.log(args)
}
}
class ApiService {
constructor (private readonly logger: LoggerService
) {
this.logger.log('api constructor')
}
public async getMydata () {return { name: 'mumiao', hobby: 'focusing in web'}
}
}
class UserService {
constructor (
private readonly api: ApiService,
private readonly logger: LoggerService
) {
this.logger.log('user constructor')
}
async getMyhobby () {const { hobby} = await this.api.getMydata()
return hobby
}
}
async function Main {
const loggerService = new LoggerService()
const apiService = new ApiService(loggerService)
const userService = new UserService(loggerService, userService)
console.log('my hobby is', await userService.getMyhobby())
}
Main()
1、存在的问题
Unit tests 很难写
组件不易复用和保护,可扩展性比拟低
UserService 不应该承载 ApiService 和 LoggerService 实例的创立。
2、如何解决
采纳依赖注入,UserService 不负责被依赖类的创立和销毁,而是通过内部传入 api 和 logger 对象的形式注入。常见依赖注入形式有三种,本文次要以结构器注入为例解释。
const apiService = Injector.resolve < ApiService > ApiService;
const userService = Injector.resolve < UserService > UserService;
// returns an instance of , with all injected dependencies
Part4: Implement simply Dependency injection
1、准备常识
ES6 的平时业务中绝对应用较少的个性:Reflect、Proxy、Decorator、Map、Symbol
理解 Dependency injection,ES/TS 装璜器
深刻了解 TypeScript - Reflect Metadata
1)Reflect
简介
Proxy 与 Reflect 是 ES6 为了操作对象引入的 API,Reflect 的 API 和 Proxy 的 API 一一对应,并且能够函数式的实现一些对象操作。
另外,应用 reflect-metadata 能够让 Reflect 反对元编程
类型获取
类型元数据:design:type
参数类型元数据:design:paramtypes
函数返回值类型元数据:design:returntype
Reflect.defineMetaData(metadataKey, metadataValue, target) // 在类上定义元数据
Reflect.getMetaData(“design:type”, target, propertyKey); // 返回类被装璜属性类型
Reflect.getMetaData(“design:paramtypes”, target, propertyKey); // 返回类被装璜参数类型
Reflect.getMetaData(“design:returntype”, target, propertyKey); // 返回类被装璜函数返回值类型
2)Decorators
function funcDecorator(target, name, descriptor) {
// target 指 类的 prototype name 是函数名 descriptor 是属性描述符
let originalMethod = descriptor.value;
descriptor.value = function () {
console.log("我是 Func 的装璜器逻辑");
return originalMethod.apply(this, arguments);
};
return descriptor;
}
class Button {
@funcDecorator
onClick() {
console.log("我是 Func 的原有逻辑");
}
}
Reflect and Decorators
const Injector = (): ClassDecorator => {
// es7 decorator
return (target, key, descriptor) => {
console.log(Reflect.getMetadata("design:paramtypes", target));
// [apiService, loggerService]
};
};
@Injector()
class userService {
constructor(api: ApiService, logger: LoggerService) {}
}
[点击并拖拽以挪动]
3)Implement simply Dependency injection
// interface.ts
type Type<T = any> = new (…args: any[]) => T;
export type GenericClassDecorator<T> = (target: T) => void;
// ServiceDecorator.ts
const Service = (): GenericClassDecorator<Type<object>> => {
return (target: Type<object>) => {};
};
// Injector.ts
export const Injector = {
// resolving instances
resolve<T>(target: Type<any>): T {
// resolved injections from the Injector
let injections = Reflect.getMetadata("design:paramtypes", target) || [],
injections = injections.map((inject) => Injector.resolve<any>(inject));
return new target(...injections);
},
};
只实现了依赖提取的外围局部,依赖注入还有一个局部是 Container 容器存储相干。
Resolve Dependency
@Service()
class LoggerService {
//…
}
@Service()
class ApiService {
constructor (private readonly logger: LoggerService
) {
}
}
@Service
class UserService {
constructor (
private readonly api: ApiService,
private readonly logger: LoggerService
) {
}
}
async function Main {
// jnject dependencies
const apiService = Injector.resolve<ApiService>(ApiService);
const userService = Injector.resolve<UserService>(UserService);
console.log(‘my hobby is’, await userService.getMyhobby())
}
Main()
4)Implement simply Dependency injection with container
Part5: APIs of InversifyJS with TypeScript
1、应用步骤
Step 1: 申明接口及类型
Step 2: 申明依赖应用 @injectable & @inject decorators
Step 3: 创立并配置一个 Container
Step 4: 解析并提取依赖
2、示例
申明接口及类型:
export interface ILoggerService {}
export interface IApiService {}
export interface IUserService {}
export default TYPES = {
// 惟一依赖标识,倡议应用 Symbol.for 替换类作为标识符
ILoggerService: Symbol.for(“ILoggerService”),
IApiService: Symbol.for(“IApiService”),
IUserService: Symbol.for(“IUserService”),
};
[点击并拖拽以挪动]
申明依赖:
import ‘reflect-metadata’
import {injectable, inject} from ‘inversify’
@injectable()
export class LoggerService implements ILoggerService{
//…
}
@injectable()
export class ApiService implements IApiService{
protected _logger: LoggerService
constructor (private @inject(TYPES.ILoggerService) logger: LoggerService
) {
this._logger = logger
}
}
也能够应用 property injection 代替 constructor injection,这样就不必申明构造函数。
@injectable()
export class ApiService implements IApiService {
@inject(TYPES.ILoggerService) private _logger: LoggerService;
}
@injectable()
export class UserService implements IUserService {
protected _api: ApiService;
protected _logger: LoggerService;
constructor (private readonly @inject(TYPES.IApiService) api: ApiService,
private readonly @inject(TYPES.ILoggerService) logger: LoggerService
) {
this._api = api
this._logger = logger
}
}
创立并配置一个 Container
…
const DIContainer = new container()
DIContainer.bind<ApiService>(TYPES.IApiService).toSelf()
DIContainer.bind<LoggerService>(TYPES.ILoggerService).toSelf()
解析依赖
import “reflect-matadata”;
import {UserService} from “./services”;
import DIContainer from “./container”;
async function Main() {
const userService: UserService = DIContainer.resolve<UserService>(
UserService
);
console.log(“my hobby is”, await userService.getMyhobby());
}
Main();
Classes as identifiers and circular dependencies
An exception:
Error: Missing required @Inject or @multiinject annotation in: argument 0 in
import “reflect-metadata”;
import {injectable} from “inversify”;
@injectable()
class Dom {
public _domUi: DomUi;
constructor(@inject(DomUi) domUi: DomUi) {
this._domUi = domUi;
}
}
@injectable()
class DomUi {
public _dom;
constructor(@inject(Dom) dom: Dom) {
this._dom = dom;
}
}
@injectable()
class Test {
public _dom;
constructor(@inject(Dom) dom: Dom) {
this._dom = dom;
}
}
container.bind<Dom>(Dom).toSelf();
container.bind<DomUi>(DomUi).toSelf();
const dom = container.resolve(Test); // Error!
[点击并拖拽以挪动]
次要起因:decorator 被调用时,类还没有申明,导致 inject(undefined),InversifyJS 举荐应用 Symboy.for 生成依赖惟一标识符。
Part6: FrameWorks
依赖注入个别都借助第三方框架来实现,实现须要思考循环依赖,错误处理,容器存储等。
tsyringe:https://github.com/microsoft/tsyringe
实际:https://github.com/DTStack/molecule
InversifyJS:https://github.com/inversify/InversifyJS
实际:https://codesandbox.io/s/github/inversify/inversify-express-example/tree/master/?file=/BindingDecorators/controller/user.ts
数栈是云原生—站式数据中台 PaaS,咱们在 github 和 gitee 上有一个乏味的开源我的项目:FlinkX,FlinkX 是一个基于 Flink 的批流对立的数据同步工具,既能够采集动态的数据,也能够采集实时变动的数据,是全域、异构、批流一体的数据同步引擎。大家喜爱的话请给咱们点个 star!star!star!
github 开源我的项目:https://github.com/DTStack/fl…
gitee 开源我的项目:https://gitee.com/dtstack_dev…