大家可能在设计模式的专题中经常会看到工厂模式。然而很多的文章对工厂模式的讲解五花八门。让大家对该设计模式感觉很高级,然而实际上让自己去解释工厂模式又不知道从何说起。
那么今天,我们就来好好聊聊,什么是工厂模式。
一、工厂模式不是一种模式!
首先,为啥不同文章对工厂模式的解释会不同。其根本原因是工厂模式是一类模式的泛称。在此基础之上,更加细致的划分为:简单工厂、工厂方法、抽象工厂等。而很多文章仅仅讨论其中一种,但是却使用工厂模式这种泛称,所以给大家一种众说纷纭的感觉。
当我们创建一个新对象时,通常会使用构造函数来完成对象的创建和初始化。例如,想要一台电脑,我们需要分别购买主板、CPU、显卡等配件,并使用螺丝刀将它们组装成一台完整的电脑。然而,对于不熟悉组装的人来说,这个过程可能既复杂又繁琐。
如果有一家整机工厂,能够根据用户的需求,自动配齐配件、完成组装和测试,用户只需选择想要的配置,而不必关心具体的购买、组装和调试过程,那将会方便许多。
这正是工厂模式所要解决的问题——它将一系列构造对象的过程(配件采购、组装、测试)封装到一个工厂中,用户只需提供需求,工厂便能生产出符合要求的产品。
二、如何创建自己的工厂?
那么刚才介绍了,工厂模式其实就是创建一个工厂类。而这类的方法则是专门用于生产各种产品的生产线。
换句话说,我们传入产品名称,则可以通过工厂生产对应“零件”,并通过“流水线”组装成一个完整的产品给到我。
举个 🌰
用户:我要一辆轿车
工厂:
- 造车身
- 造轮胎
- 造发动机
- 组装成一辆完整的车
- 交付给用户
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 1. 定义抽象产品(汽车)
typedef struct Car {
char name[20]; // 车的名字
void (*assemble)(void); // 组装流程
} Car;
// 2. 具体产品(轿车)
void assemble_sedan() {
printf("生产流程:制造车身 -> 安装轮胎 -> 安装发动机 -> 组装完成\n");
printf("轿车生产完毕!\n");
}
Car* create_sedan() {
Car* car = (Car*)malloc(sizeof(Car));
strcpy(car->name, "轿车");
car->assemble = assemble_sedan;
return car;
}
// 3. 具体产品(卡车)
void assemble_truck() {
printf("生产流程:制造加固车身 -> 安装重型轮胎 -> 安装大功率发动机 -> 组装完成\n");
printf("卡车生产完毕!\n");
}
Car* create_truck() {
Car* car = (Car*)malloc(sizeof(Car));
strcpy(car->name, "卡车");
car->assemble = assemble_truck;
return car;
}
// 4. 汽车工厂
typedef enum {
SEDAN,
TRUCK
} CarType;
Car* CarFactory(CarType type) {
switch (type) {
case SEDAN:
return create_sedan();
case TRUCK:
return create_truck();
default:
return NULL;
}
}
// 5. 客户端代码
int main() {
printf("用户:我要一辆轿车!\n");
Car* car1 = CarFactory(SEDAN);
if (car1) {
printf("工厂:生产 %s...\n", car1->name);
car1->assemble();
free(car1);
}
printf("\n用户:我还想要一辆卡车!\n");
Car* car2 = CarFactory(TRUCK);
if (car2) {
printf("工厂:生产 %s...\n", car2->name);
car2->assemble();
free(car2);
}
return 0;
}
例程中,用户只关心我要车,而不关心车的制造过程。
这便是简单工厂模式。
三、简单工厂的弊端
刚才给大家介绍了简单工厂模式,它的好处在于调用者直接可以通过输入拿到产品,而忽略组装产品的过程。但是,如果工厂引进了新的产品,则工厂的代码就面临着重构。
如果我们能够在新增产品的时候,只需要新增代码而不需要修改代码。那么工作量会降低不少。这也是人们常常说的开闭原则。
我们可以利用之前面向对象文章中提到的多态。用户虽然调用工厂类去创建对象,实际上则是通过不同的工厂子类进行对象的创建。这样一来,如果有新的产品,对应创建新的工厂就可以了。相比之前的 switch 判断来构造,利用多态可以保证了原本的代码不需要进行修改。这便是工厂方法
继续举 🌰
我们工厂可以生产轿车和卡车。但是,我们有两个子工厂:
- 产品:定义所有汽车的共同接口(
Car
)。 - 具体产品:
Sedan
(轿车)Truck
(卡车)- 总工厂:定义创建汽车的接口(
CarFactory
)。 - 子工厂:
轿车工厂
生产 轿车- 卡车工厂 生产 卡车
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 1. 抽象产品(汽车)
typedef struct Car {
char name[20]; // 车的名字
void (*assemble)(void); // 组装流程
} Car;
// 2. 具体产品:轿车
void assemble_sedan() {
printf("轿车生产流程:制造车身 -> 安装轮胎 -> 安装发动机 -> 组装完成\n");
printf("轿车生产完毕!\n");
}
Car* create_sedan() {
Car* car = (Car*)malloc(sizeof(Car));
strcpy(car->name, "轿车");
car->assemble = assemble_sedan;
return car;
}
// 3. 具体产品:卡车
void assemble_truck() {
printf("卡车生产流程:制造加固车身 -> 安装重型轮胎 -> 安装大功率发动机 -> 组装完成\n");
printf("卡车生产完毕!\n");
}
Car* create_truck() {
Car* car = (Car*)malloc(sizeof(Car));
strcpy(car->name, "卡车");
car->assemble = assemble_truck;
return car;
}
// 4. 抽象工厂(定义一个创建汽车的接口)
typedef struct CarFactory {
Car* (*createCar)(void); // 工厂方法,返回汽车对象
} CarFactory;
// 5. 具体工厂:轿车工厂
typedef struct SedanFactory {
CarFactory base; // 继承 CarFactory
} SedanFactory;
Car* SedanFactory_createCar() {
return create_sedan();
}
SedanFactory* create_sedan_factory() {
SedanFactory* factory = (SedanFactory*)malloc(sizeof(SedanFactory));
factory->base.createCar = SedanFactory_createCar;
return factory;
}
// 6. 具体工厂:卡车工厂
typedef struct TruckFactory {
CarFactory base; // 继承 CarFactory
} TruckFactory;
Car* TruckFactory_createCar() {
return create_truck();
}
TruckFactory* create_truck_factory() {
TruckFactory* factory = (TruckFactory*)malloc(sizeof(TruckFactory));
factory->base.createCar = TruckFactory_createCar;
return factory;
}
// 7. 客户端代码
int main() {
printf("用户:我要一辆轿车!\n");
SedanFactory* sedanFactory = create_sedan_factory();
Car* car1 = sedanFactory->base.createCar();
if (car1) {
printf("工厂:生产 %s...\n", car1->name);
car1->assemble();
free(car1);
}
free(sedanFactory);
printf("\n用户:我还想要一辆卡车!\n");
TruckFactory* truckFactory = create_truck_factory();
Car* car2 = truckFactory->base.createCar();
if (car2) {
printf("工厂:生产 %s...\n", car2->name);
car2->assemble();
free(car2);
}
free(truckFactory);
return 0;
}
- 定义抽象产品
Car
- 统一定义所有汽车的接口,包含
name
和assemble()
方法。 - 实现具体产品
Sedan
(轿车)和Truck
(卡车) - 轿车和卡车分别有自己的
assemble()
组装流程。 - 定义抽象工厂
CarFactory
- 只有一个
createCar()
方法,具体工厂必须实现它。 - 实现具体工厂
SedanFactory
专门创建Sedan
轿车。TruckFactory
专门创建Truck
卡车。- 具体工厂是
CarFactory
的子类,并实现createCar()
方法。 - 客户端
- 用户通过不同的工厂(
SedanFactory
或TruckFactory
)获取汽车对象,并调用assemble()
进行组装。
工厂方法的好处在于:新增产品时,只需要新增一个具体工厂,而不需要修改已有代码。每个工厂只负责生产一种产品,代码更清晰、可维护。
比如我们要新增一款 SUV 的车型,只需要在新增一个 SUVFactory 类就行。
四、产品的纵向分类
当我们的工厂变得比较大,为了降本增效。工厂里面的某些零件开始准备自己生产。比如:轿车轮胎和卡车轮胎。这时候工厂方法模式已经不够用了,因为它只能创建单个产品。它包括如下内容:
- 产品类:定义产品类的共同接口(如
Car
、Tire
)。 - 具体产品:实现抽象产品的具体类(如
Sedan
、Truck
、SedanTire
、TruckTire
)。 - 工厂父类:定义工厂的接口,可以创建多个相关的产品(如
CarFactory
)。 - 工厂子类:实现工厂接口,负责创建具体产品(如
SedanFactory
和TruckFactory
)。
父类工厂定义了一个 Car 产品需要轮胎等零件的创建,相当于在这里将产品需要的零件重新分配到一起。从另一个维度组成了一个产品所需的产品族
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 1. 产品类(汽车)
typedef struct Car {
char name[20];
void (*assemble)(void);
} Car;
// 2. 具体产品(轿车)
void assemble_sedan() {
printf("轿车生产流程:制造车身 -> 安装轮胎 -> 安装发动机 -> 组装完成\n");
}
Car* create_sedan() {
Car* car = (Car*)malloc(sizeof(Car));
strcpy(car->name, "轿车");
car->assemble = assemble_sedan;
return car;
}
// 3. 具体产品(卡车)
void assemble_truck() {
printf("卡车生产流程:制造加固车身 -> 安装重型轮胎 -> 安装大功率发动机 -> 组装完成\n");
}
Car* create_truck() {
Car* car = (Car*)malloc(sizeof(Car));
strcpy(car->name, "卡车");
car->assemble = assemble_truck;
return car;
}
// 4. 产品类(轮胎)
typedef struct Tire {
char type[20];
void (*install)(void);
} Tire;
// 5. 具体产品(轿车轮胎)
void install_sedan_tire() {
printf("安装轿车轮胎:标准轮胎\n");
}
Tire* create_sedan_tire() {
Tire* tire = (Tire*)malloc(sizeof(Tire));
strcpy(tire->type, "轿车轮胎");
tire->install = install_sedan_tire;
return tire;
}
// 6. 具体产品(卡车轮胎)
void install_truck_tire() {
printf("安装卡车轮胎:重型轮胎\n");
}
Tire* create_truck_tire() {
Tire* tire = (Tire*)malloc(sizeof(Tire));
strcpy(tire->type, "卡车轮胎");
tire->install = install_truck_tire;
return tire;
}
// 7. 工厂父类(汽车工厂)
typedef struct CarFactory {
Car* (*createCar)(void);
Tire* (*createTire)(void);
} CarFactory;
// 8. 工厂子类(轿车工厂)
typedef struct SedanFactory {
CarFactory base;
} SedanFactory;
SedanFactory* create_sedan_factory() {
SedanFactory* factory = (SedanFactory*)malloc(sizeof(SedanFactory));
factory->base.createCar = create_sedan;
factory->base.createTire = create_sedan_tire;
return factory;
}
// 9. 工厂子类(卡车工厂)
typedef struct TruckFactory {
CarFactory base;
} TruckFactory;
TruckFactory* create_truck_factory() {
TruckFactory* factory = (TruckFactory*)malloc(sizeof(TruckFactory));
factory->base.createCar = create_truck;
factory->base.createTire = create_truck_tire;
return factory;
}
// 10. 客户端代码
int main() {
printf("用户:我要一辆轿车和轮胎!\n");
SedanFactory* sedanFactory = create_sedan_factory();
Car* car1 = sedanFactory->base.createCar();
Tire* tire1 = sedanFactory->base.createTire();
if (car1 && tire1) {
printf("工厂:生产 %s...\n", car1->name);
car1->assemble();
tire1->install();
free(car1);
free(tire1);
}
free(sedanFactory);
printf("\n用户:我还想要一辆卡车和轮胎!\n");
TruckFactory* truckFactory = create_truck_factory();
Car* car2 = truckFactory->base.createCar();
Tire* tire2 = truckFactory->base.createTire();
if (car2 && tire2) {
printf("工厂:生产 %s...\n", car2->name);
car2->assemble();
tire2->install();
free(car2);
free(tire2);
}
free(truckFactory);
return 0;
}
这便是抽象工厂模式!
五、总结对比
什么时候用简单工厂?
✅ 产品种类较少,新增产品不频繁。
✅ 需要一个统一的工厂,减少对象创建的复杂性。
❌ 新增产品时需要修改工厂代码,不符合开闭原则。
🔹 什么时候用工厂方法?
✅ 需要经常新增新的产品(如 SUV
车型)。
✅ 希望让每个工厂只负责一个产品,遵循单一职责原则。
❌ 需要为每个新产品创建一个新的工厂类,可能增加代码量。
🔹 什么时候用抽象工厂?
✅ 需要创建多个相关产品(如 汽车 + 轮胎
)。
✅ 需要保证产品族之间的兼容性(不能混用不同厂商的零件)。
❌ 新增产品类型困难,因为所有工厂接口都需要修改
END
原文:裸机思维
专栏推荐文章
- 【嵌入式解耦很难么】“面向对象”还是“面向过程”?接口说“我全要”
- 【嵌入式解耦很难么】霸总:你们都要变成我的形状!
- 超级嵌入式系统“性能/时间”工具箱
- 当 DeepSeek 接管操作系统:智能体(Agent)真能让程序员提前退休?
- 【为宏正名】for的妙用你想不到
如果你喜欢我的思维,欢迎订阅裸机思维欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。