文章目录
策略模式
策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法,并将每个算法封装成独立的对象,使得它们可以互相替换。策略模式使得算法的变化独立于使用算法的客户端,从而提高了系统的灵活性和可维护性。
-
Context(上下文):上下文是使用算法的客户端对象,它包含一个指向策略对象的引用,并在需要时调用策略对象的算法。上下文对象通常会将请求委托给策略对象来执行。
-
Strategy(策略):策略是一个接口或者抽象类,它定义了一个算法族,并将每个算法封装成一个具体的策略对象。所有具体策略对象都实现了相同的接口或者抽象类。
-
ConcreteStrategy(具体策略):具体策略是策略接口的实现类,它实现了具体的算法逻辑。当客户端需要使用某种算法时,可以将相应的具体策略对象传递给上下文对象,从而使得上下文对象可以调用该算法。
场景
-
排序算法:在排序算法中,可能需要根据数据的不同特征选择不同的排序算法,比如快速排序、归并排序或者插入排序。可以将每种排序算法封装成一个策略,然后根据需要在运行时选择使用哪种策略。
-
数据压缩:在数据压缩领域,可能存在多种压缩算法,如LZW、Huffman等。通过使用策略模式,可以根据不同的压缩需求选择不同的算法。
-
图像处理:在图像处理中,可能需要根据图像的特征选择不同的处理算法,比如模糊、锐化、边缘检测等。通过使用策略模式,可以将每种处理算法封装成一个策略,然后根据需要选择不同的策略。
-
网络协议:在网络编程中,可能需要根据不同的网络协议选择不同的处理方式,比如TCP、UDP等。通过使用策略模式,可以将每种协议的处理方式封装成一个策略,然后根据需要选择不同的策略。
示例
c
#include <stdio.h>
// 支付信息结构体
typedef struct {
float amount; // 支付金额
const char *orderId; // 订单ID
} PaymentInfo;
// 支付策略接口
typedef struct {
void (*pay)(PaymentInfo); // 支付函数指针
} PaymentStrategy;
// 信用卡支付策略
void creditCardPayment(PaymentInfo paymentInfo) {
printf("使用信用卡支付 %.2f 元,订单号:%s\n", paymentInfo.amount, paymentInfo.orderId);
// 添加信用卡支付的具体实现
}
// 支付宝支付策略
void alipayPayment(PaymentInfo paymentInfo) {
printf("使用支付宝支付 %.2f 元,订单号:%s\n", paymentInfo.amount, paymentInfo.orderId);
// 添加支付宝支付的具体实现
}
// 微信支付策略
void wechatPayment(PaymentInfo paymentInfo) {
printf("使用微信支付 %.2f 元,订单号:%s\n", paymentInfo.amount, paymentInfo.orderId);
// 添加微信支付的具体实现
}
// 根据支付方式选择支付策略
void makePayment(PaymentInfo paymentInfo, PaymentStrategy *strategy) {
strategy->pay(paymentInfo);
}
int main() {
PaymentInfo paymentInfo = {100.00, "123456789"}; // 支付金额为100元,订单号为123456789
// 不同的支付策略
PaymentStrategy creditCardStrategy = {creditCardPayment};
PaymentStrategy alipayStrategy = {alipayPayment};
PaymentStrategy wechatStrategy = {wechatPayment};
// 信用卡支付
makePayment(paymentInfo, &creditCardStrategy);
// 支付宝支付
makePayment(paymentInfo, &alipayStrategy);
// 微信支付
makePayment(paymentInfo, &wechatStrategy);
return 0;
}
- 输出结果
bash
使用信用卡支付 100.00 元,订单号:123456789
使用支付宝支付 100.00 元,订单号:123456789
使用微信支付 100.00 元,订单号:123456789
迭代器模式
迭代器模式(Iterator Pattern)是一种行为型设计模式,它提供一种方法来顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。通过使用迭代器模式,可以在不了解聚合对象内部结构的情况下,对聚合对象中的元素进行遍历操作。
-
Iterator(迭代器):迭代器是一个接口或者抽象类,它定义了访问和遍历聚合对象中各个元素的方法。具体的迭代器实现类负责实现这些方法,以实现对不同类型聚合对象的遍历操作。
-
ConcreteIterator(具体迭代器):具体迭代器是迭代器接口的实现类,它实现了对具体聚合对象的遍历操作。具体迭代器对象会跟踪遍历过程中的当前位置,并提供访问和遍历下一个元素的方法。
-
Aggregate(聚合对象):聚合对象是一个接口或者抽象类,它定义了创建迭代器对象的方法。具体的聚合对象实现类负责实现这些方法,并提供迭代器对象用于遍历聚合对象中的元素。
-
ConcreteAggregate(具体聚合对象):具体聚合对象是聚合对象接口的实现类,它实现了创建具体迭代器对象的方法,并提供了访问聚合对象中各个元素的方法。
场景
-
数据结构遍历:迭代器模式最典型的应用场景就是对数据结构的遍历,比如链表、数组、树等。通过将遍历算法封装在迭代器中,可以将数据结构的具体实现和遍历逻辑分离开来,使得遍历过程更加灵活。
-
文件处理:在文件处理中,可能需要逐行读取文件内容或者按照一定规则遍历文件中的数据。使用迭代器模式可以将文件读取的逻辑抽象为迭代器,从而可以在不同的文件类型或者数据结构上使用相同的遍历算法。
-
内存管理:在操作内存数据结构时,比如堆、栈等,可能需要遍历内存中的数据或者对内存中的数据进行操作。通过使用迭代器模式,可以将对内存数据结构的遍历和操作与具体的数据结构分离开来,提高代码的灵活性和可维护性。
-
数据库查询:在数据库操作中,可能需要对查询结果进行遍历和处理。使用迭代器模式可以将查询结果封装为迭代器,从而可以在不同的数据库查询结果上使用相同的遍历算法。
-
图形界面编程:在图形界面编程中,可能需要遍历界面中的控件或者元素。使用迭代器模式可以将界面中的控件或者元素抽象为迭代器,从而可以在不同的界面布局上使用相同的遍历算法。
示例
c
#include <stdio.h>
// 整数数组结构体
typedef struct {
int *data; // 整数数组指针
int size; // 数组大小
} IntArray;
// 迭代器结构体
typedef struct {
const IntArray *array; // 指向要迭代的数组的指针
int index; // 当前迭代的索引
} IntArrayIterator;
// 初始化整数数组
void initIntArray(IntArray *array, int *data, int size) {
array->data = data;
array->size = size;
}
// 初始化迭代器
void initIntArrayIterator(IntArrayIterator *iterator, const IntArray *array) {
iterator->array = array;
iterator->index = 0;
}
// 获取迭代器当前指向的元素
int getIntArrayIteratorCurrent(const IntArrayIterator *iterator) {
return iterator->array->data[iterator->index];
}
// 移动迭代器到下一个元素
void moveIntArrayIteratorNext(IntArrayIterator *iterator) {
++iterator->index;
}
// 判断迭代器是否到达末尾
int isIntArrayIteratorEnd(const IntArrayIterator *iterator) {
return iterator->index >= iterator->array->size;
}
int main() {
int data[] = {1, 2, 3, 4, 5};
IntArray array;
initIntArray(&array, data, sizeof(data) / sizeof(data[0]));
IntArrayIterator iterator;
initIntArrayIterator(&iterator, &array);
// 遍历整数数组并输出
while (!isIntArrayIteratorEnd(&iterator)) {
printf("%d ", getIntArrayIteratorCurrent(&iterator));
moveIntArrayIteratorNext(&iterator);
}
printf("\n");
return 0;
}
- 输出结果
bash
1 2 3 4 5
访问者模式
访问者模式(Visitor Pattern)是一种行为型设计模式,它可以在不修改对象结构的情况下,定义新的操作或算法并应用于对象的元素。该模式使得可以在不改变元素类的前提下,增加新的操作方式。
-
Visitor(访问者):访问者是一个接口或者抽象类,它定义了对每个元素对象进行操作的方法,这些方法的参数类型决定了哪些对象可以被访问以及如何访问它们。
-
ConcreteVisitor(具体访问者):具体访问者是Visitor接口的实现类,它实现了对元素对象的具体操作逻辑。
-
Element(元素):元素是一个接口或者抽象类,它定义了一个accept方法,该方法接受访问者对象作为参数,以便让访问者对象访问它。
-
ConcreteElement(具体元素):具体元素是Element接口的实现类,它实现了accept方法,并在其中调用访问者对象的相应方法。
-
ObjectStructure(对象结构):对象结构是一个集合,它用于存储元素对象,并提供一个接受访问者对象的方法,以便访问者可以访问其中的元素对象。
场景
-
抽象语法树(AST)的遍历和操作:在编译器开发中,抽象语法树(AST)用于表示源代码的结构,而访问者模式可以用于遍历和操作这种复杂的数据结构。通过定义不同的访问者,可以实现不同的操作,比如语法检查、代码生成等。
-
文件解析和处理:在文件解析和处理过程中,可能需要根据文件的不同类型执行不同的操作,比如解析XML文件、JSON文件等。访问者模式可以将对文件的操作抽象为访问者,从而可以在不同类型的文件上应用相同的操作。
-
数据结构的序列化和反序列化:在将数据结构序列化为字节流或者反序列化为数据结构时,可能需要对数据结构的不同部分进行不同的处理。访问者模式可以将序列化和反序列化的操作抽象为访问者,从而可以在不同的数据结构上应用相同的操作。
-
图形界面的事件处理:在图形界面编程中,可能需要对不同的界面元素执行不同的操作,比如按钮点击事件、鼠标移动事件等。访问者模式可以将事件处理逻辑抽象为访问者,从而可以在不同的界面元素上应用相同的事件处理逻辑。
-
数据库查询和操作:在数据库编程中,可能需要对不同的数据库表执行不同的操作,比如查询数据、更新数据等。访问者模式可以将对数据库表的操作抽象为访问者,从而可以在不同的数据库表上应用相同的操作。
示例
c
#include <stdio.h>
// 前置声明,以便互相引用
typedef struct Circle Circle;
typedef struct Rectangle Rectangle;
// 访问者结构体声明
typedef struct {
// 访问不同图形的函数指针
void (*visitCircle)(Circle *circle);
void (*visitRectangle)(Rectangle *rectangle);
} Visitor;
// 圆形结构体声明
struct Circle {
int radius; // 半径
};
// 矩形结构体声明
struct Rectangle {
int width; // 宽度
int height; // 高度
};
// 圆形的accept方法
void circleAccept(Visitor *visitor, Circle *circle) {
visitor->visitCircle(circle);
}
// 矩形的accept方法
void rectangleAccept(Visitor *visitor, Rectangle *rectangle) {
visitor->visitRectangle(rectangle);
}
// 计算圆形面积的visit方法
void calculateCircleArea(Circle *circle) {
float area = 3.14 * circle->radius * circle->radius;
printf("圆形面积:%f\n", area);
}
// 计算矩形面积的visit方法
void calculateRectangleArea(Rectangle *rectangle) {
float area = rectangle->width * rectangle->height;
printf("矩形面积:%f\n", area);
}
int main() {
// 创建访问者
Visitor areaCalculator = {
.visitCircle = calculateCircleArea,
.visitRectangle = calculateRectangleArea
};
// 创建图形
Circle circle = {5};
Rectangle rectangle = {4, 6};
// 计算图形面积
circleAccept(&areaCalculator, &circle);
rectangleAccept(&areaCalculator, &rectangle);
return 0;
}
- 输出结果
bash
圆形面积:78.500000
矩形面积:24.000000
观察者模式
观察者模式(Observer Pattern)是一种行为型设计模式,用于定义对象之间的一对多依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都会得到通知并自动更新。这种模式也被称为发布-订阅模式。
-
Subject(主题):主题是被观察的对象,它包含了一组观察者对象,并提供了方法来注册、删除和通知观察者。当主题的状态发生变化时,它会通知所有注册的观察者。
-
Observer(观察者):观察者是订阅主题的对象,它定义了一个接口,当接收到主题的通知时,可以执行相应的操作。每个观察者都必须实现这个接口,以便主题能够调用它们的更新方法。
场景
-
事件处理和消息传递系统:在事件驱动的系统中,观察者模式非常有用。当一个事件发生时,所有注册的观察者都会收到通知并执行相应的操作。这种模式在图形用户界面(GUI)编程中尤其常见,例如,当用户点击按钮时,所有注册的事件处理程序都会收到通知并执行相应的操作。
-
发布-订阅模式:观察者模式的一种变体是发布-订阅模式。在这种模式中,发布者(也称为主题或事件总线)负责向所有订阅者(观察者)发布消息或事件。这种模式在消息队列系统、即时通讯应用程序等领域被广泛应用。
-
状态变化通知:当一个对象的状态发生变化时,它可能需要通知其他对象以执行相应的操作。例如,在游戏开发中,一个玩家对象的状态变化(比如生命值减少)可能需要通知其他玩家或者游戏引擎来执行相应的动作。
-
数据变更通知:在软件开发中,当一个数据模型的状态发生变化时,可能需要通知所有依赖该数据模型的视图或者其他组件进行更新。观察者模式可以用于实现这种数据变更通知机制。
-
日志记录和错误处理:在应用程序中,观察者模式可以用于实现日志记录和错误处理机制。当发生错误或者异常时,可以通知所有注册的日志记录器进行记录,并且通知所有注册的错误处理程序进行处理。
示例
c
#include <stdio.h>
// 观察者结构体声明
typedef struct {
void (*update)(void); // 更新方法指针
} Observer;
// 主题结构体声明
typedef struct {
Observer *observers[10]; // 最多支持10个观察者
int count; // 当前观察者数量
} Subject;
// 初始化主题
void initSubject(Subject *subject) {
subject->count = 0;
}
// 注册观察者
void attachObserver(Subject *subject, Observer *observer) {
if (subject->count < 10) {
subject->observers[subject->count++] = observer;
} else {
printf("无法注册观察者,已达到最大数量\n");
}
}
// 更新所有观察者
void notifyObservers(Subject *subject) {
for (int i = 0; i < subject->count; ++i) {
subject->observers[i]->update();
}
}
// 观察者A的更新方法
void observerAUpdate() {
printf("观察者A收到通知,执行更新操作\n");
}
// 观察者B的更新方法
void observerBUpdate() {
printf("观察者B收到通知,执行更新操作\n");
}
int main() {
// 创建主题和观察者
Subject subject;
Observer observerA = {observerAUpdate};
Observer observerB = {observerBUpdate};
// 初始化主题
initSubject(&subject);
// 注册观察者
attachObserver(&subject, &observerA);
attachObserver(&subject, &observerB);
// 主题状态变化,通知所有观察者
notifyObservers(&subject);
return 0;
}
- 输出结果
bash
观察者A收到通知,执行更新操作
观察者B收到通知,执行更新操作
命令模式
命令模式(Command Pattern)是一种行为型设计模式,它用于将请求或操作封装为独立的对象,从而使得请求的发送者和接收者之间解耦。在命令模式中,请求被封装成一个命令对象,请求的发送者(客户端)只需创建命令对象并将其发送给接收者(调用者),而无需了解命令的具体实现方式。
-
Command(命令):命令是一个接口或者抽象类,它定义了执行操作的方法。具体的命令对象实现了这个接口,并负责实现具体的操作逻辑。
-
ConcreteCommand(具体命令):具体命令是命令接口的实现类,它封装了请求的接收者和执行操作的具体逻辑。
-
Invoker(调用者):调用者是负责调用命令对象的对象,它只负责向命令对象发送请求,而无需了解命令的具体实现方式。
-
Receiver(接收者):接收者是命令的实际执行者,它负责执行具体的操作。命令对象将请求委托给接收者来执行。
场景
-
图形用户界面(GUI)编程:在图形用户界面中,用户的操作(比如点击按钮、菜单项等)需要触发相应的操作。命令模式可以将这些操作封装为命令对象,并将其发送给对应的接收者(控件或者界面元素)来执行。
-
多线程任务调度:在多线程编程中,可能需要将一些操作封装成任务,并将这些任务交给线程池来执行。命令模式可以将任务封装为命令对象,并将其发送给线程池来执行。
-
日志记录:在软件开发中,可能需要记录系统的操作日志。命令模式可以将记录日志的操作封装为命令对象,并将其发送给日志记录器来执行。
-
设备控制:在嵌入式系统或者硬件控制领域,可能需要控制一些设备或者执行一些特定的操作。命令模式可以将这些操作封装为命令对象,并将其发送给设备控制器来执行。
-
文本编辑器的撤销/重做功能:在文本编辑器中,用户的操作(比如添加文本、删除文本等)可能需要支持撤销和重做功能。命令模式可以将用户的操作封装为命令对象,并将其保存在命令历史记录中,从而支持撤销和重做操作。
示例
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义命令接口
typedef struct {
void (*execute)(void); // 执行命令的函数指针
void (*undo)(void); // 撤销命令的函数指针
} Command;
// 添加文本命令
typedef struct {
Command base; // 基类命令
char *text; // 要添加的文本
} AddTextCommand;
// 删除文本命令
typedef struct {
Command base; // 基类命令
char *text; // 要删除的文本
} DeleteTextCommand;
// 文本编辑器
typedef struct {
char *content; // 文本内容
} TextEditor;
// 执行添加文本命令
void executeAddTextCommand(AddTextCommand *command) {
printf("添加文本:%s\n", command->text);
// 实际执行添加文本的操作
}
// 执行删除文本命令
void executeDeleteTextCommand(DeleteTextCommand *command) {
printf("删除文本:%s\n", command->text);
// 实际执行删除文本的操作
}
// 撤销添加文本命令
void undoAddTextCommand(AddTextCommand *command) {
printf("撤销添加文本:%s\n", command->text);
// 实际执行撤销添加文本的操作
}
// 撤销删除文本命令
void undoDeleteTextCommand(DeleteTextCommand *command) {
printf("撤销删除文本:%s\n", command->text);
// 实际执行撤销删除文本的操作
}
// 初始化文本编辑器
void initTextEditor(TextEditor *editor) {
editor->content = NULL;
}
// 执行命令
void executeCommand(Command *command) {
command->execute();
}
// 撤销命令
void undoCommand(Command *command) {
command->undo();
}
int main() {
// 创建文本编辑器
TextEditor editor;
initTextEditor(&editor);
// 创建添加文本命令
AddTextCommand addCommand = {
.base.execute = (void (*)(void))executeAddTextCommand,
.base.undo = (void (*)(void))undoAddTextCommand,
.text = "Hello, World!"
};
// 创建删除文本命令
DeleteTextCommand deleteCommand = {
.base.execute = (void (*)(void))executeDeleteTextCommand,
.base.undo = (void (*)(void))undoDeleteTextCommand,
.text = "Hello, World!"
};
// 执行添加文本命令
executeCommand((Command *)&addCommand);
// 执行删除文本命令
executeCommand((Command *)&deleteCommand);
// 撤销删除文本命令
undoCommand((Command *)&deleteCommand);
return 0;
}
- 输出结果
bash
添加文本:Hello, World!
删除文本:Hello, World!
撤销删除文本:Hello, World!
模板方法模式
在C语言中,没有像其他面向对象语言那样提供原生的类和继承机制。因此,在C语言中实现传统意义上的模板方法模式会有一定挑战。然而,我们可以使用函数指针和回调函数来实现类似的行为。
模板方法模式是一种行为型设计模式,它定义了一个算法的框架,并将其中的一些步骤延迟到子类中实现。模板方法模式通过在父类中定义算法的骨架,并在子类中实现具体的步骤,以达到在运行时选择不同的行为的目的。
场景
-
算法的骨架已经确定,但具体步骤可能变化: 如果有一个算法的骨架已经确定,但其中的某些具体步骤可能会因为应用场景或者其他条件而有所变化,这时可以使用模板方法模式。通过在父类中定义算法的骨架,子类可以根据需要重写某些步骤,从而实现不同的行为。
-
避免代码重复: 如果有多个算法有相似的流程或步骤,但具体实现略有不同,这时可以将这些共同的部分提取出来,放在父类中作为模板方法,然后子类只需要实现各自特定的步骤,从而避免了代码的重复。
-
实现开闭原则: 模板方法模式可以使得算法的框架稳定不变,而具体的步骤可以灵活变化,从而符合开闭原则,即对扩展开放,对修改关闭。
示例
c
#include <stdio.h>
// 定义函数指针类型,代表算法中的某个步骤
typedef void (*StepFunction)(void);
// 父类
typedef struct {
StepFunction step1;
StepFunction step2;
StepFunction step3;
} Template;
// 模板方法,定义了算法的框架
void templateMethod(Template *obj) {
printf("开始执行模板方法\n");
obj->step1();
obj->step2();
obj->step3();
printf("结束执行模板方法\n");
}
// 子类1
typedef struct {
Template base; // 继承父类
} ConcreteClass1;
// 具体实现子类1的步骤1
void concreteClass1Step1() {
printf("子类1:步骤 1\n");
}
// 具体实现子类1的步骤2
void concreteClass1Step2() {
printf("子类1:步骤 2\n");
}
// 具体实现子类1的步骤3
void concreteClass1Step3() {
printf("子类1:步骤 3\n");
}
// 子类2
typedef struct {
Template base; // 继承父类
} ConcreteClass2;
// 具体实现子类2的步骤1
void concreteClass2Step1() {
printf("子类2:步骤 1\n");
}
// 具体实现子类2的步骤2
void concreteClass2Step2() {
printf("子类2:步骤 2\n");
}
// 具体实现子类2的步骤3
void concreteClass2Step3() {
printf("子类2:步骤 3\n");
}
int main() {
// 创建子类1对象
ConcreteClass1 obj1;
obj1.base.step1 = concreteClass1Step1;
obj1.base.step2 = concreteClass1Step2;
obj1.base.step3 = concreteClass1Step3;
// 创建子类2对象
ConcreteClass2 obj2;
obj2.base.step1 = concreteClass2Step1;
obj2.base.step2 = concreteClass2Step2;
obj2.base.step3 = concreteClass2Step3;
// 使用模板方法
templateMethod((Template *)&obj1);
templateMethod((Template *)&obj2);
return 0;
}
- 输出结果
bash
开始执行模板方法
子类1:步骤 1
子类1:步骤 2
子类1:步骤 3
结束执行模板方法
开始执行模板方法
子类2:步骤 1
子类2:步骤 2
子类2:步骤 3
结束执行模板方法
事件驱动模式
事件驱动模式是一种软件设计模式,它基于事件和事件处理机制。在事件驱动模式中,程序的执行流程是由外部事件的发生和相应的事件处理程序的执行来驱动的。
-
事件(Event):事件是程序运行过程中发生的某种事情或者状态变化,可以是用户的输入、系统的消息、定时器的触发等。事件可以分为不同的类型,每种类型的事件都有相应的处理程序。
-
事件处理程序(Event Handler):事件处理程序是针对特定类型的事件而设计的函数或者方法。当特定类型的事件发生时,相应的事件处理程序会被调用来处理事件,执行相应的逻辑。
-
事件循环(Event Loop):事件循环是一个在程序中持续运行的循环,它负责等待事件的发生,并将事件分派给相应的事件处理程序。事件循环不断地从事件队列中获取事件,然后根据事件的类型找到相应的事件处理程序来处理事件。
-
回调函数(Callback Function):回调函数是一种特殊的函数,它作为参数传递给其他函数,在特定的事件发生时被调用。在事件驱动模式中,回调函数通常用于实现事件处理程序,当特定类型的事件发生时,相应的回调函数会被调用来处理事件。
事件驱动模式的核心思想是将程序的控制权交给事件循环,由事件循环负责等待事件的发生,并将事件分派给相应的事件处理程序来处理。这种方式使得程序能够异步地响应外部事件,提高了程序的并发性和响应速度。
场景
-
图形用户界面(GUI)编程: 在图形用户界面应用程序中,用户的操作(例如点击按钮、输入文本等)会触发各种事件,程序需要根据这些事件来执行相应的操作。事件驱动模式可以用于处理用户交互事件,例如按钮点击事件、鼠标移动事件等。
-
网络编程: 在网络编程中,网络事件(例如连接建立、数据到达、连接关闭等)会触发相应的事件处理程序,用于处理这些事件并执行相应的操作。事件驱动模式可以用于实现异步的网络通信,提高程序的并发性和响应速度。
-
多线程编程: 在多线程编程中,线程的运行是由事件和信号来驱动的。例如,一个线程可以等待某个条件变量的信号,一旦条件变量发生变化,就会触发相应的事件处理程序来执行相应的操作。事件驱动模式可以用于实现多线程编程中的事件驱动模型,提高程序的并发性和可维护性。
-
系统级编程: 在系统级编程中,操作系统会产生各种事件,例如定时器事件、硬件中断事件等。程序需要根据这些事件来执行相应的操作,以实现系统的各种功能。事件驱动模式可以用于处理系统级事件,实现系统级功能。
示例
c
#include <stdio.h>
// 定义事件类型枚举
typedef enum {
BUTTON_CLICK_EVENT,
KEY_PRESS_EVENT
} EventType;
// 定义事件结构体
typedef struct {
EventType type;
int data; // 事件数据,例如按钮编号、按键键值等
} Event;
// 定义事件处理函数类型
typedef void (*EventHandler)(Event *);
// 定义按钮点击事件处理函数
void buttonClickHandler(Event *event) {
printf("处理按钮点击事件,按钮编号:%d\n", event->data);
}
// 定义按键按下事件处理函数
void keyPressHandler(Event *event) {
printf("处理按键按下事件,键值:%d\n", event->data);
}
// 模拟事件循环
void eventLoop() {
// 假设有一系列事件需要处理
Event events[] = {
{BUTTON_CLICK_EVENT, 1}, // 模拟按钮1被点击事件
{KEY_PRESS_EVENT, 65} // 模拟按键'A'被按下事件
};
// 处理事件
for (int i = 0; i < sizeof(events) / sizeof(Event); i++) {
Event event = events[i];
switch (event.type) {
case BUTTON_CLICK_EVENT:
buttonClickHandler(&event); // 调用按钮点击事件处理函数
break;
case KEY_PRESS_EVENT:
keyPressHandler(&event); // 调用按键按下事件处理函数
break;
default:
printf("未知事件类型\n");
}
}
}
int main() {
// 启动事件循环
eventLoop();
return 0;
}
- 输出结果
bash
处理按钮点击事件,按钮编号:1
处理按键按下事件,键值:65
责任链模式
责任链模式是一种行为型设计模式,用于组织处理对象的责任链。在责任链模式中,多个对象依次处理请求,直到其中一个对象能够处理该请求为止。每个处理对象都持有对下一个处理对象的引用,形成一个链条。请求沿着这条链条依次传递,直到被处理。
-
抽象处理者(Handler): 定义了一个处理请求的接口,通常包含一个处理请求的方法。可以有多个具体处理者实现这个接口。
-
具体处理者(Concrete Handler): 实现了抽象处理者接口,负责处理特定类型的请求。如果自己无法处理请求,则将请求传递给下一个处理者。
-
客户端(Client): 创建责任链,并向责任链中的第一个处理者发送请求。
场景
-
系统调用处理: 在操作系统中,可以使用责任链模式来处理系统调用。不同的系统调用可能需要由不同的处理程序来处理,责任链模式可以根据系统调用的类型将请求传递给相应的处理程序来处理。
-
事件处理: 在图形用户界面(GUI)编程中,可以使用责任链模式来处理各种用户事件,例如鼠标点击事件、键盘按键事件等。每个事件可能需要由不同的处理程序来处理,责任链模式可以根据事件的类型将请求传递给相应的处理程序来处理。
-
请求过滤器: 在网络服务器中,可以使用责任链模式来处理请求过滤器。请求过滤器用于过滤和处理传入的请求,例如对请求进行身份验证、权限检查等。责任链模式可以根据请求的类型将请求传递给相应的过滤器来处理。
-
日志记录: 在日志记录系统中,可以使用责任链模式来处理日志记录请求。不同级别的日志记录可能需要由不同的处理程序来处理,责任链模式可以根据日志记录的级别将请求传递给相应的处理程序来处理。
示例
c
#include <stdio.h>
#include <stdlib.h>
// 职责链节点结构体
typedef struct Handler {
struct Handler* next; // 下一个处理节点
void (*handleRequest)(struct Handler* handler, int request); // 处理请求的函数指针
} Handler;
// 处理请求的具体实现函数
void handleRequestA(Handler* handler, int request) {
if (request <= 10) {
printf("请求 %d 由处理者 A 处理\n", request);
} else if (handler->next != NULL) {
handler->next->handleRequest(handler->next, request);
} else {
printf("请求 %d 无法处理\n", request);
}
}
void handleRequestB(Handler* handler, int request) {
if (request > 10 && request <= 20) {
printf("请求 %d 由处理者 B 处理\n", request);
} else if (handler->next != NULL) {
handler->next->handleRequest(handler->next, request);
} else {
printf("请求 %d 无法处理\n", request);
}
}
void handleRequestC(Handler* handler, int request) {
if (request > 20 && request <= 30) {
printf("请求 %d 由处理者 C 处理\n", request);
} else if (handler->next != NULL) {
handler->next->handleRequest(handler->next, request);
} else {
printf("请求 %d 无法处理\n", request);
}
}
int main() {
// 创建职责链节点
Handler* handlerA = (Handler*)malloc(sizeof(Handler));
Handler* handlerB = (Handler*)malloc(sizeof(Handler));
Handler* handlerC = (Handler*)malloc(sizeof(Handler));
// 设置处理请求的函数指针
handlerA->handleRequest = handleRequestA;
handlerB->handleRequest = handleRequestB;
handlerC->handleRequest = handleRequestC;
// 构建职责链
handlerA->next = handlerB;
handlerB->next = handlerC;
handlerC->next = NULL;
// 处理请求
int request1 = 5;
handlerA->handleRequest(handlerA, request1);
int request2 = 15;
handlerA->handleRequest(handlerA, request2);
int request3 = 25;
handlerA->handleRequest(handlerA, request3);
// 释放内存
free(handlerA);
free(handlerB);
free(handlerC);
return 0;
}
- 输出结果
bash
请求 5 由处理者 A 处理
请求 15 由处理者 B 处理
请求 25 由处理者 C 处理
状态模式
状态模式是一种行为型设计模式,用于在对象的内部状态发生变化时改变其行为。状态模式通过将对象的状态封装成独立的类,并将对象的行为委托给当前状态对象来实现状态转换和行为变化。
在状态模式中,一个对象可以同时存在多个状态,但在任何时刻只能处于其中一个状态。当对象的状态发生变化时,它会将当前状态委托给新的状态对象来处理。状态对象负责根据当前状态执行相应的行为,并可能触发状态转换。
在C语言中,状态模式通常通过函数指针和函数调用来实现。具体来说,每个状态都可以是一个函数,它负责执行特定状态下的行为。对象会将当前状态委托给相应的状态处理函数来处理行为。
-
环境(Context): 环境是包含状态的对象,它可以维护一个对当前状态对象的引用,并且在状态发生变化时将行为委托给当前状态对象来处理。
-
抽象状态(State): 抽象状态定义了一个接口,用于封装环境对象的一个特定状态对应的行为。
-
具体状态(Concrete State): 具体状态实现了抽象状态接口,负责处理环境对象的一个特定状态下的行为,并可能触发状态转换。
场景
-
有限状态机(FSM): 在嵌入式系统、游戏开发等领域,经常需要使用有限状态机来描述系统的状态和状态之间的转换。状态模式可以用于实现有限状态机,将每个状态封装成独立的状态对象,根据当前状态执行相应的行为,并触发状态转换。
-
网络通信协议: 在网络编程中,通常需要处理各种不同的通信状态,例如建立连接、发送数据、接收数据、关闭连接等。状态模式可以用于管理网络通信协议的状态,根据当前状态执行相应的操作,并根据通信过程中的状态变化来处理事件。
-
图形用户界面(GUI)编程: 在图形用户界面应用程序中,用户的操作会触发各种不同的界面状态,例如按钮点击、鼠标移动、键盘输入等。状态模式可以用于管理界面的状态,根据用户操作的不同来改变界面的行为和显示。
-
自动化控制系统: 在自动化控制系统中,通常需要根据各种传感器的输入来控制系统的行为,例如温度控制、压力控制等。状态模式可以用于管理系统的控制状态,根据传感器的输入来改变系统的行为和控制策略。
示例
c
#include <stdio.h>
#include <stdlib.h>
// 声明状态结构体
typedef struct State State;
// 状态处理函数指针类型
typedef void (*StateFunc)(State *);
// 定义状态结构体
struct State {
StateFunc handleRequest; // 处理请求的函数指针
};
// 具体状态1的处理函数
void state1_handleRequest(State *state) {
printf("状态1:处理请求\n");
}
// 具体状态2的处理函数
void state2_handleRequest(State *state) {
printf("状态2:处理请求\n");
}
// 具体状态3的处理函数
void state3_handleRequest(State *state) {
printf("状态3:处理请求\n");
}
int main() {
// 创建三种状态对象
State state1 = {state1_handleRequest};
State state2 = {state2_handleRequest};
State state3 = {state3_handleRequest};
// 设置状态转换关系
state1.handleRequest = state2_handleRequest;
state2.handleRequest = state3_handleRequest;
state3.handleRequest = state1_handleRequest;
// 模拟状态切换和请求处理
State *currentState = &state1; // 初始状态为状态1
for (int i = 0; i < 5; ++i) {
// 处理请求
currentState->handleRequest(currentState);
// 切换状态
currentState = (currentState->handleRequest == state1_handleRequest) ? &state2 :
(currentState->handleRequest == state2_handleRequest) ? &state3 : &state1;
}
return 0;
}
- 输出结果
bash
状态2:处理请求
状态1:处理请求
状态3:处理请求
状态2:处理请求
状态1:处理请求