一、工厂模式基础认知
1.1 工厂模式是什么?
工厂模式是一种封装对象创建逻辑的设计模式,它的核心思想是:将 “对象的创建” 与 “对象的使用” 分离。
比如,我们以画图为例,假如我们平时一般通过new来创建图形对象,那么,在有了工厂模式之后,我们就可以通过一个 “工厂” 类或函数来统一负责图形创建,而使用时只需告知工厂我们 “需要什么类型的图形”,从而无需关心创建细节。
1.2 为什么需要工厂模式?
为了容易理解,我们先来看一段不使用工厂模式创建图形的代码。
无工厂模式的实现代码
// 1. 定义图形类
class Circle {
constructor(color) {
this.type = "圆形";
this.color = color;
console.log("创建一个"+ this.color + this.type);
}
// 绘制方法
draw() {
console.log("画一个" + this.color + this.type);
}
}
class Rectangle {
constructor(color) {
this.type = "矩形";
this.color = color;
console.log("创建" + this.color + this.type);
}
draw() {
console.log("画一个" + this.color + this.type);
}
}
// 2. 使用图形:直接在绘图时 new一个对象
function drawShapes() {
// 问题1:创建逻辑与绘图逻辑耦合,必须知道所有图形类名
const redCircle = new Circle("红色");
const blueRectangle = new Rectangle("蓝色");
// 问题2:若要更换图形类型(如换成绿色圆形、黄色矩形),需修改所有new的地方,比如:
// const greenCircle = new Circle("绿色");
// const yellowRectangle = new Rectangle("黄色");
// 问题3:若新增图形(如三角形),需在所有绘图处添加new Triangle()
redCircle.draw();
blueRectangle.draw();
}
drawShapes();
总结:无工厂模式的 4 大痛点
- 耦合度高:绘图时必须知道具体图形的类名(如
Circle),若类名修改(如改Round),所有绘图处都要改。 - 重复代码多:若创建图形需要复杂初始化(比如添加圆心坐标、矩形宽高等),每个绘图处都要重复写一遍参数。
- 扩展困难:如果新增图形(如三角形、梯形)时,需要在所有绘图逻辑中添加新的
new代码。 - 维护成本高:图形创建逻辑分散在各处,后期要统一修改规则(如给所有图形加一个坐标属性),还需逐个修改绘图处。
二、入门:简单工厂模式(Simple Factory)
简单工厂是工厂模式中的 “基础版本”—— 通过一个统一的工厂类或函数,然后根据传入的参数来判断 “要创建什么图形”,并返回对应的图形实例。
2.1 简单工厂模式的实现
我们先用简单工厂重构代码,将所有图形创建逻辑集中到ShapeFactory:
// 1. 定义图形类(这里与无工厂模式一致,无需修改)
class Circle {
constructor(color) {
this.type = "圆形";
this.color = color;
console.log("创建一个"+ this.color + this.type);
}
// 绘制方法
draw() {
console.log("画一个" + this.color + this.type);
}
}
class Rectangle {
constructor(color) {
this.type = "矩形";
this.color = color;
console.log("创建" + this.color + this.type);
}
draw() {
console.log("画一个" + this.color + this.type);
}
}
// 2. 简单工厂:实现集中管理图形创建逻辑
class ShapeFactory {
// 通过一个静态方法:根据参数创建对应图形
static createShape(type, color) {
// 统一处理创建逻辑(如参数校验)
if (!type || !color) throw new Error("请传入图形类型和颜色");
// 根据参数返回不同图形实例
switch (type) {
case "圆形":
return new Circle(color);
case "矩形":
return new Rectangle(color);
default:
throw new Error("不支持该图形类型");
}
}
}
// 3. 使用图形:只需调用工厂,而无需关心创建细节
function drawShapes() {
// 优势1:不用记具体类名,只需传参数(图形类型+颜色)
const redCircle = ShapeFactory.createShape("圆形", "红色");
const blueRectangle = ShapeFactory.createShape("矩形", "蓝色");
// 优势2:更换图形或颜色只需改参数,不用改new逻辑,比如:
// const greenCircle = ShapeFactory.createShape("圆形", "绿色");
// const yellowRectangle = ShapeFactory.createShape("矩形", "黄色");
redCircle.draw();
blueRectangle.draw();
}
drawShapes();
2.2 总结:简单工厂的核心特性及优缺点
-
单一入口:所有图形通过工厂的
createShape方法统一创建,绘图时只要调用工厂的createShape,不用接触具体的图形类。 -
参数驱动:通过
type(图形类型)和color(颜色)参数来控制创建的图形,可以更灵活地切换图形类型。 -
集中维护:若要给所有图形加初始化逻辑(如
this.borderWidth = 2),只需在工厂中统一处理,不用修改每个图形类。优点 缺点 降低耦合:绘图逻辑与图形类解耦 违反开闭原则:新增图形需修改工厂的 switch 逻辑 减少重复:创建逻辑集中,避免冗余参数代码 工厂职责过重:所有图形创建逻辑都在一个工厂 易于维护:修改创建规则只需改工厂 不适合图形类型极多的场景(如 10 + 种图形)
三、进阶:工厂方法模式(Factory Method)
如果要说简单工厂还有什么问题,那就是违反开闭原则,即:新增图形时必须修改工厂代码。
而工厂方法模式,我们通过 “抽象工厂 + 具体工厂” 的拆分,可以让新增图形时无需修改旧代码,完美符合开闭原则。
3.1 工厂方法模式的核心思想
将简单工厂的 “单一工厂” 进一步拆分为两层:
-
抽象工厂(Abstract Factory) :用来定义一个 “创建图形” 的接口(如
createShape),不关心具体实现。 -
具体工厂(Concrete Factory) :每种图形对应一个具体工厂,具体工厂实现抽象工厂的接口,负责创建对应类型的图形。
简单来说,工厂方法就像 “图形专属工厂”:圆形有圆形工厂(负责造各种颜色的圆形),矩形有矩形工坊(负责造各种颜色的矩形),新增三角形时,只需新建三角形工厂,不用向简单工厂模式一样修改圆形或矩形工厂的代码。
3.2 工厂方法模式的实现
我们用工厂方法重构代码,实现 “新增图形无需改旧代码”。
// 1. 定义抽象图形(统一图形方法,确保所有图形有相同接口)
class AbstractShape {
constructor(color) {
this.color = color;
}
// 抽象方法:子类必须实现draw
draw() {
throw new Error("图形子类必须实现draw方法");
}
}
// 2. 定义具体图形(具体图形继承自上面的抽象图形,实现draw方法)
// 圆形
class Circle extends AbstractShape {
constructor(color) {
super(color);
this.type = "圆形";
}
draw() {
console.log("画一个" + this.color + this.type);
}
}
//矩形
class Rectangle extends AbstractShape {
constructor(color) {
super(color);
this.type = "矩形";
}
draw() {
console.log("画一个" + this.color + this.type);
}
}
// 3. 定义抽象工厂(定义创建图形的接口)
class AbstractShapeFactory {
// 抽象方法:子类必须实现createShape
createShape(color) {
throw new Error("工厂子类必须实现createShape方法");
}
}
// 4. 定义具体工厂(具体工厂继承抽象工厂,每种图形对应一个工厂)
class CircleFactory extends AbstractShapeFactory {
// 圆形工厂:只创建圆形
createShape(color) {
return new Circle(color);
}
}
class RectangleFactory extends AbstractShapeFactory {
// 矩形工厂:只创建矩形
createShape(color) {
return new Rectangle(color);
}
}
// 5. 使用图形:通过具体工厂获取图形
function drawShapes(shapeFactory, color) {
// 优势:绘图逻辑与图形类型完全解耦,传入不同工厂就是不同图形
const shape = shapeFactory.createShape(color);
shape.draw();
}
// 绘制红色圆形:传入圆形工厂+红色
drawShapes(new CircleFactory(), "红色");
// 绘制蓝色矩形:传入矩形工厂+蓝色(无需改drawShapes逻辑)
drawShapes(new RectangleFactory(), "蓝色");
// 新增三角形:只需新建三角形图形和三角形工厂,不用改旧的代码
class Triangle extends AbstractShape {
constructor(color) {
super(color);
this.type = "三角形";
}
draw() {
console.log(`在画布上绘制${this.color}${this.type}`);
}
}
class TriangleFactory extends AbstractShapeFactory {
createShape(color) {
return new Triangle(color);
}
}
// 绘制绿色三角形:直接传入三角形工厂
drawShapes(new TriangleFactory(), "绿色");
3.3 总结:工厂方法的核心特性与优缺点
-
开闭原则友好:新增图形(如三角形)时,只需新建具体图形和具体工厂,无需修改抽象工厂或旧的具体工厂。
-
职责单一:每个具体工厂只负责创建对应类型的图形(如圆形工厂只造圆形),避免 “一个工厂管所有” 的问题。
-
接口统一:抽象图形和抽象工厂定义了统一接口,确保所有图形的
draw方法、所有工厂的createShape方法一致,降低代码复杂度。优点 缺点 符合开闭原则:新增图形无需改旧代码 类数量增多:每种图形对应 2 个类(图形 + 工厂) 职责单一:工厂只负责对应图形的创建 实现复杂:相比简单工厂,需要定义更多抽象和具体类 扩展性强:支持动态切换工厂和图形 不适合简单场景:小绘图项目用工厂方法会增加冗余
四、高级:抽象工厂模式(Abstract Factory)
工厂方法模式适合 “单一类型图形” 的场景,比如只创建圆形或只创建矩形,但如果需要更多的图形风格组合,比如实线风格的圆形 + 矩形、虚线风格的圆形 + 矩形,此时,则需要抽象工厂模式:
抽象工厂模式能创建 “一组相关联的图形”,确保图形风格的一致性。
4.1 抽象工厂模式的核心思想
抽象工厂模式的核心是 “创建图形风格族”:
-
图形风格族:同一风格下的一组相关图形(如实线风格族:实线圆形、实线矩形;虚线风格族:虚线圆形、虚线矩形);
-
抽象工厂:定义创建 “风格族中所有图形” 的接口(如
createCircle、createRectangle); -
具体工厂:每种风格对应一个具体工厂,实现抽象工厂的所有接口,创建该风格的所有图形。
简单来说,抽象工厂就像 “风格专属工厂”,实线工厂不仅造实线圆形,还造实线矩形,所有图形都是实线风格;虚线工厂造的都是虚线风格,确保 “一幅图中所有图形风格统一”。
4.2 抽象工厂模式的实现
我们现在添加新的属性,支持 实线和虚线 两种风格的图形族创建:
// 1. 定义图形风格族的抽象图形(圆形、矩形)
class AbstractCircle {
constructor(color, style) {
this.type = "圆形";
this.color = color;
this.style = style; // 新增“风格”属性(实线/虚线)
}
draw() { }
}
class AbstractRectangle {
constructor(color, style) {
this.type = "矩形";
this.color = color;
this.style = style;
}
draw() { }
}
// 2. 定义实线风格图形族(所有图形都是实线)
class SolidCircle extends AbstractCircle {
constructor(color) {
super(color, "实线");
}
draw() {
console.log("画一个" + this.color + this.style + this.type);
}
}
class SolidRectangle extends AbstractRectangle {
constructor(color) {
super(color, "实线");
}
draw() {
console.log("画一个" + this.color + this.style + this.type);
}
}
// 3. 定义虚线风格图形族(所有图形都是虚线)
class DashedCircle extends AbstractCircle {
constructor(color) {
super(color, "虚线");
}
draw() {
console.log("画一个" + this.color + this.style + this.type);
}
}
class DashedRectangle extends AbstractRectangle {
constructor(color) {
super(color, "虚线");
}
draw() {
console.log("画一个" + this.color + this.style + this.type);
}
}
// 4. 定义抽象工厂(创建风格族的所有图形)
class AbstractStyleFactory {
createCircle(color) { throw new Error("需实现createCircle"); }
createRectangle(color) { throw new Error("需实现createRectangle"); }
}
// 5. 定义具体工厂(每种风格对应一个工厂)
class SolidStyleFactory extends AbstractStyleFactory {
// 实线工厂:只造实线图形
createCircle(color) {
return new SolidCircle(color);
}
createRectangle(color) {
return new SolidRectangle(color);
}
}
class DashedStyleFactory extends AbstractStyleFactory {
// 虚线工厂:只造虚线图形
createCircle(color) {
return new DashedCircle(color);
}
createRectangle(color) {
return new DashedRectangle(color);
}
}
// 6. 使用图形风格族:获取同一风格的所有图形
function drawStyleFamily(styleFactory) {
// 同一风格工厂创建的图形,风格自动统一
const redCircle = styleFactory.createCircle("红色");
const blueRectangle = styleFactory.createRectangle("蓝色");
redCircle.draw();
blueRectangle.draw();
}
// 绘制实线风格图形族
console.log("实线风格");
drawStyleFamily(new SolidStyleFactory());
// 绘制虚线风格图形族
console.log("虚线风格");
drawStyleFamily(new DashedStyleFactory());
// 新增点线风格族:只需新建点线图形和点线工厂
class DottedCircle extends AbstractCircle { /* 实现点线圆形draw */ }
class DottedRectangle extends AbstractRectangle { /* 实现点线矩形draw */ }
class DottedStyleFactory extends AbstractStyleFactory {
createCircle(color) { return new DottedCircle(color); }
createRectangle(color) { return new DottedRectangle(color); }
}
console.log("点线风格");
drawStyleFamily(new DottedStyleFactory());
4.3 抽象工厂的核心特性及优缺点
优点:
- 风格一致性:同一具体工厂创建的图形都属于同一风格族(如实线工厂只造实线图形),避免 “实线圆形 + 虚线矩形” 的混搭,适合需要统一风格的绘图场景(如报表图表、UI 设计图)。
- 封闭风格族扩展:新增风格(如点线)只需新建具体工厂和对应图形,无需修改旧代码。
缺点:
- 限制图形类型扩展:若要新增图形类型(如在圆形、矩形外加 “三角形”),还是需要修改抽象工厂接口,违反了之前提到的开闭原则。
五、无工厂模式 & 工厂模式之间 的对比总结
| 对比属性 | 无工厂模式 | 工厂模式 |
|---|---|---|
| 耦合度 | 高:绘图逻辑依赖具体图形类名 | 低:绘图逻辑依赖工厂,不依赖图形类 |
| 代码重复 | 多:创建逻辑分散,重复写参数和初始化 | 少:创建逻辑集中,统一维护参数和风格 |
| 扩展性 | 差:新增图形需修改所有绘图处 | 好:新增图形 / 风格只需扩展工厂,不修改旧代码 |
| 维护成本 | 高:修改创建规则(如加风格)需逐个改绘图处 | 低:修改规则只需改工厂,一处生效 |
| 一致性 | 差:易出现风格混搭(如实线圆 + 虚线矩形) | 好:抽象工厂确保风格族一致性 |
| 工厂模式类型 | 核心特点 | 适用场景 | 缺点 |
|---|---|---|---|
| 简单工厂 | 单一工厂,参数驱动创建图形 | 图形少、变化少(如简单 Canvas 绘图) | 违反开闭原则,工厂职责重 |
| 工厂方法 | 抽象工厂 + 具体工厂,单图形创建 | 图形多、频繁新增(如图表库) | 类数量多,实现复杂 |
| 抽象工厂 | 抽象工厂 + 具体工厂,风格族创建 | 需统一风格(如 UI 组件库图形) | 扩展图形类型难,违反开闭原则 |