‘Dynamic Pipeline’模式:用于实时处理的可变方法链
I’m happy to translate the article for you, but I need the full text of the post (the body of the article) in order to do so. Could you please paste the content you’d like translated? Once I have it, I’ll keep the source link unchanged and translate everything else into Simplified Chinese while preserving the original formatting, markdown, and any code blocks or URLs.
什么是动态管道?
Dynamic Pipeline 是一种方法链模式,允许您在运行时 添加、删除和更新 处理步骤,同时保持 API 简洁、流畅。
Processor (Mutable)
+------------------------------------+
| |
Input ((Data)) ---------> | Filter A → Filter B → Filter C | ---------> Output ((Data))
| |
+------------------------------------+
^ ^ ^
| | |
User Settings / Events -------------+---------+---------+
(add / update / remove)
示例用法
// Incrementally add processing steps
const processor = new Processor()
.addFilterA(param1) // + Process A
.addFilterB(param2) // + Process B
.addFilterC(param3); // + Process C
// Update parameters later
processor.updateFilterA(newParam);
// Subtract (remove) a process
processor.removeFilter('B');
关键特性
| 特性 | 描述 |
|---|---|
Addition (add) | 可以随时追加新的处理步骤。 |
Subtraction (remove) | 可以动态地拆除已有的步骤。 |
Update (update) | 可以在不重建整个管道的情况下修改特定步骤的参数。 |
| Order‑sensitive | 添加的顺序决定执行顺序。 |
| Immediate use | 不需要调用 .build();处理器始终处于可执行状态。 |
隐喻:人类成长与经历
const person = new Person()
.addEducation('University')
.addSkill('Programming')
.addExperience('Living abroad')
.addTrauma('Major failure');
正如人类不断进化,你可以“添加”能力和经历。随后,生活会进一步塑造我们:
// Forgetting a skill (remove)
person.removeSkill('Programming');
// Leveling up through practice (update)
person.updateSkill('Programming', { level: 'expert' });
// Losing something precious (remove)
person.removeExperience('Living abroad');
根据具体情境,你可以 exercise(练习)、update(更新)或 let go of(放弃)这些属性。
与现有模式的比较
| 模式 | 操作 | 顺序相关性 | 运行时变更 |
|---|---|---|---|
| Builder | 配置 | 不相关 | 在 .build() 之后不可变 |
| Decorator | 包装 | 相关 | 一旦应用后难以“解包” |
| Middleware | 注册 | 相关 | 首次注册后通常是静态的 |
RxJS pipe | 转换 | 相关 | 不可变(始终返回新实例) |
| Chain of Responsibility | 链接 | 相关 | “一个处理后停止链” |
| Dynamic Pipeline | 添加/删除/更新 | 相关 | 完全可变 |
动态管道的优势在于它 在结构化、有序的管道与运行时调整的灵活性之间取得平衡。
我的使用方式:笔画稳定
在绘图应用程序中,笔画稳定通过对原始指针输入应用基于卷积的过滤器(例如,高斯平滑、卡尔曼滤波)来实现。这些过滤器必须对用户交互作出响应。
const pointer = new StabilizedPointer()
.addNoiseFilter(1.5)
.addKalmanFilter(0.1)
.addGaussianFilter(5);
// Adjusting settings via the UI
settingsPanel.onChange(settings => {
pointer.updateNoiseFilter(settings.noiseThreshold);
if (!settings.useSmoothing) {
pointer.removeFilter('gaussian');
}
});
// Dynamic adjustments based on pen velocity
pointer.onVelocityChange(velocity => {
if (velocity > 500) {
// Prioritize performance by removing heavy filters during high‑speed motion
pointer.removeFilter('gaussian');
} else {
pointer.addGaussianFilter(5);
}
});
该模式并不限于此领域;任何需要可变且有序处理链的情况都可以受益。
Source: …
可能的实现策略
1. 基于数组的管道管理
将管道保存在一个简单的数组中。这自然地保留了添加的顺序,并使遍历变得直接。
class Processor {
constructor() {
this.steps = []; // [{ id: 'A', fn: filterA }, …]
}
addFilterA(param) {
this.steps.push({ id: 'A', fn: data => filterA(data, param) });
return this;
}
// …其他 add/remove/update 方法…
execute(input) {
return this.steps.reduce((data, step) => step.fn(data), input);
}
}
2. 通过类型或唯一 ID 进行标识
每个步骤可以通过 类型字符串('validation'、'transform')或 唯一标识符(UUID)来标识。当与映射结合使用时,这使得 remove/update 操作的时间复杂度为 O(1)。
class Processor {
constructor() {
this.steps = []; // 有序列表
this.index = new Map(); // id → 在 steps 中的位置
}
addStep(id, fn) {
this.steps.push({ id, fn });
this.index.set(id, this.steps.length - 1);
return this;
}
removeStep(id) {
const pos = this.index.get(id);
if (pos !== undefined) {
this.steps.splice(pos, 1);
this.index.delete(id);
// 重新索引后续项目…
}
return this;
}
// updateStep、execute 等
}
3. 带内部可变性的近似不可变 API
提供 流式、可变的外观,同时在内部保持不可变表示(例如写时复制)。这在不牺牲线程安全性的前提下,提供了可变性的易用性。
class Processor {
constructor(steps = []) {
this._steps = steps; // 永不直接修改
}
addFilterA(param) {
const newStep = data => filterA(data, param);
return new Processor([...this._steps, { id: 'A', fn: newStep }]);
}
// remove / update 返回新的 Processor 实例
}
然后你可以决定是使用 真正的可变性(如前两种策略)还是 仍然给调用者一种可变感受的函数式风格。
摘要
动态管道模式为您提供:
- 流畅、可链式的 API – 易于阅读和编写。
- 运行时可变性 – 可随时添加、删除或更新步骤。
- 顺序保持 – 调用顺序决定执行顺序。
- 即时可用 – 无需单独的“构建”步骤。
它介于 Builder 的静态特性和 Middleware/Decorator 堆栈的灵活性之间,是任何需要可变、有序处理链的领域(图形、数据验证、事件处理等)的实用工具。
使用唯一 ID 管理类型
为每种类型使用唯一标识符可以使系统更健壮。无需依赖临时的字符串比较,您可以将标识符与对象一起存储,并在需要时查找。
将管道缓存为单个函数
对于高频执行场景,在配置更改时将整个管道缓存为单个函数可能有益。这样做可能会降低运行时开销,尽管此方法在实践中尚未得到广泛测试。
最后思考
我不确定这是否算作正式的“模式”——它可能更接近一种架构惯例,或仅仅是许多开发者直觉上已采用的常识性技术。
话虽如此,我发现这种方法对我将简洁、可读的 API 与动态、实时调整需求相结合的特定用例非常有帮助。
如果这种方法已有既定名称,或您发现维护可变管道的潜在陷阱,恳请在评论中分享您的见解。感谢阅读。