TypeScript设计模式:装饰器模式

装饰器模式(Decorator Pattern)是一种结构型设计模式,用于在不修改对象代码的情况下,动态为对象添加职责或行为。

设计模式原理

装饰器模式通过将额外职责封装到装饰器中,允许运行时动态组合功能,而无需修改原始代码。在工作流引擎中,装饰器模式可用于通过 @WorkflowNode 装饰器标记节点函数,结合 reflect-metadata 存储元数据,引擎通过扫描自动发现和加载节点,实现松耦合的扩展机制。

装饰器模式的结构

  1. Component(抽象组件接口) :定义节点的核心接口,如执行逻辑。
  2. ConcreteComponent(具体组件) :实现组件接口,提供基础节点功能。
  3. Decorator(装饰器) :通过 TypeScript 装饰器为函数或类添加元数据,扩展行为。
  4. Client(客户端) :通过 JSON 配置或引擎接口调用装饰后的节点。

优点

  • 动态性:运行时通过元数据动态添加节点功能,无需修改引擎核心代码。
  • 一致性:装饰器提供统一的节点定义方式,简化开发。
  • 可扩展性:支持动态添加新节点类型,适合复杂工作流。
  • 松耦合:节点逻辑与引擎解耦,增强模块化。

适用场景

  • 需要动态注册和扩展工作流节点(如触发器、动作、条件)。
  • 希望通过声明式方式(装饰器)定义功能,减少配置代码。
  • 系统需要支持外部模块或插件动态加载功能。

TypeScript 实现示例

以下示例展示如何在工作流引擎中使用 @WorkflowNode 装饰器和元数据扫描实现松耦合的节点扩展。示例模拟一个简单工作流(用户登录 → 发送欢迎邮件),通过 JSON 配置和装饰器动态注册节点。

typescript 复制代码
// 引入 reflect-metadata 用于存储和读取元数据
import 'reflect-metadata';

// 节点类型枚举
enum NodeType {
    TRIGGER = 'trigger',
    ACTION = 'action',
    CONDITION = 'condition'
}

// 元数据键
const WORKFLOW_NODE_METADATA_KEY = Symbol('workflowNode');

// 节点元数据接口
interface WorkflowNodeMetadata {
    type: NodeType;
    id: string;
    description?: string;
}

// JSON 节点配置接口
interface NodeConfigJson {
    type: NodeType;
    params: {
        id: string;
        [key: string]: any;
    };
}

// 节点函数接口
interface WorkflowNode {
    execute(params: any): string;
}

// 装饰器:WorkflowNode
function WorkflowNode(metadata: WorkflowNodeMetadata) {
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        // 存储元数据到函数
        Reflect.defineMetadata(WORKFLOW_NODE_METADATA_KEY, metadata, target, propertyKey);
        // 增强原始方法,添加日志
        const originalMethod = descriptor.value;
        descriptor.value = function (...args: any[]) {
            const result = originalMethod.apply(this, args);
            return `${result}, 已通过装饰器增强`;
        };
        return descriptor;
    };
}

// 工作流引擎:通过元数据扫描管理节点
class WorkflowEngine {
    private nodeRegistry: Map<string, { fn: Function; metadata: WorkflowNodeMetadata }> = new Map();

    // 扫描并注册装饰过的节点函数
    registerNode(target: any) {
        const methodNames = Object.getOwnPropertyNames(target.prototype).filter(
            name => name !== 'constructor' && typeof target.prototype[name] === 'function'
        );

        for (const methodName of methodNames) {
            const metadata = Reflect.getMetadata(WORKFLOW_NODE_METADATA_KEY, target.prototype, methodName);
            if (metadata) {
                const fn = target.prototype[methodName];
                this.nodeRegistry.set(metadata.id, { fn, metadata });
                console.log(`注册节点: ${metadata.id} (${metadata.type})`);
            }
        }
    }

    // 执行工作流
    executeWorkflow(nodes: NodeConfigJson[]): string[] {
        const results: string[] = [];
        for (const nodeConfig of nodes) {
            const node = this.nodeRegistry.get(nodeConfig.params.id);
            if (!node) {
                results.push(`错误:未找到节点 ${nodeConfig.params.id}`);
                continue;
            }
            try {
                const result = node.fn(nodeConfig.params);
                results.push(`节点 ${nodeConfig.params.id} 执行:${result}`);
            } catch (error) {
                results.push(`节点 ${nodeConfig.params.id} 错误:${(error as Error).message}`);
            }
        }
        return results;
    }
}

// 示例节点实现
class WorkflowNodes {
    @WorkflowNode({
        type: NodeType.TRIGGER,
        id: 'TRG001',
        description: '用户登录触发器'
    })
    userLogin(params: { event: string }): string {
        return `触发器节点(ID: ${params.id}):触发事件 ${params.event || '未知事件'}`;
    }

    @WorkflowNode({
        type: NodeType.ACTION,
        id: 'ACT001',
        description: '发送欢迎邮件'
    })
    sendWelcomeEmail(params: { operation: string }): string {
        return `动作节点(ID: ${params.id}):执行操作 ${params.operation || '未知操作'}`;
    }
}

// 客户端代码
function main() {
    // JSON 配置:用户登录 → 发送欢迎邮件
    const workflowNodes: NodeConfigJson[] = [
        {
            type: NodeType.TRIGGER,
            params: { id: 'TRG001', event: '用户登录' }
        },
        {
            type: NodeType.ACTION,
            params: { id: 'ACT001', operation: '发送欢迎邮件' }
        }
    ];

    console.log('工作流:用户登录 → 发送欢迎邮件');
    const engine = new WorkflowEngine();

    // 动态扫描并注册节点
    engine.registerNode(WorkflowNodes);

    // 执行工作流
    const results = engine.executeWorkflow(workflowNodes);
    results.forEach(result => console.log(result));
}

main();

运行结果

makefile 复制代码
工作流:用户登录 → 发送欢迎邮件
注册节点: TRG001 (trigger)
注册节点: ACT001 (action)
节点 TRG001 执行:触发器节点(ID: TRG001):触发事件 用户登录, 已通过装饰器增强
节点 ACT001 执行:动作节点(ID: ACT001):执行操作 发送欢迎邮件, 已通过装饰器增强

总结

通过 @WorkflowNode 装饰器和元数据扫描,装饰器模式在工作流引擎中实现了松耦合的节点扩展。客户端通过 JSON 配置定义工作流,引擎动态发现和执行节点,类似 NestJS 的装饰器驱动开发。此模式提升了系统的动态性、一致性和可扩展性,适合需要灵活扩展的工作流系统。

相关推荐
Java中文社群1 小时前
有点意思!Java8后最有用新特性排行榜!
java·后端·面试
代码匠心2 小时前
从零开始学Flink:数据源
java·大数据·后端·flink
掘金一周2 小时前
Flutter Riverpod 3.0 发布,大规模重构下的全新状态管理框架 | 掘金一周 9.18
前端·人工智能·后端
一涯2 小时前
页面出现空白区域
前端
moisture2 小时前
CUDA常规知识点
后端·面试
用户298698530142 小时前
Java 使用 Spire.PDF 将PDF文档转换为Word格式
java·后端
spmcor2 小时前
MinIO本地对象存储部署指南
前端
Reboot2 小时前
使用cloc统计代码行数
后端
少年纪2 小时前
前端用 pdf.js 将 PDF 渲染到 Canvas 再转图片,文字消失的坑
前端