在早我写过一篇有关js几种常见设计模式的demo,感兴趣的看我这篇文章:https://blog.csdn.net/woyebuzhidao321/article/details/120235389。
命令模式
我们看下vscode里面最简单的命令注册
src/vs/workbench/contrib/files/browser/fileActions.ts
CommandsRegistry.registerCommand({
id: NEW_FILE_COMMAND_ID,
handler: async (accessor) => {
await openExplorerAndCreate(accessor, false);
}
});
这里的id
是唯一的,handler
是对应的执行函数。
我们跳入registerCommand
方法里面。
export const CommandsRegistry: ICommandRegistry = new class implements ICommandRegistry {
private readonly _commands = new Map<string, LinkedList<ICommand>>();
private readonly _onDidRegisterCommand = new Emitter<string>();
readonly onDidRegisterCommand: Event<string> = this._onDidRegisterCommand.event;
registerCommand(idOrCommand: string | ICommand, handler?: ICommandHandler): IDisposable {
if (!idOrCommand) {
throw new Error(`invalid command`);
}
if (typeof idOrCommand === 'string') {
if (!handler) {
throw new Error(`invalid command`);
}
return this.registerCommand({ id: idOrCommand, handler });
}
// add argument validation if rich command metadata is provided
if (idOrCommand.description) {
const constraints: Array<TypeConstraint | undefined> = [];
for (let arg of idOrCommand.description.args) {
constraints.push(arg.constraint);
}
const actualHandler = idOrCommand.handler;
idOrCommand.handler = function (accessor, ...args: any[]) {
validateConstraints(args, constraints);
return actualHandler(accessor, ...args);
};
}
// find a place to store the command
const { id } = idOrCommand;
let commands = this._commands.get(id);
if (!commands) {
// 以健值对形式存储到map对象里统一管理
commands = new LinkedList<ICommand>();
this._commands.set(id, commands);
}
let removeFn = commands.unshift(idOrCommand);
let ret = toDisposable(() => {
removeFn();
const command = this._commands.get(id);
if (command?.isEmpty()) {
this._commands.delete(id);
}
});
// tell the world about this command
this._onDidRegisterCommand.fire(id);
return ret;
}
...
}
可以看到以健值对形式存储到_commands map对象里统一管理起来。id作为key,value是链表数据结构
接下来我们看看是如何通过按钮去调用的
src/vs/workbench/contrib/files/browser/views/explorerView.ts
registerAction2(class extends Action2 {
constructor() {
super({
id: 'workbench.files.action.createFileFromExplorer',
title: nls.localize('createNewFile', "New File"),
f1: false,
icon: Codicon.newFile,
precondition: ExplorerResourceNotReadonlyContext,
menu: {
id: MenuId.ViewTitle,
group: 'navigation',
when: ContextKeyExpr.equals('view', VIEW_ID),
order: 10
}
});
}
run(accessor: ServicesAccessor): void {
const commandService = accessor.get(ICommandService);
commandService.executeCommand(NEW_FILE_COMMAND_ID);
}
});
可以看到通过执行 executeCommand
方法去调用,我们看看executeCommand方法里面实现了什么
src/vs/workbench/services/commands/common/commandService.ts
async executeCommand<T>(id: string, ...args: any[]): Promise<T> {
...
return this._tryExecuteCommand(id, args);
....
}
private _tryExecuteCommand(id: string, args: any[]): Promise<any> {
// 获取链表节点
const command = CommandsRegistry.getCommand(id);
if (!command) {
return Promise.reject(new Error(`command '${id}' not found`));
}
try {
this._onWillExecuteCommand.fire({ commandId: id, args });
// 这里调用handler方法
const result = this._instantiationService.invokeFunction(command.handler, ...args);
this._onDidExecuteCommand.fire({ commandId: id, args });
return Promise.resolve(result);
} catch (err) {
return Promise.reject(err);
}
}
可以看到通过getCommand
方法通过id获取到了链表的节点,我们看下invokeFunction函数实现
src/vs/platform/instantiation/common/instantiationService.ts
invokeFunction<R, TS extends any[] = []>(fn: (accessor: ServicesAccessor, ...args: TS) => R, ...args: TS): R {
let _trace = Trace.traceInvocation(fn);
let _done = false;
try {
const accessor: ServicesAccessor = {
get: <T>(id: ServiceIdentifier<T>) => {
if (_done) {
throw illegalState('service accessor is only valid during the invocation of its target method');
}
const result = this._getOrCreateServiceInstance(id, _trace);
if (!result) {
throw new Error(`[invokeFunction] unknown service '${id}'`);
}
return result;
}
};
return fn(accessor, ...args);
} finally {
_done = true;
_trace.stop();
}
}
可以看到其实它就是调用了handler方法。这样从注册到执行的过程就通了。