164、【Agent】【OpenCode】TuiThreadCmd(工厂设计对比)

【声明】本博客所有内容均为个人业余时间创作,所述技术案例均来自公开开源项目(如Github,Apache基金会),不涉及任何企业机密或未公开技术,如有侵权请联系删除

标题

164、【Agent】【OpenCode】TuiThreadCmd(工厂设计对比)

背景

上篇 blog

【Agent】【OpenCode】TuiThreadCmd(原生 fetch)

分析了原生 fetch 既是具体的函数实现,也有对应的类型声明,在具体实现上,fetch 是浏览器/Node.js/Deno 等宿主环境用 C++ / Rust / V8 等底层语言写死的真实函数,通常无法看到其源码,而在类型声明时,TypeScript 本身不提供任何运行时功能,它只负责描述形状,在之前的 createWorkerFetch 工厂函数的例子里,用了 typeof fetch 作为返回值类型,只借用了它的类型签名,而没有借用它的底层 C++ 实现,createWorkerFetch 的返回值就是要替代原生 fetch,下面继续分析

OpenCode

最后再对比下它俩,它们都用了工厂模式,但解决的问题和类型系统的玩法完全不同

简单来说:RPC 泛型推导是"造数据/造类型 ",而 createWorkerFetch 是"造函数/造行为"。

下面是它们的深度对比:


🔗 联系:为什么都用工厂模式?

  • 依赖注入 / 闭包绑定 :两者都需要在创建时"记住 "一个外部依赖(RpcClient)。工厂函数把 client 关进闭包,返回的东西就能随时使用它,而不需要调用方每次都传参。
  • 关注点分离:把"配置/初始化"和"实际使用"分成两步。先配置好通道,再专心发请求/调方法。
  • 运行时与编译时的桥梁 :都在运行时 生成具体的对象/函数,同时在编译时通过 TS 类型系统约束生成的产物。

⚔️ 区别:核心差异在哪?

维度 RPC 泛型推导工厂 createWorkerFetch 工厂
产出物 带类型的代理对象 / 方法映射 一个符合标准签名的函数
类型来源 动态推导:从 Schema/接口定义中提取,每个方法的参数和返回值类型都不同 静态借用:直接复用原生 typeof fetch,签名固定不变
泛型的角色 核心主角:T 决定了返回对象的每一个字段和方法的类型 几乎不用泛型:重点是结构兼容,而非类型变换
设计目标 让 IDE 能智能提示出所有 RPC 方法名、参数、返回值 让假 fetch 能无缝替换真 fetch,对第三方库透明
类型复杂度 高:涉及映射类型、条件类型、索引签名等高级技巧 低:只需保证函数签名一致即可

📌 在实际项目中,它们经常组合使用

在一个完整的 Worker RPC 架构里,这两者往往是配合关系:

javascript 复制代码
// 1. RPC 泛型工厂:生成带类型的业务 API
const api = createRpcClient<MyServiceSchema>(client)
await api.getUser({ id: 1 }) // ✅ 有完整类型提示

// 2. Fetch 工厂:生成标准的网络层
const workerFetch = createWorkerFetch(client)

// 3. 组合:把假 fetch 传给依赖标准 fetch 的库
const queryClient = new QueryClient({ 
  defaultOptions: { queryFn: ({ queryKey }) => workerFetch(`/api/${queryKey}`) } 
})

一句话总结

两者都是工厂,但 RPC 泛型工厂的核心价值是 "类型推导 "------让 TS 知道有什么方法;createWorkerFetch 的核心价值是 "接口兼容 "------让代码感觉不到底层被换了。前者解决的是"开发体验",后者解决的是"架构透明性"。


下面来对比下 C 语言对这种设计模式的支持,先说结论:C 语言能实现"工厂返回函数"这个运行时行为,但完全无法实现 typeof fetch 这种编译时契约

C 语言能做到的部分(运行时)

C 有函数指针,完全可以写一个工厂返回函数:

javascript 复制代码
// 1. 定义函数类型(类似 typeof fetch)
typedef Response* (*FetchFn)(const char* url, const RequestInit* init);

// 2. 工厂函数:闭包用结构体模拟
typedef struct { RpcClient* client; } FetchContext;

FetchFn createWorkerFetch(RpcClient* client) {
    // ⚠️ C 没有闭包!必须手动分配上下文
    FetchContext* ctx = malloc(sizeof(FetchContext));
    ctx->client = client;
    
    // 返回一个知道如何使用 ctx 的函数指针
    // 但这里有个致命问题 ↓
}

C 语言做不到的部分(也是核心差异)

能力 TypeScript C 语言
闭包 (input, init?) => { ... } 自动捕获 client ❌ 没有闭包,必须手动 malloc + 传上下文
返回的函数携带上下文 函数本身就是闭包,直接调用即可 ❌ 函数指针是纯地址,不绑定任何数据
编译期签名校验 : typeof fetch 强制检查参数/返回值 ⚠️ typedef 只检查函数指针赋值,不检查实现内部
异步 Promise Promise<Response> 是一等公民 ❌ 没有原生异步,需回调或手写事件循环
结构化类型兼容 只要形状对就能赋值 ❌ 必须精确匹配 typedef,无鸭子类型

🔑 最致命的障碍:C 没有闭包

TS 的 createWorkerFetch 之所以优雅,是因为箭头函数自动把 client 关进闭包,返回的函数可以直接当 fetch 用:

javascript 复制代码
// TS:返回的函数自带上下文,调用方无感知
const myFetch = createWorkerFetch(client)
await myFetch("/api/data")  // ✅ 不需要额外传 client

而 C 的函数指针是裸地址,不携带任何状态。要实现同样效果,必须二选一:

c 复制代码
// 方案A:全局变量(丑、不可重入、线程不安全)
static RpcClient* g_client;
Response* workerFetch(const char* url, const RequestInit* init) {
    rpc_call(g_client, url, init); // 依赖全局状态
}

// 方案B:改签名,加 context 参数(破坏了 fetch 接口兼容性!)
typedef Response* (*FetchFn)(void* ctx, const char* url, const RequestInit* init);
//                        ^^^^^^^^ 多了一个参数,不再是标准 fetch 签名

💡 核心矛盾

createWorkerFetch设计精髓是返回的东西和原生 fetch 签名一模一样 ,C 因为没有闭包,要么破坏签名(加 ctx 参数),要么引入全局状态 。两条路都违背了"透明替换"的设计目标。

📌 一句话总结

C 能实现"工厂返回函数指针"这个骨架,但因为没有闭包和结构化类型系统,它无法实现 createWorkerFetch 的灵魂------返回一个"自带上下文、且与原生 fetch 签名完全兼容、编译期可验证"的函数。这不是语法糖的差异,是语言抽象能力的代际差距。


OK,本篇先到这里,如有疑问,欢迎评论区留言讨论,祝各位功力大涨,技术更上一层楼!!!更多内容见下篇 blog

【Agent】【OpenCode】TuiThreadCmd(代理 Fetch 实现)

相关推荐
Wang's Blog1 小时前
AI Agent白手起家29: Few Shot 提示词工程实战
人工智能·算法
杰佛史彦明 本王是暴君1 小时前
PyTorch KernelAgent 源码解读 ---(2)--- 总体流程
人工智能·pytorch·python
澜舟孟子开源社区1 小时前
从流程自动化到认知智能化:LangClaw 携手澜舟智库打造业务决策型数字专家
人工智能
云端漫步19871 小时前
HarmonyOS NEXT AI 智能生活助手:AI 代码解释
人工智能·华为·生活·harmonyos
console.log('npc')2 小时前
OptMem 使用教程
人工智能·ai编程·记忆
世优科技虚拟人2 小时前
数字人厂商赋能学校教育:校史馆党建科普导览AI数字人应用观察
人工智能·智慧校园·ai数字人·数字人一体机·大屏数字人
sali-tec2 小时前
C# 基于OpenCv的视觉工作流-章100-抠图
图像处理·人工智能·opencv·计算机视觉
精益数智工坊2 小时前
账龄分析怎么做才不流于形式?如何真正落地账龄分析?
大数据·人工智能·数据可视化
June`2 小时前
warp shuffle指令
c++·人工智能·算法·cuda