跳到主要内容

深入认识工厂模式

5 分钟阅读

前言

在平时开发过程中,如果遇到需要创建多个配置相似的对象,我们普遍想到的都是使用构造函数来解决,这种方式简单直观,有着清晰的继承体系,特别适合面向对象编程,但是不可避免的必须使用 new 关键字,否则 this 指向全局对象(如 window),导致隐蔽的 Bug。而 newthis 的关键字依赖又导致原型链的复杂性增加,难以实现私有变量。

这在我们需要灵活创建多个配置相似对象同时实现真正的封装和私有状态还要避免类继承的局限性时,使用构造函数已经很难满足我们的需求,试想一下当你在封装组件时遇到响应式丢失、类继承导致的重构困难时,就不可避免的走向对

工厂模式

也因此​工厂模式(Factory Pattern)​这种创建型的设计模式出现了,它的核心思想是 ​将对象的创建逻辑封装在一个函数或类中,而不是直接通过 new 或字面量手动创建。通过这种方式,可以实现 ​对象创建的集中管理、解耦代码,并支持 ​动态生成不同类型的对象,这些特性完美契合函数式编程的理念,即无副作用纯函数 和 ​不可变性

实现

而为了实现工厂模式,我们​需要封装创建过程,也就是通过一个函数,根据输入参数返回不同对象,可以发现工厂模式它就像流水线一样批量生产对象,不同于传统的类或构造函数,它通过普通函数返回新对象的方式实现对象创建。

也因此工厂模式本质是通过一个普通函数,在内部构造对象并返回,无需 new 关键字调用。

既然提到了函数,那么在工厂模式中函数又分为两种,即工厂函数函数工厂,虽然它们的名字非常相似,但是​侧重点不同,可以理解为 ​同一设计模式的不同应用场景

工厂函数(Factory Function)

工厂函数是一种用于创建并返回对象的函数,它的核心思想是 ​封装对象的创建过程,提供一种更灵活、可复用的对象生成方式,与传统的构造函数相比,工厂函数不依赖 new 关键字,而是直接通过函数调用来生成对象。

在 JavaScript 中,​工厂函数并不是一种官方的语言特性,而是一种被广泛使用的 ​设计模式​。

工厂函数:返回一个对象

function createUser(name) {
return {
name,
greet() {
console.log(`Hi, I'm ${name}!`);
},
};
}

const user = createUser("Alice");
user.greet(); // "Hi, I'm Alice!"

函数工厂(Function Factory)

函数工厂是一种通过函数生成并返回其他函数的设计模式,它通过闭包和参数化机制,动态创建具有特定行为的函数。在 JavaScript 中,函数工厂常用于 逻辑复用动态配置 和 上下文隔离

函数工厂:返回一个函数

function createMultiplier(factor) {
return function (x) {
return x * factor;
};
}

const double = createMultiplier(2);
console.log(double(5)); // 10

对比

术语目标返回值类型用途场景
工厂函数创建对象对象(Object)生成结构相似的对象
函数工厂创建函数函数(Function)动态生成不同行为的函数

两者都是 ​工厂模式 的具体实现,而使用哪种则取决于具体的场景,也因此接下来我会将这两种函数都统称为工厂函数。

与传统方式的对比

1. 构造函数方式

function User(name) {
this.name = name
}

User.prototype.login = function() {
console.log(`${this.name} logged in`)
}

const user = new User('Charlie')

2. 类语法

class User {
constructor(name) {
this.name = name
}

login() {
console.log(`${this.name} logged in`)
}
}

const user = new User('David')

3. 工厂函数方法

function createUser(name) {
return {
name, // 等价于 name: name
login() { // 直接定义方法
console.log(`${this.name} logged in`)
}
}
}

// 使用方式
const user = createUser('Charlie') // 不需要new关键字

工厂函数更适合 ​快速创建灵活对象、实现私有状态、避免 new 依赖 的场景。三者并非对立,而是互补的工具,应根据具体场景需求选择。

创建工厂函数的四个步骤

1. 基础对象创建

function createCircle(radius) {
return {
radius,
draw() {
console.log('Drawing circle')
}
}
}

2. 添加私有成员

function createBankAccount(balance) {
// 真正私有的变量
let _balance = balance

return {
deposit(amount) {
_balance += amount
},
getBalance() {
return _balance
}
}
}

3. 实现方法共享

const userMethods = {
login() {
console.log(`${this.name} logged in`)
},
logout() {
console.log(`${this.name} logged out`)
}
}

function createUser(name) {
return {
name,
...userMethods
}
}

4. 实现组合继承

function createAdminUser(name) {
const user = createUser(name)

return {
...user,
deleteUser(id) {
console.log(`Deleting user ${id}`)
}
}
}

高级应用技巧

动态创建组件

const defineFormComponent = <T extends object>(factory: (ctx: T) => JSX.Element) => {
return defineComponent({
inheritAttrs: false,
setup(props, { attrs }) {
const context = reactive({ ...attrs }) as T
onBeforeUnmount(() => {
if ('inputRef' in context) {
(context as any).inputRef = null
}
})
return () => factory(context)
}
})
}

// 表单配置
const formState = reactive({
username: '',
id: ''
})

const editModalFormItems = Object.freeze([
{
label: "username",
name: "username",
component: defineFormComponent((ctx) =>
<a-input
v-model:value={ctx.formState.username}
ref={(el) => ctx.inputRef = el}
/>
),
extraProps: {
formState,
inputRef: null as HTMLInputElement | null
}
},
{
label: "id",
name: "id",
component: defineFormComponent((ctx) =>
<a-input
v-model:value={ctx.formState.id}
ref={(el) => ctx.inputRef = el}
/>
),
extraProps: {
formState,
inputRef: null as HTMLInputElement | null
}
}
] as const)

总结

函数工厂 = 普通函数 + 对象字面量 + 闭包,它以函数式的方式实现对象创建,是现代 JavaScript 中践行组合优于继承理念的典型实践。是构建可维护的代码结构的不二之选。

评论
0条评论

添加新评论

昵称
邮箱
网址