在electron中基于容器和服务提供者扩展应用核心能力

应用自身可能提供多种不同的能力,结合服务提供者概念和容器,我们可以实现类似插件的扩展机制,并且通过容器来统一管理服务对象,方便后续扩展。

一、容器

容器提供注册同步/异步工厂的方法、同步/异步获取指定服务对象的方法,移除某服务的方法和清空全部服务对象的方法。

export class Container {
  private instances: Map<string, any> = new Map()
  private factories: Map<string, () => any> = new Map()
  private asyncFactories: Map<string, () => Promise<any>> = new Map()
  private resolvingPromises: Map<string, Promise<any>> = new Map()
  private singleton: Set<string> = new Set()

  // 同步注册工厂(默认为单例)
  register<T>(token: string, factory: () => T, options: { singleton?: boolean } = { singleton: true }) {
    this.factories.set(token, factory)
    if (options.singleton) {
      this.singleton.add(token)
    } else {
      this.singleton.delete(token)
    }
  }

  // 异步注册工厂(默认为单例)
  registerAsync<T>(token: string, factory: () => Promise<T>, options: { singleton?: boolean } = { singleton: true }) {
    this.asyncFactories.set(token, factory)
    if (options.singleton) {
      this.singleton.add(token)
    } else {
      this.singleton.delete(token)
    }
  }

  // 同步解析
  resolve<T>(token: string): T {
    // 如果是单例且已存在实例,直接返回
    if (this.singleton.has(token) && this.instances.has(token)) {
      return this.instances.get(token)
    }

    // 查找工厂函数
    const factory = this.factories.get(token)
    if (!factory) {
      // 如果没有工厂但有实例,可能是异步实例已完成
      if (this.instances.has(token)) {
        return this.instances.get(token)
      }
      throw new Error(`Service not found: ${token}`)
    }

    // 创建实例
    const instance = factory()

    // 如果是单例,保存实例
    if (this.singleton.has(token)) {
      this.instances.set(token, instance)
    }

    return instance
  }

  // 异步解析
  async resolveAsync<T>(token: string): Promise<T> {
    // 如果是单例且已存在实例,直接返回
    if (this.singleton.has(token) && this.instances.has(token)) {
      return this.instances.get(token)
    }

    // 检查是否已经有正在解析的Promise
    let resolvingPromise = this.resolvingPromises.get(token)
    if (resolvingPromise) {
      // 如果有,直接返回这个Promise的结果
      return resolvingPromise
    }

    // 先检查是否有异步工厂
    const asyncFactory = this.asyncFactories.get(token)
    if (asyncFactory) {
      // 创建一个新的解析Promise并存储
      resolvingPromise = (async () => {
        try {
          const resolved = await asyncFactory()
          // 如果是单例,保存实例
          if (this.singleton.has(token)) {
            this.instances.set(token, resolved)
          }
          return resolved
        } finally {
          // 解析完成后从resolvingPromises中移除
          this.resolvingPromises.delete(token)
        }
      })()

      // 如果是单例,存储Promise以避免重复创建
      if (this.singleton.has(token)) {
        this.resolvingPromises.set(token, resolvingPromise)
      }
      return resolvingPromise
    }

    // 再检查是否有同步工厂
    const factory = this.factories.get(token)
    if (factory) {
      try {
        const resolved = factory()
        // 如果是单例,保存实例
        if (this.singleton.has(token)) {
          this.instances.set(token, resolved)
        }
        return resolved
      } catch (error) {
        if (error instanceof Error) {
            throw new Error(`Failed to resolve service '${token}': ${error.message}`)
        } else {
            throw error
        }
      }
    }

    // 最后检查是否已有实例(可能是之前异步解析完成的)
    if (this.instances.has(token)) {
      return this.instances.get(token)
    }

    throw new Error(`Service not found: ${token}`)
  }

  // 检查是否存在指定token的服务
  has(token: string): boolean {
    return this.instances.has(token) ||
           this.factories.has(token) ||
           this.asyncFactories.has(token)
  }

  // 清除指定服务的实例(不移除工厂)
  clearInstance(token: string): void {
    this.instances.delete(token)
    this.resolvingPromises.delete(token)
  }

  // 移除服务(包括工厂和实例)
  remove(token: string): void {
    this.instances.delete(token)
    this.factories.delete(token)
    this.asyncFactories.delete(token)
    this.resolvingPromises.delete(token)
    this.singleton.delete(token)
  }
}

二、服务提供者

服务提供者接口比较简单,区分register和boot主要是为了确保boot中可以调用任何其他提供者。

import type { Container } from "../container"

export default interface ServiceProvider {
    /**
     * 应用启动后立即调用
     * 建议仅用于注册服务,不要执行其他操作,其他操作可以放在boot方法中
     *
     * @param container
     */
    register?(container: Container): void

    /**
     * boot函数在所有服务注册完成后调用
     * 可以执行一些初始化操作
     *
     * @param container
     */
    boot?(container: Container): void | Promise<void>
}

三、应用对象

import { Container } from './container'

export default class APP {

    /**
     * 容器对象
     */
    private container: Container

    /**
     * 服务提供者列表
     */
    private providers: ServiceProvider[] = []


    constructor() {
        // 初始化容器并注册服务提供者
        this.container = new Container()

        // 注册服务
        this.registerProviders()

        // 启动服务
        this.bootProviders()
    }

    registerProviders() {
        for (const provider of this.providers) {
            provider.register?.(this.container)
        }
    }

    bootProviders() {
        return Promise.all(
            this.providers.map(provider => provider.boot?.(this.container))
        )
    }
}

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注