第十四章:代码质量分析

第十四章:代码质量分析

📊 本章分析 MiniClaw 的代码质量、设计模式和最佳实践。


14.1 代码风格

14.1.1 TypeScript 严格模式

┌─────────────────────────────────────────────────────────────────────┐
│                    TypeScript 严格模式                               │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  tsconfig.json 中的严格配置:                                       │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  {                                                          │   │
│  │    "compilerOptions": {                                     │   │
│  │      "strict": true,                                        │   │
│  │      "noImplicitAny": true,                                 │   │
│  │      "strictNullChecks": true,                              │   │
│  │      "strictFunctionTypes": true,                           │   │
│  │      "noUnusedLocals": true,                                │   │
│  │      "noUnusedParameters": true,                            │   │
│  │      "noImplicitReturns": true,                             │   │
│  │      "noFallthroughCasesInSwitch": true                     │   │
│  │    }                                                        │   │
│  │  }                                                          │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  严格模式的好处:                                                   │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  ✅ 编译时捕获更多错误                                       │   │
│  │  ✅ 更好的类型推断                                           │   │
│  │  ✅ 更安全的代码                                             │   │
│  │  ✅ 更好的 IDE 支持                                          │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

14.1.2 ES Module 规范

┌─────────────────────────────────────────────────────────────────────┐
│                    ES Module 规范                                    │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  package.json 配置:                                                │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  { "type": "module" }                                       │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  导入导出风格:                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  // 命名导出                                                 │   │
│  │  export interface Entity { ... }                            │   │
│  │  export class SkillCache { ... }                            │   │
│  │  export function getTimeMode(): TimeMode { ... }            │   │
│  │                                                             │   │
│  │  // 命名导入                                                 │   │
│  │  import { Server } from '@modelcontextprotocol/sdk/server';│   │
│  │  import { promises as fs } from 'fs';                       │   │
│  │                                                             │   │
│  │  // 默认导出                                                 │   │
│  │  export default kernel;                                     │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  文件扩展名要求:                                                   │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  • TypeScript 源文件:.ts                                    │   │
│  │  • 编译输出文件:.js                                          │   │
│  │  • 导入时需要包含 .js 扩展名(编译后)                       │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

14.1.3 异步编程模式

┌─────────────────────────────────────────────────────────────────────┐
│                    异步编程模式                                      │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  async/await 风格:                                                 │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                                                             │   │
│  │  // 推荐:async/await                                        │   │
│  │  async function boot(options: BootOptions): Promise<string> {│   │
│  │    const state = await this.loadState();                    │   │
│  │    const files = await this.scanFiles();                    │   │
│  │    return this.compileContext(files);                       │   │
│  │  }                                                          │   │
│  │                                                             │   │
│  │  // 并行执行                                                 │   │
│  │  const [agents, soul, identity] = await Promise.all([       │   │
│  │    this.readFile('AGENTS.md'),                              │   │
│  │    this.readFile('SOUL.md'),                                │   │
│  │    this.readFile('IDENTITY.md'),                            │   │
│  │  ]);                                                        │   │
│  │                                                             │   │
│  │  // 错误处理                                                 │   │
│  │  try {                                                      │   │
│  │    const result = await this.execCommand(cmd);              │   │
│  │    return result;                                           │   │
│  │  } catch (error) {                                          │   │
│  │    this.bootErrors.push(error.message);                     │   │
│  │    return null;                                             │   │
│  │  }                                                          │   │
│  │                                                             │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  避免:                                                             │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  ❌ 嵌套回调                                                 │   │
│  │  ❌ 未处理的 Promise 拒绝                                    │   │
│  │  ❌ 混用回调与 Promise                                       │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

14.2 设计模式

14.2.1 单例模式(Kernel)

┌─────────────────────────────────────────────────────────────────────┐
│                    单例模式                                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ContextKernel 作为核心内核,在应用生命周期中只存在一个实例         │
│                                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                                                             │   │
│  │  class ContextKernel {                                      │   │
│  │    private static instance: ContextKernel;                  │   │
│  │                                                             │   │
│  │    private constructor() {                                  │   │
│  │      // 私有构造函数,防止外部实例化                         │   │
│  │      this.skillCache = new SkillCache();                    │   │
│  │      this.entityStore = new EntityStore();                  │   │
│  │    }                                                        │   │
│  │                                                             │   │
│  │    public static getInstance(): ContextKernel {             │   │
│  │      if (!ContextKernel.instance) {                         │   │
│  │        ContextKernel.instance = new ContextKernel();        │   │
│  │      }                                                      │   │
│  │      return ContextKernel.instance;                         │   │
│  │    }                                                        │   │
│  │  }                                                          │   │
│  │                                                             │   │
│  │  // 使用                                                     │   │
│  │  const kernel = ContextKernel.getInstance();                │   │
│  │                                                             │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  好处:                                                             │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  ✅ 全局唯一的状态管理                                       │   │
│  │  ✅ 避免重复初始化                                           │   │
│  │  ✅ 共享缓存和实体存储                                       │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

14.2.2 缓存模式(SkillCache)

┌─────────────────────────────────────────────────────────────────────┐
│                    缓存模式                                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  SkillCache 实现带 TTL 的缓存机制                                   │
│                                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                                                             │   │
│  │  class SkillCache {                                         │   │
│  │    private cache: Map<string, SkillCacheEntry>;             │   │
│  │    private lastScanTime: number = 0;                        │   │
│  │    private readonly TTL_MS: number = 5000;  // 5 秒         │   │
│  │                                                             │   │
│  │    async getAll(): Promise<Map<string, SkillCacheEntry>> {  │   │
│  │      // 检查缓存是否有效                                     │   │
│  │      if (Date.now() - this.lastScanTime < this.TTL_MS) {    │   │
│  │        return this.cache;  // 返回缓存                      │   │
│  │      }                                                      │   │
│  │      // 缓存过期,重新扫描                                   │   │
│  │      await this.refresh();                                  │   │
│  │      return this.cache;                                     │   │
│  │    }                                                        │   │
│  │                                                             │   │
│  │    invalidate(): void {                                     │   │
│  │      this.lastScanTime = 0;  // 使缓存失效                   │   │
│  │    }                                                        │   │
│  │                                                             │   │
│  │    private async refresh(): Promise<void> {                 │   │
│  │      // 扫描技能目录                                         │   │
│  │      this.cache = await this.scanSkillsDir();               │   │
│  │      this.lastScanTime = Date.now();                        │   │
│  │    }                                                        │   │
│  │  }                                                          │   │
│  │                                                             │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  缓存模式优势:                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  ✅ 减少文件系统 I/O                                         │   │
│  │  ✅ 提高响应速度                                             │   │
│  │  ✅ TTL 自动过期                                             │   │
│  │  ✅ 可手动失效                                               │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

14.2.3 策略模式(TimeMode)

┌─────────────────────────────────────────────────────────────────────┐
│                    策略模式                                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  时间模式根据当前时间选择不同的上下文策略                           │
│                                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                                                             │   │
│  │  type TimeMode = 'morning' | 'work' | 'break' | 'evening' | 'night';│
│  │                                                             │   │
│  │  interface TimeModeConfig {                                 │   │
│  │    emoji: string;                                           │   │
│  │    greeting: string;                                        │   │
│  │    features: string[];                                      │   │
│  │  }                                                          │   │
│  │                                                             │   │
│  │  const TIME_MODES: Record<TimeMode, TimeModeConfig> = {     │   │
│  │    morning: {                                               │   │
│  │      emoji: '☀️',                                            │   │
│  │      greeting: 'Good morning!',                             │   │
│  │      features: ['briefing', 'planning'],                    │   │
│  │    },                                                       │   │
│  │    work: {                                                  │   │
│  │      emoji: '💼',                                            │   │
│  │      greeting: 'Let\'s work!',                              │   │
│  │      features: ['focus', 'minimal'],                        │   │
│  │    },                                                       │   │
│  │    night: {                                                 │   │
│  │      emoji: '😴',                                            │   │
│  │      greeting: 'Good night!',                               │   │
│  │      features: ['minimal'],                                 │   │
│  │    },                                                       │   │
│  │  };                                                         │   │
│  │                                                             │   │
│  │  function getTimeMode(): TimeMode {                         │   │
│  │    const hour = new Date().getHours();                      │   │
│  │    if (hour >= 6 && hour < 9) return 'morning';             │   │
│  │    if (hour >= 9 && hour < 18) return 'work';               │   │
│  │    if (hour >= 22 || hour < 6) return 'night';              │   │
│  │    return 'break';                                          │   │
│  │  }                                                          │   │
│  │                                                             │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  策略模式优势:                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  ✅ 易于扩展新的时间模式                                     │   │
│  │  ✅ 配置与逻辑分离                                           │   │
│  │  ✅ 运行时动态切换                                           │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

14.2.4 工厂模式(Skill Discovery)

┌─────────────────────────────────────────────────────────────────────┐
│                    工厂模式                                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  技能发现机制动态创建技能对象                                       │
│                                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                                                             │   │
│  │  interface SkillCacheEntry {                                │   │
│  │    name: string;                                            │   │
│  │    description: string;                                     │   │
│  │    prompts?: SkillPromptDeclaration[];                      │   │
│  │    tools?: SkillToolDeclaration[];                          │   │
│  │    resources?: SkillResourceDeclaration[];                  │   │
│  │  }                                                          │   │
│  │                                                             │   │
│  │  async discoverSkillPrompts(): Promise<SkillPromptDeclaration[]> {│
│  │    const skills = await this.skillCache.getAll();           │   │
│  │    const prompts: SkillPromptDeclaration[] = [];            │   │
│  │                                                             │   │
│  │    for (const [name, skill] of skills) {                    │   │
│  │      if (skill.prompts) {                                   │   │
│  │        for (const prompt of skill.prompts) {                │   │
│  │          prompts.push({                                     │   │
│  │            name: `skill_${name}_${prompt.name}`,            │   │
│  │            description: prompt.description,                 │   │
│  │          });                                                │   │
│  │        }                                                    │   │
│  │      }                                                      │   │
│  │    }                                                        │   │
│  │    return prompts;                                          │   │
│  │  }                                                          │   │
│  │                                                             │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  工厂模式优势:                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  ✅ 动态创建对象                                             │   │
│  │  ✅ 解耦创建逻辑                                             │   │
│  │  ✅ 支持插件化扩展                                           │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

14.3 错误处理

14.3.1 try-catch 模式

┌─────────────────────────────────────────────────────────────────────┐
│                    try-catch 模式                                    │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  文件读取错误处理:                                                 │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                                                             │   │
│  │  async function readFileSafely(path: string): Promise<string | null> {│
│  │    try {                                                    │   │
│  │      return await fs.readFile(path, 'utf-8');               │   │
│  │    } catch (error) {                                        │   │
│  │      if ((error as NodeJS.ErrnoException).code === 'ENOENT') {│
│  │        // 文件不存在,返回 null                              │   │
│  │        return null;                                         │   │
│  │      }                                                      │   │
│  │      // 其他错误,记录并返回 null                            │   │
│  │      console.error(`Error reading ${path}:`, error);        │   │
│  │      return null;                                           │   │
│  │    }                                                        │   │
│  │  }                                                          │   │
│  │                                                             │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  启动错误收集:                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                                                             │   │
│  │  async boot(options: BootOptions): Promise<string> {        │   │
│  │    this.bootErrors = [];  // 重置错误列表                   │   │
│  │                                                             │   │
│  │    // 尝试加载各个文件                                       │   │
│  │    const agents = await this.readFileSafely('AGENTS.md');   │   │
│  │    const soul = await this.readFileSafely('SOUL.md');       │   │
│  │                                                             │   │
│  │    // 如果有错误,在上下文中标记                             │   │
│  │    if (this.bootErrors.length > 0) {                        │   │
│  │      context += `\n## ⚠️ 启动警告\n${this.bootErrors.join('\n')}`;│
│  │    }                                                        │   │
│  │                                                             │   │
│  │    return context;                                          │   │
│  │  }                                                          │   │
│  │                                                             │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

14.3.2 McpError 使用

┌─────────────────────────────────────────────────────────────────────┐
│                    McpError 使用                                     │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  MCP 协议错误处理:                                                 │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                                                             │   │
│  │  import { McpError } from '@modelcontextprotocol/sdk/types';│   │
│  │                                                             │   │
│  │  server.setRequestHandler(CallToolRequestSchema, async (req) => {│
│  │    try {                                                    │   │
│  │      const result = await handleToolCall(req.params);       │   │
│  │      return { content: [{ type: 'text', text: result }] };  │   │
│  │    } catch (error) {                                        │   │
│  │      if (error instanceof McpError) {                       │   │
│  │        throw error;  // 重新抛出 MCP 错误                    │   │
│  │      }                                                      │   │
│  │      // 包装为 MCP 错误                                      │   │
│  │      throw new McpError(                                    │   │
│  │        ErrorCode.InternalError,                             │   │
│  │        error.message                                        │   │
│  │      );                                                     │   │
│  │    }                                                        │   │
│  │  });                                                        │   │
│  │                                                             │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  常用错误码:                                                       │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  ErrorCode.InvalidRequest   → 无效请求                      │   │
│  │  ErrorCode.MethodNotFound   → 方法不存在                    │   │
│  │  ErrorCode.InvalidParams    → 参数无效                      │   │
│  │  ErrorCode.InternalError    → 内部错误                      │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

14.3.3 错误日志

┌─────────────────────────────────────────────────────────────────────┐
│                    错误日志                                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  日志记录策略:                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                                                             │   │
│  │  // 错误日志输出到 stderr(不影响 MCP 通信)                 │   │
│  │  function logError(context: string, error: unknown): void { │   │
│  │    const message = error instanceof Error                   │   │
│  │      ? error.message                                        │   │
│  │      : String(error);                                       │   │
│  │                                                             │   │
│  │    console.error(`[MiniClaw ERROR] ${context}: ${message}`);│   │
│  │  }                                                          │   │
│  │                                                             │   │
│  │  // 使用示例                                                 │   │
│  │  try {                                                      │   │
│  │    await someOperation();                                   │   │
│  │  } catch (error) {                                          │   │
│  │    logError('someOperation', error);                        │   │
│  │  }                                                          │   │
│  │                                                             │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  注意事项:                                                         │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  • MCP 使用 stdin/stdout 通信                               │   │
│  │  • 日志必须输出到 stderr                                     │   │
│  │  • 不能使用 console.log(会干扰 MCP 协议)                   │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

14.4 代码度量

14.4.1 代码规模

┌─────────────────────────────────────────────────────────────────────┐
│                    代码规模                                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                                                             │   │
│  │  文件                    行数        说明                    │   │
│  │  ─────────────────────────────────────────────────────────  │   │
│  │  src/index.ts           ~300        MCP 服务入口             │   │
│  │  src/kernel.ts          ~2400       核心内核实现             │   │
│  │  ─────────────────────────────────────────────────────────  │   │
│  │  总计                    ~2700                                │   │
│  │                                                             │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  与 OpenClaw 对比:                                                 │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  OpenClaw:  ~50,000+ 行                                      │   │
│  │  MiniClaw:  ~2,700 行                                        │   │
│  │  压缩比:    ~5%                                              │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

14.4.2 圈复杂度

┌─────────────────────────────────────────────────────────────────────┐
│                    圈复杂度                                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  主要函数复杂度评估:                                               │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                                                             │   │
│  │  函数                      复杂度    评估                    │   │
│  │  ─────────────────────────────────────────────────────────  │   │
│  │  boot()                   中等      核心启动逻辑             │   │
│  │  getTimeMode()            低        简单条件判断             │   │
│  │  evaluateDistillation()   中等      多条件判断               │   │
│  │  execCommand()            低        安全检查 + 执行          │   │
│  │  compileContext()         中等      段落组装                 │   │
│  │                                                             │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  复杂度控制策略:                                                   │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  ✅ 单一职责原则                                             │   │
│  │  ✅ 函数拆分                                                 │   │
│  │  ✅ 提前返回                                                 │   │
│  │  ✅ 避免深层嵌套                                             │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

本章小结

┌─────────────────────────────────────────────────────────────────────┐
│                     第十四章 核心要点                                │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  📝 代码风格                                                        │
│     • TypeScript 严格模式                                          │
│     • ES Module 规范                                               │
│     • async/await 异步模式                                         │
│                                                                     │
│  🎨 设计模式                                                        │
│     • 单例模式:ContextKernel                                       │
│     • 缓存模式:SkillCache                                          │
│     • 策略模式:TimeMode                                            │
│     • 工厂模式:Skill Discovery                                     │
│                                                                     │
│  ⚠️ 错误处理                                                        │
│     • try-catch 模式                                               │
│     • McpError 协议错误                                            │
│     • stderr 日志输出                                              │
│                                                                     │
│  📊 代码度量                                                        │
│     • 总代码量:~2,700 行                                          │
│     • 压缩比:~5%(相对 OpenClaw)                                  │
│     • 复杂度:中等可控                                              │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

下一章:性能考量 →

← 返回目录