TypeScript设计模式:解释器模式

解释器模式(Interpreter Pattern)是一种行为型设计模式,用于定义一门语言的语法,并通过解释器解析和执行该语言的表达式。它将复杂逻辑分解为语法规则和解释器,适合处理特定领域的语言或表达式,如命令行输入、查询语言或脚本解析。

设计模式原理

解释器模式的核心是为特定语言定义语法表示(抽象语法树,AST),并通过解释器递归解析和执行表达式。每个语法规则对应一个解释器类,负责处理特定的表达式部分,最终将输入转换为可执行的操作。

结构

  • 抽象表达式(AbstractExpression):定义解释接口。
  • 终端表达式(TerminalExpression):处理语言的基本元素(如命令名称)。
  • 非终端表达式(NonTerminalExpression):处理复杂语法规则,组合其他表达式。
  • 上下文(Context):包含解释器需要的全局信息(如输入字符串)。
  • 客户端(Client):构建语法树并调用解释器。

优点

  • 灵活性:易于定义和扩展新语法规则。
  • 模块化:每个表达式独立,易于维护和测试。
  • 可重用:语法规则和解释器可复用于不同上下文。
  • 领域特定:适合处理特定领域的语言或命令。

缺点

  • 复杂性增加:复杂语法可能导致大量表达式类。
  • 性能开销:解析复杂语法树可能影响性能。
  • 适用局限:仅适合小型或特定领域的语言。
  • 维护成本:大量规则可能增加维护难度。

适用场景

  • 命令行工具:如解析用户输入的测试命令。
  • 查询语言:如SQL解析、搜索表达式。
  • 脚本引擎:如自定义脚本或规则引擎。
  • 配置解析:如配置文件或DSL(领域特定语言)。

TypeScript 实现示例

我们实现一个Nest.js命令行工具,解析用户输入的测试命令(如test service TaskService method createTask),通过解释器模式调用Nest.js容器中的单例TaskService执行测试方法。代码使用TypeScript和Nest.js框架,确保类型安全和模块化。

项目结构

java 复制代码
cli-tester/
├── src/
│   ├── tasks/
│   │   ├── task.service.ts
│   │   ├── tasks.module.ts
│   ├── interpreter/
│   │   ├── command-expression.ts
│   │   ├── service-test-expression.ts
│   │   ├── test-command-expression.ts
│   ├── cli/
│   │   ├── cli.controller.ts
│   │   ├── cli.module.ts
│   ├── app.module.ts
│   ├── main.ts
├── tsconfig.json
├── package.json

1. 安装依赖

bash 复制代码
npm init -y
npm install @nestjs/core @nestjs/common @nestjs/cqrs @nestjs/core rxjs typescript @types/node
npx tsc --init

配置 tsconfig.json

json 复制代码
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "strict": true,
    "moduleResolution": "node",
    "outDir": "./dist",
    "rootDir": "./src"
  }
}

配置 package.json

json 复制代码
{
  "scripts": {
    "start": "npx ts-node src/main.ts"
  }
}

2. 定义任务服务 (tasks/task.service.ts)

typescript 复制代码
import { Injectable } from '@nestjs/common';

@Injectable()
export class TaskService {
  async createTask(id: string, title: string): Promise<string> {
    console.log(`任务服务:创建任务,ID=${id}, 标题="${title}"`);
    return `任务 ${id} 已创建`;
  }

  async updateTask(id: string, title: string): Promise<string> {
    console.log(`任务服务:更新任务,ID=${id}, 标题="${title}"`);
    return `任务 ${id} 已更新`;
  }
}

说明TaskService是Nest.js容器中的单例服务,模拟任务管理逻辑。

3. 定义任务模块 (tasks/tasks.module.ts)

typescript 复制代码
import { Module } from '@nestjs/common';
import { TaskService } from './task.service';

@Module({
  providers: [TaskService],
  exports: [TaskService],
})
export class TasksModule {}

4. 定义抽象表达式 (interpreter/command-expression.ts)

typescript 复制代码
import { INestApplication } from '@nestjs/common';

export interface CommandContext {
  input: string[];
  app: INestApplication;
}

export interface CommandExpression {
  interpret(context: CommandContext): Promise<string>;
}

说明CommandExpression定义解释接口,CommandContext包含用户输入和Nest.js应用上下文。

5. 定义终端表达式 (interpreter/service-test-expression.ts)

typescript 复制代码
import { CommandExpression, CommandContext } from './command-expression';
import { TaskService } from '../tasks/task.service';

export class ServiceTestExpression implements CommandExpression {
  constructor(private serviceName: string, private methodName: string, private args: string[]) {}

  async interpret(context: CommandContext): Promise<string> {
    if (this.serviceName !== 'TaskService') {
      throw new Error(`未知服务:${this.serviceName}`);
    }

    const service = context.app.get(TaskService);
    if (!service[this.methodName]) {
      throw new Error(`未知方法:${this.methodName}`);
    }

    console.log(`解释终端表达式:测试服务 ${this.serviceName} 的方法 ${this.methodName}`);
    return await service[this.methodName](...this.args);
  }
}

说明ServiceTestExpression处理具体服务测试命令,调用Nest.js容器中的TaskService方法。

6. 定义非终端表达式 (interpreter/test-command-expression.ts)

typescript 复制代码
import { CommandExpression, CommandContext } from './command-expression';
import { ServiceTestExpression } from './service-test-expression';

export class TestCommandExpression implements CommandExpression {
  private expressions: CommandExpression[] = [];

  interpret(context: CommandContext): Promise<string> {
    const [keyword, type, ...rest] = context.input;

    if (keyword !== 'test' || type !== 'service') {
      throw new Error('无效命令:仅支持 "test service" 格式');
    }

    const [serviceName, methodName, ...args] = rest;
    console.log(`解释非终端表达式:解析命令 ${context.input.join(' ')}`);
    this.expressions.push(new ServiceTestExpression(serviceName, methodName, args));

    return this.expressions[0].interpret(context);
  }
}

说明TestCommandExpression解析复合命令(如test service TaskService method createTask),构建终端表达式。

7. 定义命令行控制器 (cli/cli.controller.ts)

typescript 复制代码
import { Controller, Post, Body, Inject } from '@nestjs/common';
import { INestApplication } from '@nestjs/common';
import { TestCommandExpression } from '../interpreter/test-command-expression';

@Controller('cli')
export class CliController {
  constructor(@Inject('APP_INSTANCE') private readonly app: INestApplication) {}

  @Post('execute')
  async executeCommand(@Body() body: { command: string }): Promise<string> {
    console.log(`控制器:接收命令 "${body.command}"`);
    const input = body.command.split(' ');
    const context = { input, app: this.app };
    const expression = new TestCommandExpression();
    return await expression.interpret(context);
  }
}

说明CliController作为客户端,接收命令行输入,构建上下文并调用解释器。

8. 定义命令行模块 (cli/cli.module.ts)

typescript 复制代码
import { Module } from '@nestjs/common';
import { CliController } from './cli.controller';

@Module({
  controllers: [CliController],
})
export class CliModule {}

9. 定义根模块 (app.module.ts)

typescript 复制代码
import { Module } from '@nestjs/common';
import { TasksModule } from './tasks/tasks.module';
import { CliModule } from './cli/cli.module';

@Module({
  imports: [TasksModule, CliModule],
  providers: [
    {
      provide: 'APP_INSTANCE',
      useFactory: () => null, // 占位符,实际由main.ts注入
    },
  ],
})
export class AppModule {}

10. 入口文件 (main.ts)

typescript 复制代码
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 注入Nest应用实例
  app.get('APP_INSTANCE').provide = () => app;
  await app.listen(3000);
  console.log('应用启动在 http://localhost:3000');
}
bootstrap();

11. 编译与运行

bash 复制代码
npx tsc
npm start

12. 测试示例

使用工具(如Postman)发送HTTP请求测试:

  • 测试创建任务

    bash 复制代码
    POST http://localhost:3000/cli/execute
    Content-Type: application/json
    {
      "command": "test service TaskService createTask task1 新任务"
    }
  • 测试更新任务

    bash 复制代码
    POST http://localhost:3000/cli/execute
    Content-Type: application/json
    {
      "command": "test service TaskService updateTask task1 更新任务"
    }

运行后,控制台输出类似:

bash 复制代码
应用启动在 http://localhost:3000
控制器:接收命令 "test service TaskService createTask task1 新任务"
解释非终端表达式:解析命令 test service TaskService createTask task1 新任务
解释终端表达式:测试服务 TaskService 的方法 createTask
任务服务:创建任务,ID=task1, 标题="新任务"
控制器:接收命令 "test service TaskService updateTask task1 更新任务"
解释非终端表达式:解析命令 test service TaskService updateTask task1 更新任务
解释终端表达式:测试服务 TaskService 的方法 updateTask
任务服务:更新任务,ID=task1, 标题="更新任务"

解释输出

  • 控制器接收命令,构建上下文。
  • TestCommandExpression解析命令,创建ServiceTestExpression
  • ServiceTestExpression调用Nest.js容器中的TaskService,执行测试方法。
  • 解释器模式将命令解析为可执行操作,解耦输入与服务调用。

总结

解释器模式的优点在于其灵活性、模块化、可重用性和领域特定性。易于定义和扩展语法规则;每个表达式独立,易于维护;语法规则可复用;适合处理特定领域的语言。该模式特别适用于命令行工具、查询语言、脚本引擎和配置解析场景,如Nest.js命令行测试工具、SQL解析器、自定义脚本和DSL解析。

相关推荐
易元3 小时前
模式组合应用-享元模式
后端·设计模式
对象存储与RustFS3 小时前
零基础小白手把手教程:用Docker和MinIO打造专属私有图床,并完美搭配PicGo
后端
Codebee3 小时前
魔改 OneCode-RAD 实现 LLM 编程:打造自然语言驱动的低代码助手
前端·人工智能·前端框架
我是日安3 小时前
从零到一打造 Vue3 响应式系统 Day 11 - Effect:Link 节点的复用实现
前端·vue.js
德育处主任3 小时前
文字识别:辛辛苦苦练模型,不如调用PP-OCRv5
后端·图像识别
TeamDev3 小时前
用一个 prompt 搭建带 React 界面的 Java 桌面应用
java·前端·后端
知其然亦知其所以然3 小时前
国产大模型也能无缝接入!Spring AI + 智谱 AI 实战指南
java·后端·算法
悟空码字3 小时前
阿里通义开源啦,源码地址+部署脚本,让AI会“做研究”
后端·阿里巴巴