目录
[1. 定义](#1. 定义)
[2. 特点](#2. 特点)
[1. 创建复杂对象](#1. 创建复杂对象)
[2. 根据不同条件创建对象](#2. 根据不同条件创建对象)
[1. 代码复用](#1. 代码复用)
[2. 解耦对象创建和使用](#2. 解耦对象创建和使用)
[3. 易于维护](#3. 易于维护)
[1. 增加代码复杂度](#1. 增加代码复杂度)
[2. 工厂函数可能变得臃肿](#2. 工厂函数可能变得臃肿)
[1. 命名规范](#1. 命名规范)
[2. 单一职责原则](#2. 单一职责原则)
[3. 错误处理](#3. 错误处理)
一、定义和特点
1. 定义
工厂模式是一种创建对象的设计模式。它提供了一种创建对象的方式,将对象的创建和使用分离。就像是一个工厂,负责生产各种产品(对象),使用者只需要从工厂获取产品,而不需要关心产品是如何生产出来的。
2. 特点
封装创建过程:把对象创建的复杂逻辑封装在工厂函数内部。例如,在创建一个复杂的 UI 组件时,可能需要进行 DOM 元素的创建、属性设置、事件绑定等多个步骤,工厂模式可以将这些步骤封装起来,使代码更加整洁。
提高可维护性和可复用性:当需要创建多个相似的对象时,通过工厂模式可以复用创建对象的代码。如果创建对象的逻辑发生变化,只需要修改工厂函数内部的代码,而不需要在每个使用该对象的地方进行修改。
二、实现方式
以下是一个简单的 JavaScript 工厂模式示例,用于创建不同类型的汽车对象:
javascript
// 汽车工厂函数
function CarFactory() {
this.createCar = function (type) {
let car;
if (type === "sedan") {
car = new Sedan();
} else if (type === "suv") {
car = new SUV();
}
return car;
}
}
// 轿车构造函数
function Sedan() {
this.type = "sedan";
this.drive = function () {
console.log("Driving a sedan.");
}
}
// SUV构造函数
function SUV() {
this.type = "suv";
this.drive = function () {
console.log("Driving an SUV.");
}
}
// 使用工厂函数创建汽车
let carFactory = new CarFactory();
let sedan = carFactory.createCar("sedan");
sedan.drive();
let suv = carFactory.createCar("suv");
suv.drive();
在上述代码中:
CarFactory
是一个工厂函数,它包含一个createCar
方法。这个方法根据传入的汽车类型(sedan
或suv
)创建相应的汽车对象。
Sedan
和SUV
是具体的汽车类型构造函数,它们定义了每种汽车的属性和方法。
三、使用场景
1. 创建复杂对象
当创建 DOM 元素时,例如创建一个带有特定样式、事件绑定的自定义按钮。
javascript
function ButtonFactory() {
this.createButton = function (text, onclick) {
let button = document.createElement("button");
button.textContent = text;
button.addEventListener("click", onclick);
button.style.backgroundColor = "blue";
button.style.color = "white";
return button;
}
}
let buttonFactory = new ButtonFactory();
let myButton = buttonFactory.createButton("Click me", function () {
console.log("Button clicked!");
});
document.body.appendChild(myButton);
在这个例子中,ButtonFactory
用于创建具有特定样式(蓝色背景、白色文字)和点击事件的按钮,将创建按钮的复杂过程封装起来。
2. 根据不同条件创建对象
在一个绘图应用中,根据用户选择的图形类型(如圆形、矩形)来创建相应的图形对象。
javascript
function ShapeFactory() {
this.createShape = function (type, options) {
let shape;
if (type === "circle") {
shape = new Circle(options.radius);
} else if (type === "rectangle") {
shape = new Rectangle(options.width, options.height);
}
return shape;
}
}
function Circle(radius) {
this.draw = function () {
console.log("Drawing a circle with radius " + radius);
}
}
function Rectangle(width, height) {
this.draw = function () {
console.log("Drawing a rectangle with width " + width + " and height " + height);
}
}
let shapeFactory = new ShapeFactory();
let circle = shapeFactory.createShape("circle", {radius: 5});
circle.draw();
let rectangle = shapeFactory.createShape("rectangle", {width: 10, height: 5});
rectangle.draw();
四、优点
1. 代码复用
可以在多个地方复用工厂函数来创建相同类型的对象。例如,在一个电商网站中,有多个页面需要创建产品展示卡片,通过产品卡片工厂函数,可以在每个页面复用创建卡片的代码,减少代码冗余。
2. 解耦对象创建和使用
使用者不需要知道对象是如何创建的,只需要关心如何使用对象。这样可以降低代码之间的耦合度。比如在一个游戏开发中,游戏场景中需要使用各种道具对象,通过道具工厂来创建道具,游戏场景代码只需要获取道具并使用,而不需要了解道具的创建细节,如道具的资源加载、初始化参数设置等。
3. 易于维护
如果对象的创建过程发生变化,比如需要添加新的属性或者修改初始化逻辑,只需要修改工厂函数内部的代码。以一个用户信息表单对象的创建为例,如果需要添加一个新的输入字段,只需要在表单工厂函数中修改创建表单的代码,而不需要在每个使用表单的地方进行修改。
五、缺点
1. 增加代码复杂度
对于简单的对象创建场景,使用工厂模式可能会使代码变得过于复杂。例如,只需要创建一个简单的配置对象,使用工厂模式反而会增加不必要的代码结构。
2. 工厂函数可能变得臃肿
当需要创建多种类型的复杂对象,且每种对象的创建逻辑差异较大时,工厂函数内部可能会包含大量的条件判断和复杂的创建逻辑,导致工厂函数变得臃肿、难以维护。例如,在一个大型的图形编辑软件中,要创建多种类型的图形(如线条、多边形、贝塞尔曲线等),工厂函数可能会因为过多的图形类型判断和不同的创建步骤而变得非常复杂。
六、注意事项
1. 命名规范
工厂函数的命名应该清晰地表达其功能。例如,UserFactory
、ComponentFactory
等,让其他开发人员能够快速理解工厂函数的用途。
2. 单一职责原则
尽量让工厂函数只负责对象的创建,不要在工厂函数中添加过多与对象使用相关的逻辑。例如,在创建一个 UI 组件工厂函数时,不要在工厂函数中添加组件的业务逻辑,如数据获取和处理,应该将这些逻辑放在使用组件的其他代码部分。
3. 错误处理
在工厂函数内部,需要考虑对象创建过程中可能出现的错误。例如,在创建网络请求对象时,如果网络连接失败,应该有适当的错误处理机制,如返回一个错误对象或者抛出异常,而不是让错误导致整个应用崩溃。