装饰器模式(Decorator Pattern)是一种结构型设计模式,用于在不修改对象代码的情况下,动态为对象添加职责或行为。
设计模式原理
装饰器模式通过将额外职责封装到装饰器中,允许运行时动态组合功能,而无需修改原始代码。在工作流引擎中,装饰器模式可用于通过 @WorkflowNode
装饰器标记节点函数,结合 reflect-metadata
存储元数据,引擎通过扫描自动发现和加载节点,实现松耦合的扩展机制。
装饰器模式的结构
- Component(抽象组件接口) :定义节点的核心接口,如执行逻辑。
- ConcreteComponent(具体组件) :实现组件接口,提供基础节点功能。
- Decorator(装饰器) :通过 TypeScript 装饰器为函数或类添加元数据,扩展行为。
- 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 的装饰器驱动开发。此模式提升了系统的动态性、一致性和可扩展性,适合需要灵活扩展的工作流系统。