vscode 源码学习5 - ioc 机制之定义服务

999 阅读2分钟

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 的话。

可以看到,上面即导出了名为IEnvironmentServiceinterface 又导出了一个同名变量。第一个其实是装饰器,用在注入服务的时候,第二个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 设计 ---- 注册服务

今天先到这里哦!上班咯!