【嵌入式解耦很难么】在“程序世界”做甲方爸爸是一种怎样的感觉?

image.png

大家可能在设计模式的专题中经常会看到工厂模式。然而很多的文章对工厂模式的讲解五花八门。让大家对该设计模式感觉很高级,然而实际上让自己去解释工厂模式又不知道从何说起。

Image

那么今天,我们就来好好聊聊,什么是工厂模式。

一、工厂模式不是一种模式!

首先,为啥不同文章对工厂模式的解释会不同。其根本原因是工厂模式是一类模式的泛称。在此基础之上,更加细致的划分为:简单工厂、工厂方法、抽象工厂等。而很多文章仅仅讨论其中一种,但是却使用工厂模式这种泛称,所以给大家一种众说纷纭的感觉。

当我们创建一个新对象时,通常会使用构造函数来完成对象的创建和初始化。例如,想要一台电脑,我们需要分别购买主板、CPU、显卡等配件,并使用螺丝刀将它们组装成一台完整的电脑。然而,对于不熟悉组装的人来说,这个过程可能既复杂又繁琐。

如果有一家整机工厂,能够根据用户的需求,自动配齐配件、完成组装和测试,用户只需选择想要的配置,而不必关心具体的购买、组装和调试过程,那将会方便许多。

这正是工厂模式所要解决的问题——它将一系列构造对象的过程(配件采购、组装、测试)封装到一个工厂中,用户只需提供需求,工厂便能生产出符合要求的产品。

Image

二、如何创建自己的工厂?

那么刚才介绍了,工厂模式其实就是创建一个工厂类。而这类的方法则是专门用于生产各种产品的生产线。

换句话说,我们传入产品名称,则可以通过工厂生产对应“零件”,并通过“流水线”组装成一个完整的产品给到我。

举个 🌰

用户:我要一辆轿车

工厂:

  • 造车身
  • 轮胎
  • 造发动机
  • 组装成一辆完整的车
  • 交付给用户
#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)。
  • 子工厂
  • 轿车工厂  生产   轿车
  • 卡车工厂   生产   卡车

Image

#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 类就行。

四、产品的纵向分类

当我们的工厂变得比较大,为了降本增效。工厂里面的某些零件开始准备自己生产。比如:轿车轮胎和卡车轮胎。这时候工厂方法模式已经不够用了,因为它只能创建单个产品。它包括如下内容:

  • 产品类:定义产品类的共同接口(如  CarTire)。
  • 具体产品:实现抽象产品的具体类(如  SedanTruckSedanTireTruckTire)。
  • 工厂父类:定义工厂的接口,可以创建多个相关的产品(如  CarFactory)。
  • 工厂子类:实现工厂接口,负责创建具体产品(如  SedanFactory  和  TruckFactory)。

父类工厂定义了一个 Car 产品需要轮胎等零件的创建,相当于在这里将产品需要的零件重新分配到一起。从另一个维度组成了一个产品所需的产品族

Image

#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;
}

这便是抽象工厂模式!

五、总结对比

image.png

什么时候用简单工厂?

✅ 产品种类较少,新增产品不频繁。
✅ 需要一个统一的工厂,减少对象创建的复杂性。
❌ 新增产品时需要修改工厂代码,不符合开闭原则

🔹 什么时候用工厂方法?

✅ 需要经常新增新的产品(如  SUV  车型)。
✅ 希望让每个工厂只负责一个产品,遵循单一职责原则
❌ 需要为每个新产品创建一个新的工厂类,可能增加代码量。

🔹 什么时候用抽象工厂?

✅ 需要创建多个相关产品(如  汽车 + 轮胎)。
✅ 需要保证产品族之间的兼容性(不能混用不同厂商的零件)。
❌ 新增产品类型困难,因为所有工厂接口都需要修改

END

原文:裸机思维

专栏推荐文章

如果你喜欢我的思维,欢迎订阅裸机思维欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。
推荐阅读
关注数
1491
内容数
125
探讨嵌入式系统开发的相关思维、方法、技巧。
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息