名称: 工厂函数组合 描述: 应用工厂函数模式来组合客户端和服务,实现适当的关注点分离。当创建依赖于外部客户端的函数、用特定领域方法包装资源或重构将客户端/服务/方法选项混合在一起的代码时使用。 元数据: 作者: epicenter 版本: ‘1.0’
工厂函数组合
这个技能帮助您在TypeScript中应用工厂函数模式,实现干净的依赖注入和函数组合。
何时应用此技能
当您看到以下情况时使用此模式:
- 一个函数以客户端/资源作为第一个参数
- 不同层(客户端、服务、方法)的选项混合在一起
- 客户端创建发生在不应该拥有它的函数内部
- 函数难以测试,因为它们创建自己的依赖项
通用签名
每个工厂函数都遵循此签名:
function createSomething(dependencies, options?) {
return {
/* methods */
};
}
- 第一个参数:总是资源。要么是单个客户端,要么是解构的多个依赖对象。
- 第二个参数:可选的特定于此工厂的配置。永远不是客户端配置——那属于客户端创建时。
最多两个参数。第一个是资源,第二个是配置。没有例外。
核心模式
// 单个依赖
function createService(client, options = {}) {
return {
method(methodOptions) {
// 使用client、options和methodOptions
},
};
}
// 多个依赖
function createService({ db, cache }, options = {}) {
return {
method(methodOptions) {
// 使用db、cache、options和methodOptions
},
};
}
// 使用示例
const client = createClient(clientOptions);
const service = createService(client, serviceOptions);
service.method(methodOptions);
关键原则
- 客户端配置属于客户端创建时 — 不要通过工厂传递clientOptions
- 每个层都有自己的选项 — 客户端、服务和方法的选项保持分离
- 依赖项优先 — 工厂函数以依赖项作为第一个参数
- 返回带有方法的对象 — 不是需要传递资源的独立函数
识别反模式
反模式 1: 函数以客户端作为第一个参数
// 不良做法
function doSomething(client, options) { ... }
doSomething(client, options);
// 良好做法
const service = createService(client);
service.doSomething(options);
反模式 2: 客户端创建隐藏内部
// 不良做法
function doSomething(clientOptions, methodOptions) {
const client = createClient(clientOptions); // 隐藏!
// ...
}
// 良好做法
const client = createClient(clientOptions);
const service = createService(client);
service.doSomething(methodOptions);
反模式 3: 混合选项块
// 不良做法
doSomething({
timeout: 5000, // 客户端选项
retries: 3, // 客户端选项
endpoint: '/users', // 方法选项
payload: data, // 方法选项
});
// 良好做法
const client = createClient({ timeout: 5000, retries: 3 });
const service = createService(client);
service.doSomething({ endpoint: '/users', payload: data });
反模式 4: 多层隐藏
// 不良做法
function doSomething(clientOptions, serviceOptions, methodOptions) {
const client = createClient(clientOptions);
const service = createService(client, serviceOptions);
return service.method(methodOptions);
}
// 良好做法 — 每层可见且可配置
const client = createClient(clientOptions);
const service = createService(client, serviceOptions);
service.method(methodOptions);
多个依赖项
当您的服务需要多个客户端时:
function createService(
{ db, cache, http }, // 依赖项作为解构对象
options = {}, // 服务选项
) {
return {
method(methodOptions) {
// 使用db、cache、http
},
};
}
// 使用示例
const db = createDbConnection(dbOptions);
const cache = createCacheClient(cacheOptions);
const http = createHttpClient(httpOptions);
const service = createService({ db, cache, http }, serviceOptions);
service.method(methodOptions);
心智模型
将其视为一个链条,其中每个链接:
- 从前一个链接接收资源
- 添加自己的配置
- 为下一个链接产生某些东西
createClient(...) → createService(client, ...) → service.method(...)
↑ ↑ ↑
clientOptions serviceOptions methodOptions
优势
- 可测试性:轻松注入模拟客户端
- 可重用性:在多个服务之间共享客户端
- 灵活性:独立配置每个层
- 清晰性:每个级别配置的明确所有权
参考文献
查看完整文章了解更多详情:
- 通用工厂函数签名 — 深入解释签名
- 停止将客户端作为参数传递 — 实践指南
- 工厂函数模式 — 详细解释
- 工厂方法模式 — 分离选项和方法模式