IOC 机制
在 Electron
开发中,我们很多时候做一件事情是需要多进程协作的,比如:渲染进程访问主进程,让其执行一段逻辑并返回结果。因此,随着项目的越来越大,这样的一种写作,就变成了「提供服务,访问服务」这么一个流程去进行协作。在这样的情况下,为了更好的组织代码与功能模块,我们会按照类的思想来抽象服务,就好比在写「服务端」一样。
我们知道,在java
中,spring
很好去管理了所有服务实例。因此,在这里,我们也可以把思想借鉴过来,进行使用。
所以,接下来,我们的目的,就是在 js
中去实现IOC
。
ioc 设计
上面我们有提到「服务」的概念,也提到了整个过程其实是「提供服务,访问服务」。因此在这个环节上,我们抽象为如下过程:
- 定义服务
- 注册服务(服务池)
- 获取服务
- 注入服务
ioc 设计----定义服务
为了统一的去管理服务,我们首先要对服务做一个基本的定义,从而统一去维护和管理。在vscode
中,服务是这样定义的:
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const IEnvironmentService = createDecorator<IEnvironmentService>('environmentService');
export interface IEnvironmentService {
readonly _serviceBrand: undefined;
...
}
针对上述结构,其实挺困惑的,尤其是如果不熟悉 ts 的话。
可以看到,上面即导出了名为IEnvironmentService
的interface
又导出了一个同名变量。第一个其实是装饰器,用在注入服务的时候,第二个interface
则适用于服务实现类的继承。
同时,我们发现interface
中存在:_serviceBrand
,这个东西在vscode
中暂时也没发现用处,记个 TODO 吧。
这里先不扩展createDecorator
的讲解,这个会在讲解到注入服务的时候,详细讲解。
废话不多说,我们先把需要的实现了,首先先创建src/ioc/instantiation.ts
并在里面讲必须的方法拷贝进去,这会在注入服务时详解。
/**
* Identifies a service of type T
*/
export interface ServiceIdentifier<T> { // 服务唯一 id
(...args: any[]): void;
type: T;
}
// ------ internal util
export namespace _util {
export const serviceIds = new Map<string, ServiceIdentifier<any>>();
export const DI_TARGET = '$di$target';
export const DI_DEPENDENCIES = '$di$dependencies';
export function getServiceDependencies(ctor: any): { id: ServiceIdentifier<any>, index: number, optional: boolean }[] {
return ctor[DI_DEPENDENCIES] || [];
}
}
function storeServiceDependency(id: Function, target: Function, index: number, optional: boolean): void {
if ((target as any)[_util.DI_TARGET] === target) {
(target as any)[_util.DI_DEPENDENCIES].push({ id, index, optional });
} else {
(target as any)[_util.DI_DEPENDENCIES] = [{ id, index, optional }];
(target as any)[_util.DI_TARGET] = target;
}
}
s
/**
* The *only* valid way to create a {{ServiceIdentifier}}.
*/
export function createDecorator<T>(serviceId: string): ServiceIdentifier<T> {
if (_util.serviceIds.has(serviceId)) {
return _util.serviceIds.get(serviceId)!;
}
const id = <any>function (target: Function, key: string, index: number): any {
if (arguments.length !== 3) {
throw new Error('@IServiceName-decorator can only be used to decorate a parameter');
}
storeServiceDependency(id, target, index, false);
};
id.toString = () => serviceId;
_util.serviceIds.set(serviceId, id);
return id;
}
接着,我们来创建一个自己的服务:testService
。
// src/services/test/common/testService.ts
import { createDecorator } from "../../../ioc/instantiation";
export const ITestService = createDecorator<ITestService>('testService');
export interface ITestService {
readonly _serviceBrand: undefined;
test: () => void; // 测试方法
}
实现此服务:
// src/services/test/electron-main/testMainService.ts
import { ITestService } from "../common/testService";
export class TestMainService implements ITestService {
_serviceBrand: undefined;
test() {
console.log('this is test service');
}
}
ioc 设计 ---- 注册服务
今天先到这里哦!上班咯!