mcpField
I
implementation-functional-patterns

implementation-functional-patterns

github.com/pproenca开发218125 次安装11 周前更新
代码仓库

关于 implementation-functional-patterns

面向 TypeScript 的参考技能,帮助选择函数式实现来替代基于类的 GoF 模式。它将 22 种模式映射到函数、标记联合、流和包装函数等形式,并说明放置规则与性能取舍。

主要功能

  1. 01将 GoF 模式映射为函数式方案
  2. 02涵盖工厂、闭包与高阶函数
  3. 03说明管道、流与包装函数
  4. 04用标记联合处理状态与访问者模式
  5. 05提供放置规则和性能取舍

适用场景

  1. 01将基于类的模式重构为 TypeScript 函数
  2. 02选择状态或组合数据的表示方式
  3. 03审查流链和累积循环
  4. 04检查 TSX 与钩子中的 Lambda 放置

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/pproenca/dot-skills --skill implementation-functional-patterns

安装后在 Claude Code 中输入/implementation-functional-patterns确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

AGENTS.md
metadata.json
references/
_sections.md
cache-weakmap-over-flyweight.md
closure-as-command.md
create-factory-function-over-factory-classes.md
create-fluent-immutable-builder.md
create-module-scope-over-singleton.md
hof-lambda-as-strategy.md
match-tagged-union-over-state-visitor-composite.md
pipe-compose-over-decorator.md
pipe-pipeline-over-chain-of-responsibility.md
place-module-scope-pure-transformers.md
signal-event-emitter-over-mediator-and-observer.md
stream-flatmap-over-nested-loops.md
stream-lazy-iteration-for-large-or-infinite.md
stream-prefer-single-pass-over-chained-passes.md
stream-reduce-over-imperative-accumulation.md
wrap-function-over-adapter-and-facade.md
wrap-proxy-native-or-hof.md

详细介绍

TypeScript 函数式模式

这份参考指南介绍了在惯用 TypeScript 中取代四人帮目录的函数式形式。它与 implementation-design-patterns 配套:如果答案是类,请阅读那份指南;如果答案是函数、带标签联合类型或小型数据结构,请阅读这份指南。

TypeScript 拥有一等函数、可辨识联合类型、结构化类型,以及几乎无需额外语法即可使用的闭包。这意味着,GoF 的 22 种模式——在目录中以类层次结构的形式编写,因为源材料面向 Java/C#——在实际 TS 代码中会归并为少得多的函数式形式。若干模式有相同的函数式解法:带标签联合类型可涵盖 State、Visitor 和 Composite;工厂函数可涵盖 Factory Method、Abstract Factory、Prototype 和 Memento;事件发射器可涵盖 Mediator 和 Observer;包装函数可涵盖 Adapter 和 Facade。本指南将说明这些归并形式、它们所隐含的代码放置规则,以及性能权衡。

适用场景

  • 重构工厂类层次结构、返回一系列产品的 AbstractFactory,或具有 clone() / Memento 保存与恢复功能的类
  • 重构具有 private constructor + static getInstance 的 Singleton 类
  • 重构用于配置对象的可变 Builder 类,尤其是包含许多可选字段的类
  • 重构变化仅在单个方法上的 Strategy / Template Method / Bridge 类层次结构
  • 替换 Chain of Responsibility 类链或 Decorator 包装类栈
  • 使用流方法、生成器或惰性迭代器辅助函数,替换自定义 Iterator 类
  • 使用存储在队列中的闭包替换 Command 类(不需要撤销或序列化时)
  • 替换仅用于转发调用或隐藏子系统编排的 Adapter / Facade 类
  • 使用原生 JS Proxy(用于透明拦截)或 HOF 包装器(用于选择性包装)替换 Proxy 类
  • 使用 Map/WeakMap 缓存 + 工厂函数替换 Flyweight 工厂类
  • 使用可辨识联合类型 + 穷尽式 match 函数替换 State / Visitor / Composite 类层次结构
  • 使用事件发射器或响应式信号替换 Mediator / Observer 类
  • 检查 TSX 中内联在 JSX、Hook 依赖项或经 memo 处理的子组件属性中的 lambda——放置位置决定其标识
  • 识别可用 reduce、Object.groupBy 或 flatMap 更清晰地表达的命令式 for 循环及其可变累加器

规则类别

#类别影响规则主题
1创建型替代方案(create)HIGH3工厂函数、模块作用域单例、流畅的不可变构建器
2高阶函数(hof)HIGH1传递函数而非类
3管道与组合(pipe)HIGH2组合小型函数:pipe(数据流)、compose(包装器分层)
4流方法(stream)HIGH4map/filter/flatMap/reduce、惰性迭代、单次遍历链
5包装器(wrap)HIGH2用于 Adapter/Facade 的包装函数;用于 Proxy 的原生 Proxy 或 HOF
6缓存与共享(cache)HIGH1用 Map/WeakMap + 工厂函数替代 Flyweight 类
7模式匹配(match)HIGH1用于 State/Visitor/Composite 的可辨识联合类型 + 穷尽式匹配
8信号与事件发射器(signal)HIGH1用于 Mediator/Observer 的事件发射器 / 信号
9放置位置与标识(place)HIGH1lambda 所在的位置会控制行为
10作为数据的闭包(closure)MEDIUM1携带自身状态的函数

10 个类别共 17 条规则,涵盖全部 22 种 GoF 模式(部分模式共享同一种函数式解法——请参阅下方的 GoF → 函数式映射)。

GoF → 函数式映射

每种 GoF 模式在符合惯用 TS 风格的代码中的函数式实现方式完整映射。查阅此表可查找特定模式的规则;查阅上方的类别可按函数式技术查找规则。

GoF 组GoF 模式函数式等价实现规则
创建型工厂方法返回带标签对象的函数create-factory-function-over-factory-classes
抽象工厂返回构造函数记录的函数由工厂函数规则涵盖
生成器对象字面量 + Partial<T>,或流畅式不可变对象create-fluent-immutable-builder
原型structuredClone / 展开语法 / Immer produce由工厂函数规则涵盖
单例模块作用域常量、惰性 ??= 记忆化create-module-scope-over-singleton
结构型适配器转换形状的包装函数wrap-function-over-adapter-and-facade
桥接以实现为参数的高阶函数由 hof-lambda-as-strategy 涵盖
组合判别联合 + 递归函数由带标签联合规则涵盖
装饰器compose(withA, withB, withC)(target)pipe-compose-over-decorator
外观隐藏子系统的单个高阶函数由包装函数规则涵盖
享元Map/WeakMap + 工厂函数cache-weakmap-over-flyweight
代理原生 Proxy 或高阶函数包装器wrap-proxy-native-or-hof
行为型责任链pipe(fn1, fn2) / 数组折叠pipe-pipeline-over-chain-of-responsibility
命令闭包 () => voidclosure-as-command
迭代器流式方法 + 生成器stream-flatmap-over-nested-loops、stream-reduce-over-imperative-accumulation、stream-lazy-iteration-for-large-or-infinite、stream-prefer-single-pass-over-chained-passes
中介者事件发射器 / 信号signal-event-emitter-over-mediator-and-observer
备忘录通过 structuredClone 创建不可变快照由工厂函数规则涵盖
观察者事件发射器 / 信号 / RxJS由事件发射器规则涵盖
状态判别联合 + 穷尽匹配match-tagged-union-over-state-visitor-composite
策略Lambdahof-lambda-as-strategy
模板方法接受步骤回调的高阶函数由高阶函数 Lambda 规则涵盖
访问者判别联合 + 匹配函数由带标签联合规则涵盖

如何使用

  1. 查找模式。 如果你知道要采用哪种 GoF 模式,可在上方的 GoF → 函数式映射 中查找并阅读链接的规则。如果你只知道问题症状(带累加器的循环、返回类的类、三个 setter),下方的快速参考按函数式技术对规则进行了归类。
  2. 阅读“何时不应采用”。 每条规则都会列出类形式仍然更适用的特定条件。这项技能是父技能的补充,而非否定——如果序列化、运行时内省、带类型的模式间关系、横切状态、框架集成或生命周期所有权有此要求,就保留类。
  3. 检查 TSX 中的身份假设。 如果代码位于 TSX 文件中或在 Hook 内运行,也请阅读 place-* 规则——位置决定了 memo、useEffect 和 React Compiler 能否发挥作用。
  4. 注意性能。 每条规则都有 ### Performance trade-offs 部分,用数据说明时间、内存和分配成本。大多数函数式形式的性能与类形式相当;少数形式(链式流、flatMap)存在实际的常数级开销,在热点路径中可能很重要。

快速参考

1. 创建型替代方案
  • create-factory-function-over-factory-classes — 用返回带标签对象的函数替代 Factory 类层次结构。涵盖 Factory Method、Abstract Factory、Prototype 和 Memento。"我会写 new FooFactory().create(),但返回带标签对象的函数更简洁,也能进行 Tree Shaking。" — HIGH
  • create-module-scope-over-singleton — 使用 export const x = … 或延迟求值的 ??=,替代 class X { private static instance; getInstance() }。ES 模块本身就是单例;在 TS 中使用类形式有悖惯例。"我需要这种对象恰好只有一个——配置、DB 客户端、日志记录器。" — HIGH
  • create-fluent-immutable-builder — 简单场景使用对象字面量 + Partial<T>;需要跟踪类型状态的 DSL 则使用流畅的不可变构建器(每个方法都返回一个新的构建器)。"我的构造函数有 10 个参数/我的 Builder 类有 8 个 setter。" — HIGH
2. 高阶函数
  • hof-lambda-as-strategy — 传入比较器/谓词/转换器 Lambda,而不是定义 Strategy 类。也涵盖 Template Method(带步骤回调的 HOF)和 Bridge(由实现参数化的 HOF)。"我的 Strategy 接口只有一个方法。" — HIGH
3. 管道与组合
  • pipe-pipeline-over-chain-of-responsibility — 使用 pipe(validate, authorize, parse)(req) 或对处理器数组进行折叠,而不是创建 Handler 类的链表。"我的 CoR 链中的处理器各自执行一种转换,并传递结果。" — HIGH
  • pipe-compose-over-decorator — 使用 compose(withLogging, withCache, withAuth)(handler)——每个包装器都是 (handler) => handler,而不是 Decorator 类。从右到左的顺序读起来像自顶向下的类堆栈。"我想为这个处理器添加日志记录 + 缓存 + 身份验证。" — HIGH
4. 流方法
  • stream-flatmap-over-nested-loops — 使用 .flatMap 执行一对多转换,而不是 for + push 或 map().reduce(concat)。“对每个用户,展开其所有订单,然后收集起来。” — HIGH
  • stream-reduce-over-imperative-accumulation — 使用 reduce / Object.groupBy / Map.groupBy,而不是 let acc = …; for (…) acc[…] = …。这是实际 TypeScript 中最常见的函数式模式:求和、计数、索引、直方图。“我正在循环中累加总数 / 构建索引 / 分组映射。” — HIGH
  • stream-lazy-iteration-for-large-or-infinite — 只需要结果的前缀时,使用生成器或 TC39 Iterator 辅助方法(Iterator.from(arr).filter(p).take(10).toArray())。复杂度为 O(matched-needed),而不是 O(n)。“从庞大或无界的数据源中取出前 N 个匹配项。” — HIGH
  • stream-prefer-single-pass-over-chained-passes — 输入量大或链式操作位于热点路径时,将 .filter().map().filter() 合并为一次 reduce 或 for-of。三次遍历变为一次;峰值内存减半;实测热点路径快 2–5 倍。“这条链会在每次请求 / 渲染时,对数千个项目运行。” — HIGH
5. 包装器
  • wrap-function-over-adapter-and-facade — 使用包装函数将一个接口转换为另一个接口(适配器),或隐藏子系统的编排过程(外观)。“我会写一个 class XAdapter implements Y,其中每个方法都只是单行转发。” — HIGH
  • wrap-proxy-native-or-hof — 使用原生 Proxy 透明拦截动态键,或使用高阶函数包装器有选择地包装方法级操作。“我想要延迟加载 / 访问控制 / 拦截功能,但不想写一个 Proxy 类。” — HIGH
6. 缓存与共享
  • cache-weakmap-over-flyweight — 使用 WeakMap/Map 缓存 + 工厂函数,而不是享元工厂类。当键超出作用域时,WeakMap 会自动清理。“我正在分配数百万个共享状态的相似对象。” — HIGH
7. 模式匹配
  • match-tagged-union-over-state-visitor-composite — 使用可辨识联合 + 对标签执行 switch + assertNever,涵盖状态(每种状态一个类)、访问者(双重分派)和组合(叶节点 / 分支)。这可能是 TypeScript 中收益最高的单一函数式模式。“我的类有一个针对 kind 的超大 switch / 我有 N 个状态类 / 我会为 AST 编写一个访问者。” — HIGH
8. 信号与事件发射器
  • signal-event-emitter-over-mediator-and-observer — 使用事件发射器(mitt、EventEmitter)或响应式信号(Solid、Preact signals、Zustand),而不是 Subject / Observer 类或中央 Mediator 类。“许多对象需要在某个对象变化时做出响应 / 表单字段需要协同工作 / 需要跨组件事件。” — HIGH
9. 放置位置与标识
  • place-module-scope-pure-transformers — 将纯转换器 lambda 放在模块作用域(标识稳定、可复用、可进行摇树优化)。仅当需要捕获某些内容时,才将它们嵌套在函数中。“这个 (s) => s.toLowerCase() 不需要放在组件主体中。” — HIGH
10. 将闭包用作数据
  • closure-as-command — 在队列 / 历史记录 / 回调列表中存储 () => void 闭包,而不是带有 execute() 的 Command 类。“我需要一个延迟操作队列,而且永远不需要检查或序列化这些操作。” — MEDIUM

如何选择:类还是函数

当至少满足以下一项时,类形式(参见 implementation-design-patterns)所带来的额外开销才值得:

  • 序列化 — 必须在进程重启后继续存在或通过网络传输的命令或备忘录
  • 运行时注册表 / 自省 — 系统会枚举已知策略、在选择器中显示这些策略,或附加元数据
  • 模式间的类型化关系 — 遍历 AST 的 Visitor,其中节点类型会相互引用;状态会相互引用的状态机;具有类型化角色的 Mediator
  • 横切状态 — “变化项”除了单次调用外,还携带自身的配置、生命周期或不变量
  • instanceof 的稳定标识 — 对一组有限的具名类进行穷尽匹配(较少见;判别联合通常更合适)
  • 生命周期所有权 — 对象拥有某种资源(连接、文件句柄、可释放对象),且需要集成 using / Symbol.dispose
  • 框架集成 — ORM、DI 容器、基于装饰器的库,以及基于类的 RxJS 服务都需要类

否则,默认使用函数(或带标签的联合,或数据结构)。类只是给值套上一层繁文缛节,并无实际收益。

参考资料

  1. MDN — Array.prototype
  2. TC39 — Iterator Helpers 提案
  3. Mostly Adequate Guide to Functional Programming(Brian Lonsdorf)
  4. TC39 — Pipeline Operator 提案
  5. MDN — 闭包
  6. TS Handbook — 判别联合
  7. MDN — Proxy
  8. MDN — WeakMap
  9. MDN — structuredClone

评论

支持 Markdown