JavaScript 工厂模式详解:定义、实现与重构

作者:木棉

时间:2026-10-24

浏览:306

本文深入解析 JavaScript 中的工厂模式,这是一种遵循 DRY 原则的创建型设计模式。通过日志工具 Logger 的实际案例,演示如何从基础的 if-else 实现重构为基于对象映射的工厂函数。文章详细阐述了动态创建对象、封装构造细节以及提升代码可维护性的优势,并展示了如何利用开闭原则简化新增类型的过程。

文章目录

本文通过具体实例,详细解析 JavaScript 中工厂模式的定义及其实现方式,旨在为读者提供清晰的参考与学习素材。

前言

在前一篇文章中,我们探讨了单例模式,尚未阅读该内容的读者可参考以下链接进行回顾:

浅析 JS 设计模式之:单例模式

本文将重点介绍一种在开发中极为常见的设计模式:工厂模式。

工厂模式属于创建型设计模式,其核心在于遵循 DRY(Don't Repeat Yourself)原则。该模式允许代码依据特定的输入或既定规则,自动判断并创建相应类型的对象。简而言之,它的主要功能是动态返回用户所需的实例对象。

回顾上次的例子

为了保持连贯性,我们继续沿用单例模式中的案例,即一个日志工具 Logger:


class Logger {
 log (...args) {
  console.log(...args);
 }
}

上述代码展示了核心 API。每次使用时,都需要通过 new Logger() 来实例化一个 logger 对象,随后即可像使用 console 那样调用其方法。

多种 Logger

假设当前项目需要支持 Electron 环境,这意味着日志输出既可以是控制台日志,也可以是文件日志。因此,我们需要定义两种不同类型的 Logger:

ConsoleLogger


// logger/console.js
class ConsoleLogger {
 log (...args) {
  console.log(...args)
 }
}
export default ConsoleLogger

FileLogger


// logger/file.js
class FileLogger {
 log (...args) {
  dumpLog(...args)
 }
}
export default FileLogger

此处暂不深究 dumpLog 的具体实现逻辑,只需明确其功能是将日志数据写入文件即可。

使用工厂

目前我们已拥有两种类型的 Logger,虽然可以直接在项目中导入各自类型的模块并使用,但这会导致每次使用都需单独导入对应模块,操作如下所示:

使用 console logger


import ConsoleLogger from './logger/console'
const logger = new ConsoleLogger()

使用 file logger


import FileLogger from './logger/file'
const logger = new FileLogger()

这种调用方式显得较为繁琐。若未来还需支持远程日志等其他类型的 Logger,使用方式将更加繁杂。为了统一 Logger 模块的使用接口,此时引入工厂模式便显得尤为必要。

接下来,我们新建一个 index.js 文件:


// logger/index.js
import ConsoleLogger from './console.js'
import FileLogger from './file.js'

function createLogger(type = 'console') {
 if (type === 'console') {
  return new ConsoleLogger()
 } else if (type === 'file') {
  return new FileLogger()
 }
 throw new Error(`Logger type not found: ${type}`)
}

export default createLogger

经过上述封装后,我们的使用方式将简化为如下形式:


import createLogger from './logger'
// console logger
const logger1 = createLogger('console')
// file logger
const logger2 = createLogger('file')

重构一下

前文示例中的 if-else 结构并不够优雅,且随着 Logger 类型增加,维护成本将显著上升。我们可以利用对象映射来替代条件判断,从而消除 if-else 结构,同时引入一个 logger 选项配置。


// logger/index.js
import ConsoleLogger from './console.js'
import FileLogger from './file.js'

const loggerMap = {
 console: ConsoleLogger,
 file: FileLogger
}
// 可选参数一般放在最后面
function createLogger(options, type = 'console') {
 const Logger = loggerMap[type]
 if (Logger) {
  return new Logger(options)
 }
 throw new Error(`Logger type not found: ${type}`)
}
上述封装方式符合 SOLID 原则中的 开闭原则,即对扩展开放,对修改关闭。每当需要新增一种 logger 类型时,只需新增一个文件,并将对应的构造器注册到 loggerMap 中即可。外部调用方式保持不变,从而以最小的修改代价实现了功能扩展,这体现了良好的设计效果。

总结

以下是对工厂模式主要优点的回顾:

  • 动态创建对象:适用于需要在运行时确定对象类型的场景。
  • 抽象:封装了对象创建的具体细节,用户无需接触对象的构造器,只需指定所需对象类型。
  • 可用性与可维护性:通过工厂统一管理相似对象,提供统一的创建接口,符合开闭原则。这使得我们能够轻松添加多种类型的对象,而无需修改大量现有代码。

至此,工厂模式的介绍暂告一段落。下次我们将探讨装饰器模式。

参考内容

  • JavaScript Object Oriented Patterns: Factory Pattern
  • 《JavaScript 设计模式》
  • 《JavaScript 面向对象编程指南》

感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.jb51.net/code/HtmlJsRun测试上述代码运行效果。

更多关于JavaScript相关内容感兴趣的读者可查看本站专题:《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript数学运算用法总结》

希望本文所述对大家JavaScript程序设计有所帮助。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章