编码母题 / P1 / 前端与专项架构 / 03

插件注册与生命周期系统

先读清题意与边界,独立尝试,再展开思路并对照可运行的参考代码。

重要程度高价值难度困难预计完成45–60 分钟

难度按 LeetCode 简单/中等/困难三档估计;工程题并非 LeetCode 官方题目。用时指独立编码并检查主要边界的参考时间,因经验而异。

题目是什么

插件声明名称、依赖和 setup;按依赖顺序启动。任一插件失败时按已成功启动的逆序回滚,正常停止也逆序卸载。

输入输出示例

B 依赖 A,若 B 的 setup 失败,应调用 A 已登记的 teardown;B 未成功返回 teardown,不得假装它已完成卸载。

练习起手式与验证

复制代码后补全空方法,再运行下方三组验证;复杂题所需的辅助类型与输入准备一并放在代码块中。

/*
 * 题目:插件注册与生命周期系统
 * 要求:插件声明名称、依赖和 setup;按依赖顺序启动。任一插件失败时按已成功启动的逆序回滚,
 * 正常停止也逆序卸载。
 * 示例:B 依赖 A,若 B 的 setup 失败,应调用 A 已登记的 teardown;B
 * 未成功返回 teardown,不得假装它已完成卸载。
 */
class PluginSystem {
  constructor() {}
  register(plugin) {}
  order() {}
  async start(context = {}) {}
  async rollback() {}
  async stop() {}
}

(async () => {
  const order = [];
  const system = new PluginSystem()
    .register({
      name: "db",
      setup: async () => {
        order.push("db+");
        return () => order.push("db-");
      },
    })
    .register({
      name: "api",
      dependencies: ["db"],
      setup: async () => {
        order.push("api+");
        return () => order.push("api-");
      },
    });
  await system.start();
  await system.stop();
  console.log(order); // 预期:["db+", "api+", "api-", "db-"]
  const failed = new PluginSystem()
    .register({ name: "a", setup: () => () => order.push("a-") })
    .register({
      name: "b",
      dependencies: ["a"],
      setup: () => {
        throw new Error("boom");
      },
    });
  console.log(await Promise.resolve(failed.start()).then(() => false, () => true)); // 预期:true(拒绝)
  console.log(failed.status); // 预期:"stopped"
})().catch(console.error);

解题思路

展开思路、不变量与常见边界

关键切入点:拓扑排序保证启动顺序,成功 setup 的 teardown 构成逆序释放栈。

核心模型:注册阶段构建静态图,启动阶段形成资源栈。逆序释放保证依赖者先于 被依赖者销毁。不变量:running 时所有依赖已先启动;teardown 栈只包含成功项; 失败后回到 stopped。迁移:前端平台、Agent Tool、构建插件和服务容器。

参考代码与验证

“关键机制”注释用于解释实现,下载链接提供未经页面注释修改的原始 JS 文件。

// 关键机制:拓扑排序保证启动顺序,成功 setup 的 teardown 构成逆序释放栈。
class PluginSystem {
  constructor() {
    this.plugins = new Map();
    this.teardowns = [];
    this.status = "stopped";
  }
  register(plugin) {
    if (this.status !== "stopped")
      throw new Error("cannot register while running");
    if (
      !plugin ||
      typeof plugin.name !== "string" ||
      typeof plugin.setup !== "function"
    )
      throw new TypeError("invalid plugin");
    if (this.plugins.has(plugin.name))
      throw new Error(`duplicate plugin: ${plugin.name}`);
    this.plugins.set(plugin.name, plugin);
    return this;
  }
  order() {
    const visiting = new Set();
    const visited = new Set();
    const result = [];
    const visit = (name) => {
      if (visited.has(name)) return;
      if (visiting.has(name)) throw new Error("plugin dependency cycle");
      const plugin = this.plugins.get(name);
      if (!plugin) throw new Error(`missing plugin: ${name}`);
      visiting.add(name);
      for (const dep of plugin.dependencies || []) visit(dep);
      visiting.delete(name);
      visited.add(name);
      result.push(plugin);
    };
    for (const name of this.plugins.keys()) visit(name);
    return result;
  }
  async start(context = {}) {
    if (this.status !== "stopped") throw new Error("already started");
    this.status = "starting";
    try {
      for (const plugin of this.order()) {
        const teardown = await plugin.setup(context);
        this.teardowns.push({
          name: plugin.name,
          teardown: typeof teardown === "function" ? teardown : async () => {},
        });
      }
      this.status = "running";
    } catch (error) {
      await this.rollback();
      throw error;
    }
  }
  async rollback() {
    const errors = [];
    while (this.teardowns.length) {
      const item = this.teardowns.pop();
      try {
        await item.teardown();
      } catch (e) {
        errors.push(e);
      }
    }
    this.status = "stopped";
    if (errors.length)
      throw new AggregateError(errors, "plugin teardown failed");
  }
  async stop() {
    if (this.status !== "running") throw new Error("not running");
    await this.rollback();
  }
}

// 三组以上可直接运行的验证;异步题使用独立执行块。
(async () => {
  const order = [];
  const system = new PluginSystem()
    .register({
      name: "db",
      setup: async () => {
        order.push("db+");
        return () => order.push("db-");
      },
    })
    .register({
      name: "api",
      dependencies: ["db"],
      setup: async () => {
        order.push("api+");
        return () => order.push("api-");
      },
    });
  await system.start();
  await system.stop();
  console.log(order); // 预期:["db+", "api+", "api-", "db-"]
  const failed = new PluginSystem()
    .register({ name: "a", setup: () => () => order.push("a-") })
    .register({
      name: "b",
      dependencies: ["a"],
      setup: () => {
        throw new Error("boom");
      },
    });
  console.log(await Promise.resolve(failed.start()).then(() => false, () => true)); // 预期:true(拒绝)
  console.log(failed.status); // 预期:"stopped"


})().catch(console.error);

复杂度分析

时间复杂度:O(V+E),不计 setup/teardown。拓扑访问每个插件和依赖一次,启动与回滚逐插件处理。

空间复杂度:O(V+E)。依赖遍历状态和逆序清理栈与插件图规模同阶。

下载原始 JS 文件