C 语言设计模式(行为型)

文章目录

  • 策略模式
    • 场景
    • 示例
  • 迭代器模式
    • 场景
    • 示例
  • 访问者模式
    • 场景
    • 示例
  • 观察者模式
    • 场景
    • 示例
  • 命令模式
    • 场景
    • 示例
  • 模板方法模式
    • 场景
    • 示例
  • 事件驱动模式
    • 场景
    • 示例
  • 责任链模式
    • 场景
    • 示例
  • 状态模式
    • 场景
    • 示例

策略模式

策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法,并将每个算法封装成独立的对象,使得它们可以互相替换。策略模式使得算法的变化独立于使用算法的客户端,从而提高了系统的灵活性和可维护性。

  1. Context(上下文):上下文是使用算法的客户端对象,它包含一个指向策略对象的引用,并在需要时调用策略对象的算法。上下文对象通常会将请求委托给策略对象来执行。

  2. Strategy(策略):策略是一个接口或者抽象类,它定义了一个算法族,并将每个算法封装成一个具体的策略对象。所有具体策略对象都实现了相同的接口或者抽象类。

  3. ConcreteStrategy(具体策略):具体策略是策略接口的实现类,它实现了具体的算法逻辑。当客户端需要使用某种算法时,可以将相应的具体策略对象传递给上下文对象,从而使得上下文对象可以调用该算法。

场景

  1. 排序算法:在排序算法中,可能需要根据数据的不同特征选择不同的排序算法,比如快速排序、归并排序或者插入排序。可以将每种排序算法封装成一个策略,然后根据需要在运行时选择使用哪种策略。

  2. 数据压缩:在数据压缩领域,可能存在多种压缩算法,如LZW、Huffman等。通过使用策略模式,可以根据不同的压缩需求选择不同的算法。

  3. 图像处理:在图像处理中,可能需要根据图像的特征选择不同的处理算法,比如模糊、锐化、边缘检测等。通过使用策略模式,可以将每种处理算法封装成一个策略,然后根据需要选择不同的策略。

  4. 网络协议:在网络编程中,可能需要根据不同的网络协议选择不同的处理方式,比如TCP、UDP等。通过使用策略模式,可以将每种协议的处理方式封装成一个策略,然后根据需要选择不同的策略。

示例

#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;
}
  • 输出结果
使用信用卡支付 100.00 元,订单号:123456789
使用支付宝支付 100.00 元,订单号:123456789
使用微信支付 100.00 元,订单号:123456789

迭代器模式

迭代器模式(Iterator Pattern)是一种行为型设计模式,它提供一种方法来顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。通过使用迭代器模式,可以在不了解聚合对象内部结构的情况下,对聚合对象中的元素进行遍历操作。

  1. Iterator(迭代器):迭代器是一个接口或者抽象类,它定义了访问和遍历聚合对象中各个元素的方法。具体的迭代器实现类负责实现这些方法,以实现对不同类型聚合对象的遍历操作。

  2. ConcreteIterator(具体迭代器):具体迭代器是迭代器接口的实现类,它实现了对具体聚合对象的遍历操作。具体迭代器对象会跟踪遍历过程中的当前位置,并提供访问和遍历下一个元素的方法。

  3. Aggregate(聚合对象):聚合对象是一个接口或者抽象类,它定义了创建迭代器对象的方法。具体的聚合对象实现类负责实现这些方法,并提供迭代器对象用于遍历聚合对象中的元素。

  4. ConcreteAggregate(具体聚合对象):具体聚合对象是聚合对象接口的实现类,它实现了创建具体迭代器对象的方法,并提供了访问聚合对象中各个元素的方法。

场景

  1. 数据结构遍历:迭代器模式最典型的应用场景就是对数据结构的遍历,比如链表、数组、树等。通过将遍历算法封装在迭代器中,可以将数据结构的具体实现和遍历逻辑分离开来,使得遍历过程更加灵活。

  2. 文件处理:在文件处理中,可能需要逐行读取文件内容或者按照一定规则遍历文件中的数据。使用迭代器模式可以将文件读取的逻辑抽象为迭代器,从而可以在不同的文件类型或者数据结构上使用相同的遍历算法。

  3. 内存管理:在操作内存数据结构时,比如堆、栈等,可能需要遍历内存中的数据或者对内存中的数据进行操作。通过使用迭代器模式,可以将对内存数据结构的遍历和操作与具体的数据结构分离开来,提高代码的灵活性和可维护性。

  4. 数据库查询:在数据库操作中,可能需要对查询结果进行遍历和处理。使用迭代器模式可以将查询结果封装为迭代器,从而可以在不同的数据库查询结果上使用相同的遍历算法。

  5. 图形界面编程:在图形界面编程中,可能需要遍历界面中的控件或者元素。使用迭代器模式可以将界面中的控件或者元素抽象为迭代器,从而可以在不同的界面布局上使用相同的遍历算法。

示例

#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;
}
  • 输出结果
1 2 3 4 5 

访问者模式

访问者模式(Visitor Pattern)是一种行为型设计模式,它可以在不修改对象结构的情况下,定义新的操作或算法并应用于对象的元素。该模式使得可以在不改变元素类的前提下,增加新的操作方式。

  1. Visitor(访问者):访问者是一个接口或者抽象类,它定义了对每个元素对象进行操作的方法,这些方法的参数类型决定了哪些对象可以被访问以及如何访问它们。

  2. ConcreteVisitor(具体访问者):具体访问者是Visitor接口的实现类,它实现了对元素对象的具体操作逻辑。

  3. Element(元素):元素是一个接口或者抽象类,它定义了一个accept方法,该方法接受访问者对象作为参数,以便让访问者对象访问它。

  4. ConcreteElement(具体元素):具体元素是Element接口的实现类,它实现了accept方法,并在其中调用访问者对象的相应方法。

  5. ObjectStructure(对象结构):对象结构是一个集合,它用于存储元素对象,并提供一个接受访问者对象的方法,以便访问者可以访问其中的元素对象。

场景

  1. 抽象语法树(AST)的遍历和操作:在编译器开发中,抽象语法树(AST)用于表示源代码的结构,而访问者模式可以用于遍历和操作这种复杂的数据结构。通过定义不同的访问者,可以实现不同的操作,比如语法检查、代码生成等。

  2. 文件解析和处理:在文件解析和处理过程中,可能需要根据文件的不同类型执行不同的操作,比如解析XML文件、JSON文件等。访问者模式可以将对文件的操作抽象为访问者,从而可以在不同类型的文件上应用相同的操作。

  3. 数据结构的序列化和反序列化:在将数据结构序列化为字节流或者反序列化为数据结构时,可能需要对数据结构的不同部分进行不同的处理。访问者模式可以将序列化和反序列化的操作抽象为访问者,从而可以在不同的数据结构上应用相同的操作。

  4. 图形界面的事件处理:在图形界面编程中,可能需要对不同的界面元素执行不同的操作,比如按钮点击事件、鼠标移动事件等。访问者模式可以将事件处理逻辑抽象为访问者,从而可以在不同的界面元素上应用相同的事件处理逻辑。

  5. 数据库查询和操作:在数据库编程中,可能需要对不同的数据库表执行不同的操作,比如查询数据、更新数据等。访问者模式可以将对数据库表的操作抽象为访问者,从而可以在不同的数据库表上应用相同的操作。

示例

#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;
}
  • 输出结果
圆形面积:78.500000
矩形面积:24.000000

观察者模式

观察者模式(Observer Pattern)是一种行为型设计模式,用于定义对象之间的一对多依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都会得到通知并自动更新。这种模式也被称为发布-订阅模式。

  1. Subject(主题):主题是被观察的对象,它包含了一组观察者对象,并提供了方法来注册、删除和通知观察者。当主题的状态发生变化时,它会通知所有注册的观察者。

  2. Observer(观察者):观察者是订阅主题的对象,它定义了一个接口,当接收到主题的通知时,可以执行相应的操作。每个观察者都必须实现这个接口,以便主题能够调用它们的更新方法。

场景

  1. 事件处理和消息传递系统:在事件驱动的系统中,观察者模式非常有用。当一个事件发生时,所有注册的观察者都会收到通知并执行相应的操作。这种模式在图形用户界面(GUI)编程中尤其常见,例如,当用户点击按钮时,所有注册的事件处理程序都会收到通知并执行相应的操作。

  2. 发布-订阅模式:观察者模式的一种变体是发布-订阅模式。在这种模式中,发布者(也称为主题或事件总线)负责向所有订阅者(观察者)发布消息或事件。这种模式在消息队列系统、即时通讯应用程序等领域被广泛应用。

  3. 状态变化通知:当一个对象的状态发生变化时,它可能需要通知其他对象以执行相应的操作。例如,在游戏开发中,一个玩家对象的状态变化(比如生命值减少)可能需要通知其他玩家或者游戏引擎来执行相应的动作。

  4. 数据变更通知:在软件开发中,当一个数据模型的状态发生变化时,可能需要通知所有依赖该数据模型的视图或者其他组件进行更新。观察者模式可以用于实现这种数据变更通知机制。

  5. 日志记录和错误处理:在应用程序中,观察者模式可以用于实现日志记录和错误处理机制。当发生错误或者异常时,可以通知所有注册的日志记录器进行记录,并且通知所有注册的错误处理程序进行处理。

示例

#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;
}
  • 输出结果
观察者A收到通知,执行更新操作
观察者B收到通知,执行更新操作

命令模式

命令模式(Command Pattern)是一种行为型设计模式,它用于将请求或操作封装为独立的对象,从而使得请求的发送者和接收者之间解耦。在命令模式中,请求被封装成一个命令对象,请求的发送者(客户端)只需创建命令对象并将其发送给接收者(调用者),而无需了解命令的具体实现方式。

  1. Command(命令):命令是一个接口或者抽象类,它定义了执行操作的方法。具体的命令对象实现了这个接口,并负责实现具体的操作逻辑。

  2. ConcreteCommand(具体命令):具体命令是命令接口的实现类,它封装了请求的接收者和执行操作的具体逻辑。

  3. Invoker(调用者):调用者是负责调用命令对象的对象,它只负责向命令对象发送请求,而无需了解命令的具体实现方式。

  4. Receiver(接收者):接收者是命令的实际执行者,它负责执行具体的操作。命令对象将请求委托给接收者来执行。

场景

  1. 图形用户界面(GUI)编程:在图形用户界面中,用户的操作(比如点击按钮、菜单项等)需要触发相应的操作。命令模式可以将这些操作封装为命令对象,并将其发送给对应的接收者(控件或者界面元素)来执行。

  2. 多线程任务调度:在多线程编程中,可能需要将一些操作封装成任务,并将这些任务交给线程池来执行。命令模式可以将任务封装为命令对象,并将其发送给线程池来执行。

  3. 日志记录:在软件开发中,可能需要记录系统的操作日志。命令模式可以将记录日志的操作封装为命令对象,并将其发送给日志记录器来执行。

  4. 设备控制:在嵌入式系统或者硬件控制领域,可能需要控制一些设备或者执行一些特定的操作。命令模式可以将这些操作封装为命令对象,并将其发送给设备控制器来执行。

  5. 文本编辑器的撤销/重做功能:在文本编辑器中,用户的操作(比如添加文本、删除文本等)可能需要支持撤销和重做功能。命令模式可以将用户的操作封装为命令对象,并将其保存在命令历史记录中,从而支持撤销和重做操作。

示例

#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;
}
  • 输出结果
添加文本:Hello, World!
删除文本:Hello, World!
撤销删除文本:Hello, World!

模板方法模式

在C语言中,没有像其他面向对象语言那样提供原生的类和继承机制。因此,在C语言中实现传统意义上的模板方法模式会有一定挑战。然而,我们可以使用函数指针和回调函数来实现类似的行为。

模板方法模式是一种行为型设计模式,它定义了一个算法的框架,并将其中的一些步骤延迟到子类中实现。模板方法模式通过在父类中定义算法的骨架,并在子类中实现具体的步骤,以达到在运行时选择不同的行为的目的。

场景

  1. 算法的骨架已经确定,但具体步骤可能变化: 如果有一个算法的骨架已经确定,但其中的某些具体步骤可能会因为应用场景或者其他条件而有所变化,这时可以使用模板方法模式。通过在父类中定义算法的骨架,子类可以根据需要重写某些步骤,从而实现不同的行为。

  2. 避免代码重复: 如果有多个算法有相似的流程或步骤,但具体实现略有不同,这时可以将这些共同的部分提取出来,放在父类中作为模板方法,然后子类只需要实现各自特定的步骤,从而避免了代码的重复。

  3. 实现开闭原则: 模板方法模式可以使得算法的框架稳定不变,而具体的步骤可以灵活变化,从而符合开闭原则,即对扩展开放,对修改关闭。

示例

#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;
}
  • 输出结果
开始执行模板方法
子类1:步骤 1
子类1:步骤 2
子类1:步骤 3
结束执行模板方法
开始执行模板方法
子类2:步骤 1
子类2:步骤 2
子类2:步骤 3
结束执行模板方法

事件驱动模式

事件驱动模式是一种软件设计模式,它基于事件和事件处理机制。在事件驱动模式中,程序的执行流程是由外部事件的发生和相应的事件处理程序的执行来驱动的。

  1. 事件(Event):事件是程序运行过程中发生的某种事情或者状态变化,可以是用户的输入、系统的消息、定时器的触发等。事件可以分为不同的类型,每种类型的事件都有相应的处理程序。

  2. 事件处理程序(Event Handler):事件处理程序是针对特定类型的事件而设计的函数或者方法。当特定类型的事件发生时,相应的事件处理程序会被调用来处理事件,执行相应的逻辑。

  3. 事件循环(Event Loop):事件循环是一个在程序中持续运行的循环,它负责等待事件的发生,并将事件分派给相应的事件处理程序。事件循环不断地从事件队列中获取事件,然后根据事件的类型找到相应的事件处理程序来处理事件。

  4. 回调函数(Callback Function):回调函数是一种特殊的函数,它作为参数传递给其他函数,在特定的事件发生时被调用。在事件驱动模式中,回调函数通常用于实现事件处理程序,当特定类型的事件发生时,相应的回调函数会被调用来处理事件。

事件驱动模式的核心思想是将程序的控制权交给事件循环,由事件循环负责等待事件的发生,并将事件分派给相应的事件处理程序来处理。这种方式使得程序能够异步地响应外部事件,提高了程序的并发性和响应速度。

场景

  1. 图形用户界面(GUI)编程: 在图形用户界面应用程序中,用户的操作(例如点击按钮、输入文本等)会触发各种事件,程序需要根据这些事件来执行相应的操作。事件驱动模式可以用于处理用户交互事件,例如按钮点击事件、鼠标移动事件等。

  2. 网络编程: 在网络编程中,网络事件(例如连接建立、数据到达、连接关闭等)会触发相应的事件处理程序,用于处理这些事件并执行相应的操作。事件驱动模式可以用于实现异步的网络通信,提高程序的并发性和响应速度。

  3. 多线程编程: 在多线程编程中,线程的运行是由事件和信号来驱动的。例如,一个线程可以等待某个条件变量的信号,一旦条件变量发生变化,就会触发相应的事件处理程序来执行相应的操作。事件驱动模式可以用于实现多线程编程中的事件驱动模型,提高程序的并发性和可维护性。

  4. 系统级编程: 在系统级编程中,操作系统会产生各种事件,例如定时器事件、硬件中断事件等。程序需要根据这些事件来执行相应的操作,以实现系统的各种功能。事件驱动模式可以用于处理系统级事件,实现系统级功能。

示例

#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;
}
  • 输出结果
处理按钮点击事件,按钮编号:1
处理按键按下事件,键值:65

责任链模式

责任链模式是一种行为型设计模式,用于组织处理对象的责任链。在责任链模式中,多个对象依次处理请求,直到其中一个对象能够处理该请求为止。每个处理对象都持有对下一个处理对象的引用,形成一个链条。请求沿着这条链条依次传递,直到被处理。

  1. 抽象处理者(Handler): 定义了一个处理请求的接口,通常包含一个处理请求的方法。可以有多个具体处理者实现这个接口。

  2. 具体处理者(Concrete Handler): 实现了抽象处理者接口,负责处理特定类型的请求。如果自己无法处理请求,则将请求传递给下一个处理者。

  3. 客户端(Client): 创建责任链,并向责任链中的第一个处理者发送请求。

场景

  1. 系统调用处理: 在操作系统中,可以使用责任链模式来处理系统调用。不同的系统调用可能需要由不同的处理程序来处理,责任链模式可以根据系统调用的类型将请求传递给相应的处理程序来处理。

  2. 事件处理: 在图形用户界面(GUI)编程中,可以使用责任链模式来处理各种用户事件,例如鼠标点击事件、键盘按键事件等。每个事件可能需要由不同的处理程序来处理,责任链模式可以根据事件的类型将请求传递给相应的处理程序来处理。

  3. 请求过滤器: 在网络服务器中,可以使用责任链模式来处理请求过滤器。请求过滤器用于过滤和处理传入的请求,例如对请求进行身份验证、权限检查等。责任链模式可以根据请求的类型将请求传递给相应的过滤器来处理。

  4. 日志记录: 在日志记录系统中,可以使用责任链模式来处理日志记录请求。不同级别的日志记录可能需要由不同的处理程序来处理,责任链模式可以根据日志记录的级别将请求传递给相应的处理程序来处理。

示例

#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;
}
  • 输出结果
请求 5 由处理者 A 处理
请求 15 由处理者 B 处理
请求 25 由处理者 C 处理

状态模式

状态模式是一种行为型设计模式,用于在对象的内部状态发生变化时改变其行为。状态模式通过将对象的状态封装成独立的类,并将对象的行为委托给当前状态对象来实现状态转换和行为变化。

在状态模式中,一个对象可以同时存在多个状态,但在任何时刻只能处于其中一个状态。当对象的状态发生变化时,它会将当前状态委托给新的状态对象来处理。状态对象负责根据当前状态执行相应的行为,并可能触发状态转换。

在C语言中,状态模式通常通过函数指针和函数调用来实现。具体来说,每个状态都可以是一个函数,它负责执行特定状态下的行为。对象会将当前状态委托给相应的状态处理函数来处理行为。

  1. 环境(Context): 环境是包含状态的对象,它可以维护一个对当前状态对象的引用,并且在状态发生变化时将行为委托给当前状态对象来处理。

  2. 抽象状态(State): 抽象状态定义了一个接口,用于封装环境对象的一个特定状态对应的行为。

  3. 具体状态(Concrete State): 具体状态实现了抽象状态接口,负责处理环境对象的一个特定状态下的行为,并可能触发状态转换。

场景

  1. 有限状态机(FSM): 在嵌入式系统、游戏开发等领域,经常需要使用有限状态机来描述系统的状态和状态之间的转换。状态模式可以用于实现有限状态机,将每个状态封装成独立的状态对象,根据当前状态执行相应的行为,并触发状态转换。

  2. 网络通信协议: 在网络编程中,通常需要处理各种不同的通信状态,例如建立连接、发送数据、接收数据、关闭连接等。状态模式可以用于管理网络通信协议的状态,根据当前状态执行相应的操作,并根据通信过程中的状态变化来处理事件。

  3. 图形用户界面(GUI)编程: 在图形用户界面应用程序中,用户的操作会触发各种不同的界面状态,例如按钮点击、鼠标移动、键盘输入等。状态模式可以用于管理界面的状态,根据用户操作的不同来改变界面的行为和显示。

  4. 自动化控制系统: 在自动化控制系统中,通常需要根据各种传感器的输入来控制系统的行为,例如温度控制、压力控制等。状态模式可以用于管理系统的控制状态,根据传感器的输入来改变系统的行为和控制策略。

示例

#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; // 初始状态为状态1for (int i = 0; i < 5; ++i) {// 处理请求currentState->handleRequest(currentState);// 切换状态currentState = (currentState->handleRequest == state1_handleRequest) ? &state2 :(currentState->handleRequest == state2_handleRequest) ? &state3 : &state1;}return 0;
}
  • 输出结果
状态2:处理请求
状态1:处理请求
状态3:处理请求
状态2:处理请求
状态1:处理请求

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/16917.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

银行为什么要对网点开展神秘顾客检测项目?

银行业面临的形势复杂多变&#xff0c;包括技术创新、客户行为变化、竞争加剧、监管环境变化、全球化与本地化平衡、经济环境影响以及可持续发展和社会责任等多方面的挑战和机遇。银行需要通过种策略&#xff0c;积极应对这些变化&#xff0c;实现可持续发展。其中提升客户服务…

顺序表实现通讯录项目

目录 一.实现功能&#xff1a; 二.文件结构 三.代码实现 1.初始化 2.通讯录的销毁 3.通讯录添加数据 4.通讯录删除数据 5.通讯录的修改 6.展现通讯录数据 7.通讯录查找 四.代码 SeqList.h Contact.h Contact.c test(通讯录).c 一.实现功能&#xff1a; ⾄少能够存…

Samtec技术漫谈 | 电动自行车中的传感器和信号传输技术

【摘要/前言】 电动自行车&#xff0c;大家熟悉吗&#xff1f; 今天的话题似乎是可以唤起大家心底骑车的美好回忆&#xff0c;我们也曾骑车探索过大自然和社区&#xff0c;自行车也是我们曾经不可或缺的便捷交通工具。 怀旧思潮的影响&#xff0c;加持科技的进步&#xff0c…

php 使用phpoffice导出导出excel

荆轲刺秦王 在PHP中&#xff0c;可以使用 PhpSpreadsheet 库来创建和导出Excel文件。PhpSpreadsheet 是一个纯PHP 编写的组件库&#xff0c;它使用现代 PHP 写法&#xff0c;代码质量和性能比 PHPExcel 高不少&#xff0c;完全可以替代PHPExcel&#xff08;PHPExcel已不再维护…

【HDFS】FSImage加载过程之loadINode过程

普通的loadINode方法(即不是root inode): 根据inode的类型:文件、目录、链接,做不同的加载处理。 // 根据传入的PB INode的type做不同处理。// 我们下面关注FILE和DIRECTORY两种类型:private INode loadINode(INodeSection.INode n) {switch (n.getType()) {<

【云原生】Kubernetes中的List-Watch机制详解与容器生命周期

目录 引言 一、List-Watch机制概述 &#xff08;一&#xff09;基本概念 &#xff08;二&#xff09;工作机制 1.List操作 2.Watch操作 &#xff08;三&#xff09;数据流向 1.按模块划分 2.按整体总结 二、Pod生命周期 &#xff08;一&#xff09;生命周期 1.创建…

CMake-1 cmake简介及安装使用

文章目录 1. CMake 简单介绍2. CMake 安装使用 1. CMake 简单介绍 为什么需要CMake 写过C语言的都知道&#xff0c;C语言项目使用Makefile进行管理&#xff0c;而随着项目复杂度的增加 Makefile编写的难度也随之增加&#xff0c;而且在不同平台Makefile 语法规则是不一样的&am…

5款好用的AI写作软件,一键生成高质量文章

在当今信息快速发展的时代&#xff0c;AI写作软件逐渐成为创作者们的得力助手。它们能够凭借先进的技术和算法&#xff0c;一键生成高质量的文章&#xff0c;为创作者们节省大量的创作时间和精力。以下是5款备受好评的AI写作软件&#xff0c;下面在本文中分享给大家&#xff0c…

20240522金融读报:出口信用保险提效苏易融碳中和机票贷款差异化投放替代数据征信培育壮大数字经济

1、印发通知从响应速度、承保力度、承包评审要素、产业链范围、定制化、线上化、便利化等方面去充分发挥出口信用保险作用。&#xff08;这也可以作为这个贷款业务担保时的一个考虑项吧&#xff09; 2、苏易融&#xff1a;汇集江苏辖内特定客群信贷产品&#xff0c;可一站式查…

BitConverter类型,Byte数组与其他基本类型数据之间的转换

BitConvert对于byte数组转换为其他的基本变量很方便&#xff0c;是我们开发必须要学会的类型转换&#xff0c;因为我在使用中使用的比较多&#xff0c;创作不易&#xff0c;大家点赞关注收藏。 GetBytes(XX)将基本变量转换成字节数组&#xff0c;C#在数据存储在计算机中的方式…

kettle学习之表的输入输出

需求 把表A里的数据传送到表B中&#xff0c;在此之前&#xff0c;清空表B内的数据 表输入 执行SQL脚本 表输出

一文带你学会如何部署个人博客到云服务器,并进行域名备案与解析!

哈喽&#xff0c;大家好呀&#xff01;这里是码农后端。之前我给大家介绍了如何快速注册一个自己的域名&#xff0c;并创建一台自己的阿里云ECS云服务器。本篇将介绍如何将个人博客部署到云服务器&#xff0c;并进行域名备案与解析。 1、域名备案 注册了域名并购买了云服务器之…

探索自动化办公的新境界:批量操作与智能管理

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、自动化办公的必要性与价值 二、基础操作与自动化脚本 三、Python在自动化办公中的应用…

Meme币总市值突破630亿美元 以太坊ETF获批意味着代币化资产“完全安全”

近日&#xff0c;数字货币市场再次掀起轩然大波。一方面&#xff0c;Meme币总市值突破了630亿美元&#xff0c;令人瞠目结舌&#xff1b;另一方面&#xff0c;以太坊ETF的获批也引发了市场的广泛关注&#xff0c;被视为代币化资产的“完全安全”标志。 Meme币总市值飙升 Meme币…

深圳比创达电子EMC|EMC电磁兼容性行业:挑战与机遇并存

随着电子技术的迅猛发展&#xff0c;电磁兼容性&#xff08;EMC&#xff09;已成为各行各业不可忽视的关键问题。EMC是指设备或系统在其电磁环境中能正常工作且不对该环境中任何事物构成不能承受的电磁骚扰的能力。 一、EMC电磁兼容性行业的现状 EMC电磁兼容性行业作为电子技…

[数据集][目标检测]道路井盖下水道井盖开关闭和检测数据集VOC+YOLO格式407张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;407 标注数量(xml文件个数)&#xff1a;407 标注数量(txt文件个数)&#xff1a;407 标注类别…

构建php环境、安装、依赖、nginx配置、ab压力测试命令

目录 php简介 官网php安装包 选择下载稳定版本 &#xff08;建议使用此版本&#xff0c;文章以此版本为例&#xff09; 安装php解析环境 准备工作 安装依赖 zlib-devel 和 libxml2-devel包。 安装扩展工具库 安装 libmcrypt 安装 mhash 安装mcrypt 安装php 选项含…

深入理解一下栈

1、栈&#xff1a;数据结构 为什么 main()方法 最先执行&#xff0c;最后结束&#xff1f; 当然是因为 main()方法入栈啦。 2、栈&#xff1a;栈内存&#xff0c;主管程序的运行&#xff0c;生命周期和现成同步&#xff1b; 线程结束&#xff0c;栈内内存也就释放了&#xff0c…

STM32_RCC

1、RCC RCC即Reset and Clock Control&#xff0c;复位和时钟控制。通过stm32f10x结构图可以看出RCC控制着stm32的AHB系统总线&#xff0c;而AHB总线又桥接APB1和APB2&#xff0c;分别通过它们控制不同的片上外设。如果要使用某个片上外设的功能&#xff0c;必须先通过…

SpringBoot集成腾讯IM流程

1.application.yaml中添加IM配置信息 #im模块 im: identifier: admin sdkappid: 1400888888 key: ccf2dc88c1ca232cfabbd24906d5091ab81ba0250224abc 2.封装IM工具类 Component Getter RefreshScope public class ImAdminSignConfig {/*** 签名*/private String usersig;…