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 的装饰器驱动开发。此模式提升了系统的动态性、一致性和可扩展性,适合需要灵活扩展的工作流系统。

相关推荐
app出海创收老李7 分钟前
海外独立创收日记(4)-第一笔汇款
前端·后端·程序员
Takklin7 分钟前
React JSX 转换原理与 GSR 实现解析
前端·react.js
咕白m62517 分钟前
Python 将 Excel 转换为图片:实现数据可视化
后端·python
苏打水com25 分钟前
字节跳动前端业务:从「短视频交互」到「全球化适配」的技术挑战
前端·音视频
又是忙碌的一天34 分钟前
前端学习 JavaScript
前端·javascript·学习
Jagger_1 小时前
Cursor + Apifox MCP:告别手动复制接口,AI 助你高效完成接口文档开发
前端
蓑笠翁0011 小时前
Django REST Framework 全面指南:从模型到完整API接口开发
后端·python·django
鸽鸽程序猿1 小时前
【项目】基于Spring全家桶的论坛系统 【下】
后端·spring·restful
IT_陈寒1 小时前
Redis性能优化:5个被低估的配置项让你的QPS提升50%
前端·人工智能·后端
Hilaku1 小时前
重新思考CSS Reset:normalize.css vs reset.css vs remedy.css,在2025年该如何选?
前端·css·代码规范