- Published on
设计模式(三)之工厂模式
- Authors

- Name
- 葛颂
工厂模式(Factory)把「创建哪一种对象」集中到一个地方。调用方只拿产品接口,不写 new 的分支。创建规则变化时,改工厂即可,使用处保持不动。
常见有两档:
- 简单工厂:一个函数或类,用参数决定具体产品。
- 工厂方法:每种产品有自己的创建器,创建器继承同一抽象。
示例:按类型创建通知
产品先统一接口:
interface Notice {
render(): string
}
class InfoNotice implements Notice {
constructor(private text: string) {}
render() {
return `[info] ${this.text}`
}
}
class WarnNotice implements Notice {
constructor(private text: string) {}
render() {
return `[warn] ${this.text}`
}
}
简单工厂用一个函数就够:
type NoticeKind = 'info' | 'warn'
function createNotice(kind: NoticeKind, text: string): Notice {
switch (kind) {
case 'info':
return new InfoNotice(text)
case 'warn':
return new WarnNotice(text)
}
}
const notice = createNotice('warn', '磁盘空间不足')
notice.render() // [warn] 磁盘空间不足
当创建过程本身也不同(读配置、校验、组装依赖)时,再用工厂方法把创建器分开:
interface NoticeFactory {
create(text: string): Notice
}
class InfoNoticeFactory implements NoticeFactory {
create(text: string) {
return new InfoNotice(text.trim())
}
}
class WarnNoticeFactory implements NoticeFactory {
create(text: string) {
return new WarnNotice(text.trim().toUpperCase())
}
}
调用方依赖 NoticeFactory,测试时换一个返回假通知的工厂即可。
现实场景
- 跨云存储:业务只调用
Storage.put(key, body)。工厂按配置创建 S3、OSS 或本地磁盘实现,切换厂商不改上传代码。 - 按文件类型解析:上传
.csv、.xlsx、.json时,工厂按扩展名返回对应解析器,接口统一是parse(buffer)。 - UI 组件库:设计系统用
createButton('primary' | 'ghost')决定样式和默认属性,页面不再自己拼 class。 - 测试夹具:测试里换一个工厂,固定返回内存假对象,避免连真实数据库或支付网关。
什么时候用
new前面已经出现按类型分支,而且散落在多处。- 具体类来自第三方或会替换,调用方只应看到接口。
- 创建步骤不止一行:读环境变量、选择实现、注入依赖。
如果全项目只有一处 new InfoNotice(text),直接构造更清楚。
注意
- 简单工厂的
switch仍是集中修改点。类型会频繁增加时,可改成注册表:
const registry: Record<NoticeKind, new (text: string) => Notice> = {
info: InfoNotice,
warn: WarnNotice,
}
function createNotice(kind: NoticeKind, text: string): Notice {
return new registry[kind](text)
}
- 工厂返回的应是接口,而不是把具体类类型泄露给所有调用方。
- 不要为了「每个类配一个工厂」而机械套用。创建没有变化点时,工厂只是多一层跳转。