如何以不可变/函数式编程方式实现多路径可变修改(适用于PDF生成场景)
函数式范式下PDF生成流程的落地方案
核心用 RWS(Reader/Writer/State)三合一的纯函数结构 即可完全覆盖你的需求,全程没有可变修改,关注点天然分离:
- Writer 端负责暂存生成的PDF内容,不执行实际写入
- State 端负责维护不可变的object id映射表、content_for注入端口数据
- Reader 端可按需传入全局配置(比如页面尺寸、字体参数等,不需要可以省略)
具体落地步骤
1. 定义不可变的上下文结构
先定义两个纯数据结构,全程只会返回新副本,不会修改原对象:
// 以类JS的伪代码举例,任意语言都可以对应实现 const initialPdfState = { objectIdMap: new Map(), // 懒加载object id映射表 contentPorts: new Map() // content_for注入端口,key为端口名,value为注入内容 }
2. 封装渲染操作的返回格式
所有渲染逻辑的返回值统一为 [contentFragment, stateUpdater],分别对应本次渲染生成的内容片段、对全局状态的修改函数:
// 示例:渲染一个PDF对象,同时注册object id function renderPdfObject(content, objId) { const contentFragment = content; const stateUpdater = (prevState) => ({ ...prevState, objectIdMap: new Map(prevState.objectIdMap).set(content, objId) }) return [contentFragment, stateUpdater] } // 示例:实现content_for注入能力 function contentFor(portName, content) { const contentFragment = ''; // 注入操作不直接输出内容 const stateUpdater = (prevState) => { const prevPortContent = prevState.contentPorts.get(portName) || '' return { ...prevState, contentPorts: new Map(prevState.contentPorts).set(portName, prevPortContent + content) } } return [contentFragment, stateUpdater] } // 示例:渲染指定端口的所有注入内容 function renderPort(portName) { const stateUpdater = (prevState) => prevState // 不修改状态 const contentFragment = (prevState) => prevState.contentPorts.get(portName) || '' return [contentFragment, stateUpdater] }
3. 批量执行渲染逻辑,合并结果
你提到的用map同时生成内容和更新对象的思路,只要在遍历后做一次合并即可:
// 假设你有一组渲染任务列表 const renderTasks = [ renderPdfObject('%PDF-1.7', 1), contentFor('header', 'PDF标题'), renderPort('header'), // 其他渲染逻辑... ] // 一次性执行所有纯计算,不做任何IO操作 let allContent = '' let currentState = initialPdfState for (const [fragment, updater] of renderTasks) { // 处理动态内容(比如端口渲染的结果) const actualFragment = typeof fragment === 'function' ? fragment(currentState) : fragment allContent += actualFragment currentState = updater(currentState) } // 最后统一执行IO,满足先写长度再写内容的要求 writeToPdfFile(allContent.length.toString()) writeToPdfFile(allContent) // 最终的object id映射、所有端口数据都存在currentState里,可按需使用
思路本质
所有可变操作、IO操作都被剥离到最后一步统一执行,中间的内容生成、状态更新全都是纯计算,没有任何副作用,天然可测试、可调试。你需要的content_for能力本质就是把分散的内容收集到状态的指定槽位,最后再统一取出渲染,完全不需要依赖框架提供的特殊注入能力。
内容的提问来源于stack exchange,提问作者Lance Pollard
相关产品推荐
相关产品推荐

