工厂模式
工厂模式(Factory Pattern)是一种创建型设计模式,它提供了一种创建对象的最佳方式,同时隐藏了创建对象的细节。工厂模式的主要目的是将对象的实例化过程封装起来,使得客户端代码不需要知道具体对象的实例化细节,只需要知道如何使用工厂来创建对象即可。
-
工厂(Factory):工厂是一个接口或者抽象类,它定义了一个创建对象的方法。具体的工厂类负责实现这个方法,并根据客户端的请求来创建具体的对象。
-
具体工厂(Concrete Factory):具体工厂是工厂接口的实现类,它实现了创建对象的方法,并根据客户端的请求来创建具体的对象实例。
-
产品(Product):产品是工厂创建的对象,它可以是一个接口、抽象类或者具体类。工厂模式通过工厂创建产品对象,并将其返回给客户端使用。
场景
-
动态对象创建: 当程序需要根据某些条件或配置动态创建不同类型的对象时,工厂模式非常有用。工厂函数可以根据传入的参数或条件决定创建哪种类型的对象。
-
隐藏对象创建细节: 工厂模式可以将对象的创建过程封装在一个工厂函数中,从而隐藏对象的具体创建细节。这样可以降低代码的耦合度,提高代码的可维护性和可扩展性。
-
统一接口: 工厂模式可以为不同类型的对象提供一个统一的接口,使得客户端代码无需关心具体对象的创建过程,只需要通过工厂函数来获取对象实例。
-
扩展性: 当需要添加新的产品类时,可以通过扩展工厂函数来实现,而无需修改现有的客户端代码。这样可以提高代码的可扩展性,使得系统更加灵活。
-
多态性: 尽管C语言没有面向对象的多态性概念,但是通过工厂模式,你可以模拟多态性的行为。通过一个统一的接口函数来创建不同类型的对象,并通过函数指针来实现不同类型对象的特定行为。
使用结构体示例
#include <stdio.h>
#include <stdlib.h>
// 定义产品类型枚举
typedef enum {
PRODUCT_TYPE_A,
PRODUCT_TYPE_B
} ProductType;
// 定义产品结构体
typedef struct {
ProductType type; // 产品类型
int id; // 产品编号
char* name; // 产品名称
} Product;
// 创建产品A的工厂函数
Product* createProductA() {
Product* product = (Product*)malloc(sizeof(Product));
if (product != NULL) {
product->type = PRODUCT_TYPE_A;
product->id = 1;
product->name = "Product A";
}
return product;
}
// 创建产品B的工厂函数
Product* createProductB() {
Product* product = (Product*)malloc(sizeof(Product));
if (product != NULL) {
product->type = PRODUCT_TYPE_B;
product->id = 2;
product->name = "Product B";
}
return product;
}
// 工厂函数根据产品类型创建产品实例
Product* createProduct(ProductType type) {
switch (type) {
case PRODUCT_TYPE_A:
return createProductA();
case PRODUCT_TYPE_B:
return createProductB();
default:
return NULL;
}
}
int main() {
// 用户输入选择要创建的产品类型
printf("请输入要创建的产品类型(0代表产品A,1代表产品B):");
int choice;
scanf("%d", &choice);
// 创建产品
Product* product = createProduct((ProductType)choice);
// 根据产品类型显示产品信息
if (product != NULL) {
printf("产品类型:%s\n", (product->type == PRODUCT_TYPE_A) ? "产品A" : "产品B");
printf("产品编号:%d\n", product->id);
printf("产品名称:%s\n", product->name);
free(product); // 释放产品内存
} else {
printf("无效的产品类型。\n");
}
return 0;
}
- 输出结果
请输入要创建的产品类型(0代表产品A,1代表产品B):0
产品类型:产品A
产品编号:1
产品名称:Product A
请输入要创建的产品类型(0代表产品A,1代表产品B):1
产品类型:产品B
产品编号:2
产品名称:Product B
使用函数指针示例
#include <stdio.h>
#include <stdlib.h>
// 定义产品类型枚举
typedef enum {
PRODUCT_TYPE_A,
PRODUCT_TYPE_B
} ProductType;
// 定义产品结构体
typedef struct {
ProductType type; // 产品类型
int id; // 产品编号
char* name; // 产品名称
} Product;
// 定义工厂函数指针类型
typedef Product* (*CreateProductFunc)();
// 创建产品A的函数
Product* createProductA() {
Product* product = (Product*)malloc(sizeof(Product));
if (product != NULL) {
product->type = PRODUCT_TYPE_A;
product->id = 1;
product->name = "Product A";
}
return product;
}
// 创建产品B的函数
Product* createProductB() {
Product* product = (Product*)malloc(sizeof(Product));
if (product != NULL) {
product->type = PRODUCT_TYPE_B;
product->id = 2;
product->name = "Product B";
}
return product;
}
// 根据产品类型选择对应的工厂函数
CreateProductFunc chooseFactory(ProductType type) {
switch (type) {
case PRODUCT_TYPE_A:
return createProductA;
case PRODUCT_TYPE_B:
return createProductB;
default:
return NULL;
}
}
int main() {
// 用户输入选择要创建的产品类型
printf("请输入要创建的产品类型(0代表产品A,1代表产品B):");
int choice;
scanf("%d", &choice);
// 根据用户选择获取相应的工厂函数
CreateProductFunc factory = chooseFactory((ProductType)choice);
// 创建产品
Product* product = factory();
// 根据产品类型显示产品信息
if (product != NULL) {
printf("产品类型:%s\n", (product->type == PRODUCT_TYPE_A) ? "产品A" : "产品B");
printf("产品编号:%d\n", product->id);
printf("产品名称:%s\n", product->name);
free(product); // 释放产品内存
} else {
printf("无效的产品类型。\n");
}
return 0;
}
- 输出结果
输入要创建的产品类型(0代表产品A,1代表产品B):0
产品类型:产品A
产品编号:1
产品名称:Product A
请输入要创建的产品类型(0代表产品A,1代表产品B):1
产品类型:产品B
产品编号:2
产品名称:Product B
线程安全示例
使用C语言结构体实现工厂模式,并通过函数指针来动态选择创建产品的具体函数。同时,通过在多线程环境中使用不同的工厂对象来创建产品对象,确保了线程安全。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
// 定义产品类型枚举
typedef enum {
PRODUCT_TYPE_A,
PRODUCT_TYPE_B
} ProductType;
// 定义产品结构体
typedef struct {
ProductType type; // 产品类型
int id; // 产品编号
char* name; // 产品名称
} Product;
// 定义工厂函数指针类型
typedef Product* (*CreateProductFunc)();
// 创建产品A的函数
Product* createProductA() {
Product* product = (Product*)malloc(sizeof(Product));
if (product != NULL) {
product->type = PRODUCT_TYPE_A;
product->id = 1;
product->name = "Product A";
}
return product;
}
// 创建产品B的函数
Product* createProductB() {
Product* product = (Product*)malloc(sizeof(Product));
if (product != NULL) {
product->type = PRODUCT_TYPE_B;
product->id = 2;
product->name = "Product B";
}
return product;
}
// 定义工厂结构体
typedef struct {
CreateProductFunc createProduct; // 创建产品的函数指针
} Factory;
// 创建工厂对象的函数
Factory* createFactory(CreateProductFunc createProduct) {
Factory* factory = (Factory*)malloc(sizeof(Factory));
if (factory != NULL) {
factory->createProduct = createProduct;
}
return factory;
}
// 获取产品的函数
Product* getProduct(Factory* factory) {
if (factory != NULL && factory->createProduct != NULL) {
return factory->createProduct();
} else {
return NULL;
}
}
// 打印产品信息的函数
void printProductInfo(Product* product) {
if (product != NULL) {
printf("产品类型:%s, 编号:%d, 名称:%s\n",
product->type == PRODUCT_TYPE_A ? "A" : "B",
product->id, product->name);
} else {
printf("获取产品失败!\n");
}
}
int main() {
// 创建产品A工厂
Factory* factoryA = createFactory(createProductA);
// 创建产品B工厂
Factory* factoryB = createFactory(createProductB);
// 创建线程
pthread_t thread1, thread2;
// 在两个线程中同时获取产品
pthread_create(&thread1, NULL, (void*(*)(void*))getProduct, factoryA);
pthread_create(&thread2, NULL, (void*(*)(void*))getProduct, factoryB);
// 等待线程结束
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
// 输出产品信息
printf("线程1获取的产品信息:\n");
printProductInfo(getProduct(factoryA));
printf("线程2获取的产品信息:\n");
printProductInfo(getProduct(factoryB));
// 释放内存
free(factoryA);
free(factoryB);
return 0;
}
- 输出结果
线程1获取的产品信息:
产品类型:A, 编号:1, 名称:Product A
线程2获取的产品信息:
产品类型:B, 编号:2, 名称:Product B
单例模式
单例模式是一种创建型设计模式,用于确保类只有一个实例,并提供全局访问点。在C语言中,由于缺乏类和对象的概念,单例模式的实现会略有不同,通常可以通过静态变量或全局变量来实现。
场景
-
全局资源管理: 当程序中需要共享一个全局资源,例如日志文件、配置文件、数据库连接等,可以使用单例模式确保只有一个实例存在,避免资源的重复创建和管理。
-
线程池、连接池: 在多线程环境中,使用单例模式可以创建一个线程池或连接池,确保所有线程共享同一个池,提高资源利用率和性能。
-
对象缓存: 在需要频繁创建和销毁对象的场景下,可以使用单例模式来缓存对象,避免重复的创建和销毁过程,提高性能。
-
系统配置管理: 当程序需要读取和管理系统的配置信息时,可以使用单例模式将配置信息保存在单例对象中,提供全局访问点,方便其他模块使用。
-
日志管理: 单例模式可以用于创建日志管理器,确保只有一个日志对象存在,并提供统一的接口来记录日志,方便程序的调试和排查问题。
-
游戏开发: 在游戏开发中,通常会有一些全局的管理器对象,例如场景管理器、资源管理器、音效管理器等,可以使用单例模式确保这些管理器只有一个实例存在。
示例
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
// 定义单例结构体
typedef struct {
// 单例对象的属性
int data;
} Singleton;
// 获取单例实例的函数
Singleton* getSingletonInstance() {
// 定义静态局部变量来存储单例对象的实例
static Singleton instance = {0};
// 返回单例对象的指针
return &instance;
}
int main() {
// 获取单例实例
Singleton* singleton1 = getSingletonInstance();
Singleton* singleton2 = getSingletonInstance();
// 修改单例实例的数据
singleton1->data = 10;
// 输出单例实例的数据
printf("单例实例1的数据:%d\n", singleton2->data); // 预期输出结果为 10
return 0;
}
- 输出结果
单例实例1的数据:10
线程安全示例
使用互斥锁来确保单例对象的创建过程是线程安全的。定义了一个名为singletonMutex的互斥锁,并在getSingletonInstance函数中使用pthread_mutex_lock和pthread_mutex_unlock函数来加锁和解锁。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
// 定义单例结构体
typedef struct {
// 单例对象的属性
int data;
} Singleton;
// 定义单例对象指针
static Singleton *singletonInstance = NULL;
// 定义互斥锁
static pthread_mutex_t singletonMutex = PTHREAD_MUTEX_INITIALIZER;
// 获取单例实例的函数(线程安全版本)
Singleton* getSingletonInstance() {
// 加锁
pthread_mutex_lock(&singletonMutex);
// 如果单例对象尚未创建,则创建它
if (singletonInstance == NULL) {
singletonInstance = (Singleton*)malloc(sizeof(Singleton));
if (singletonInstance != NULL) {
singletonInstance->data = 0;
}
}
// 解锁
pthread_mutex_unlock(&singletonMutex);
// 返回单例对象指针
return singletonInstance;
}
int main() {
// 创建线程
pthread_t thread1, thread2;
// 在两个线程中同时获取单例实例
pthread_create(&thread1, NULL, (void*(*)(void*))getSingletonInstance, NULL);
pthread_create(&thread2, NULL, (void*(*)(void*))getSingletonInstance, NULL);
// 等待线程结束
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
// 输出单例实例的地址
printf("单例实例1的地址:%p\n", getSingletonInstance());
printf("单例实例2的地址:%p\n", getSingletonInstance());
return 0;
}
- 输出结果
单例实例1的地址:0x7ff329111840
单例实例2的地址:0x7ff329111840
建造者模式
建造者模式(Builder Pattern)是一种创建型设计模式,它将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。
-
产品(Product): 表示被构建的复杂对象。通常,产品类是一个包含多个部件的复杂对象,它们的组合形成了一个完整的产品。
-
建造者(Builder): 定义了构建产品的接口。通常,建造者接口包含多个方法,每个方法用于构建产品的一个部件。不同的具体建造者可以实现相同的接口,但是具体实现方式可能不同。
-
具体建造者(Concrete Builder): 实现了建造者接口,负责构建具体的产品部件。每个具体建造者负责构建特定类型的产品,并提供了构建该产品所需的具体实现逻辑。
-
指挥者(Director): 负责使用建造者对象构建产品。指挥者通常包含一个构建方法,该方法接收一个建造者对象作为参数,并根据需要调用建造者对象的方法来构建产品。
场景
-
复杂对象的构建: 当一个对象的构建过程较为复杂,需要多个步骤或者多个组件来完成,可以使用建造者模式。通过将构建过程拆分成多个步骤,并由建造者对象负责组装这些步骤,可以使得构建过程更加灵活和可控。
-
对象的构建与表示分离: 建造者模式可以将一个复杂对象的构建过程与其表示分离,使得构建过程独立于表示,并且可以灵活地组装不同的部件来构建不同的对象。
-
创建复杂的产品变种: 当需要创建多个相似但又有所不同的产品变种时,可以使用建造者模式。通过定义不同的建造者来创建不同的产品变种,可以有效地复用相同的构建过程。
-
避免构造函数参数过多: 当一个类的构造函数需要接收大量参数时,可以使用建造者模式。通过将参数封装在建造者对象中,并逐步设置这些参数,可以避免构造函数参数过多导致代码难以理解和维护的问题。
-
动态配置对象的属性: 建造者模式允许在构建过程中动态配置对象的属性,从而可以根据需求灵活地调整对象的属性。
示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义产品结构体
typedef struct {
char type[20]; // 产品类型
int size; // 产品尺寸
char color[20]; // 产品颜色
} Product;
// 定义建造者结构体
typedef struct {
// 建造者对象的属性
char type[20]; // 产品类型
int size; // 产品尺寸
char color[20]; // 产品颜色
} Builder;
// 定义建造者初始化函数
void initBuilder(Builder *builder, const char *type, int size, const char *color) {
strncpy(builder->type, type, sizeof(builder->type) - 1);
builder->type[sizeof(builder->type) - 1] = '\0'; // 确保字符串以'\0'结尾
builder->size = size;
strncpy(builder->color, color, sizeof(builder->color) - 1);
builder->color[sizeof(builder->color) - 1] = '\0'; // 确保字符串以'\0'结尾
}
// 定义建造者创建产品的函数
void buildProduct(Builder *builder, Product *product) {
// 将建造者对象的属性复制到产品对象
strncpy(product->type, builder->type, sizeof(product->type) - 1);
product->type[sizeof(product->type) - 1] = '\0'; // 确保字符串以'\0'结尾
product->size = builder->size;
strncpy(product->color, builder->color, sizeof(product->color) - 1);
product->color[sizeof(product->color) - 1] = '\0'; // 确保字符串以'\0'结尾
}
int main() {
// 创建建造者对象
Builder builder;
initBuilder(&builder, "Chair", 20, "Red");
// 创建产品对象
Product product;
buildProduct(&builder, &product);
// 输出产品信息
printf("产品类型:%s\n", product.type);
printf("产品尺寸:%d\n", product.size);
printf("产品颜色:%s\n", product.color);
return 0;
}
- 输出结果
产品类型:Chair
产品尺寸:20
产品颜色:Red
原型模式
原型模式(Prototype Pattern)是一种创建型设计模式,它用于创建对象的新实例,同时通过复制原型对象的方法来避免显式的构造函数调用。原型模式基于原型对象创建新对象,而不是通过实例化类。
-
原型(Prototype):原型是一个接口或者抽象类,它定义了一个克隆方法,用于复制原型对象并返回新的对象实例。具体的原型类负责实现这个克隆方法,以复制自身并返回新的对象实例。
-
具体原型(Concrete Prototype):具体原型是原型接口的实现类,它实现了克隆方法,并在其中对自身进行复制并返回新的对象实例。
-
客户端(Client):客户端使用原型对象来创建新的对象实例,而不需要显式调用构造函数。客户端通过调用原型对象的克隆方法来复制原型对象并获取新的对象实例。
场景
-
动态创建对象: 当需要根据运行时的条件或用户输入动态创建对象时,原型模式非常有用。通过克隆一个已有的对象,可以避免重复的创建过程,提高效率。
-
对象初始化复杂: 如果对象的初始化过程比较复杂,包含大量的计算或资源分配,可以使用原型模式来避免重复的初始化过程。通过复制已有的对象,可以节省初始化的时间和资源消耗。
-
减少子类数量: 在一些情况下,可能存在大量类似的对象,但它们的差异只是一些属性或配置的不同。使用原型模式可以减少类的数量,将共同的部分抽取到原型对象中,然后通过克隆来创建不同的对象。
-
资源共享: 如果多个对象需要共享某些资源,但又需要保持独立性,可以使用原型模式。通过共享原型对象,可以避免资源的重复分配,提高资源利用率。
-
保护对象: 在一些情况下,需要保护对象的创建过程,防止外部直接访问构造函数或初始化函数。使用原型模式可以隐藏对象的创建细节,只提供一个克隆接口来创建对象。
示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义原型结构体
typedef struct {
// 原型对象的属性
int id;
char name[20];
} Prototype;
// 创建原型对象的函数
Prototype* createPrototype(int id, const char *name) {
// 动态分配内存并初始化原型对象
Prototype *prototype = (Prototype*)malloc(sizeof(Prototype));
if (prototype != NULL) {
prototype->id = id;
strncpy(prototype->name, name, sizeof(prototype->name) - 1);
prototype->name[sizeof(prototype->name) - 1] = '\0'; // 确保字符串以'\0'结尾
}
return prototype;
}
// 复制原型对象的函数
Prototype* clonePrototype(Prototype *prototype) {
// 动态分配内存并复制原型对象
Prototype *clone = (Prototype*)malloc(sizeof(Prototype));
if (clone != NULL) {
*clone = *prototype; // 执行浅拷贝
}
return clone;
}
int main() {
// 创建原型对象
Prototype *original = createPrototype(1, "Original");
// 克隆原型对象
Prototype *clone = clonePrototype(original);
// 修改克隆对象的数据
clone->id = 2;
strncpy(clone->name, "Clone", sizeof(clone->name) - 1);
clone->name[sizeof(clone->name) - 1] = '\0'; // 确保字符串以'\0'结尾
// 输出原型对象和克隆对象的数据
printf("原型对象:ID=%d, Name=%s\n", original->id, original->name);
printf("克隆对象:ID=%d, Name=%s\n", clone->id, clone->name);
// 释放内存
free(original);
free(clone);
return 0;
}
- 输出结果
原型对象:ID=1, Name=Original
克隆对象:ID=2, Name=Clone