您现在的位置是:首页 >其他 >设计模式(四) —— 观察者模式/发布订阅模式,c和c++示例代码网站首页其他
设计模式(四) —— 观察者模式/发布订阅模式,c和c++示例代码
简介用c和c++代码来实现发布-订阅模式
往期地址:
本期主题:
使用c和c++代码,讲解观察者模式、发布订阅模式
1.什么是发布-订阅模式
- 发布-订阅模式是一种行为设计模式,它允许多个对象通过事件的发布和订阅来进行通信;
- 在这种模式中,发布者(又称为主题)负责发布事件,而订阅者(也称为观察者)则通过订阅主题来接收这些事件;
- 这种模式使得应用程序的不同部分能够松散耦合,并且可以动态地添加或删除订阅者;
2.实例
2.1 场景
A、B、C、D 4个人都订阅了报纸,所以希望当有新报纸来时,通知到这4个人,并且订阅的人可能后面会越来越多,也可能减少,需要有一个机制能够方便的添加、删除订阅的人。因此需求如下:
- 当有新报纸时,需要通知到所有订阅的人;
- 订阅的人可以方便的添加、修改、删除;
2.2 代码设计
设计思路:
发布者(也称为主题),负责:
- 发布事件,把消息推送到所有的订阅者
- 负责管理(增加、删除)订阅者
订阅者负责:
- 具体的发布消息的实现
示例代码:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
// 前向声明
class Publisher;
class Subscriber;
// 订阅者接口
class Subscriber {
public:
virtual void update(const string& message) = 0;
};
// 发布者类
class Publisher {
public:
// 添加订阅者
void subscribe(Subscriber* subscriber) {
subscribers_.push_back(subscriber);
}
// 删除订阅者
void unsubscribe(Subscriber* subscriber) {
for (auto it = subscribers_.begin(); it != subscribers_.end(); ++it) {
if (*it == subscriber) {
subscribers_.erase(it);
break;
}
}
}
// 发送消息给所有订阅者
void notify(const string& message) {
for (auto subscriber : subscribers_) {
subscriber->update(message);
}
}
private:
vector<Subscriber*> subscribers_;
};
// 订阅者1
class Subscriber1 : public Subscriber {
public:
virtual void update(const string& message) {
cout << "Subscriber1 received message: " << message << endl;
}
};
// 订阅者2
class Subscriber2 : public Subscriber {
public:
virtual void update(const string& message) {
cout << "Subscriber2 received message: " << message << endl;
}
};
int main() {
Publisher publisher;
Subscriber1 subscriber1;
Subscriber2 subscriber2;
// 添加订阅者
publisher.subscribe(&subscriber1);
publisher.subscribe(&subscriber2);
// 发送消息
publisher.notify("Hello, world!");
// 删除订阅者
publisher.unsubscribe(&subscriber1);
// 发送另一条消息
publisher.notify("Goodbye!");
return 0;
}
//测试结果:
$ ./a.out
Subscriber1 received message: Hello, world!
Subscriber2 received message: Hello, world!
Subscriber2 received message: Goodbye!
2.3 代码的优化
有些场景是我们并不想一有消息就通知所有的订阅者,我们希望在添加订阅者时,就知道这个订阅者需要订阅哪种消息
,这样无关的消息就不会推送给他 ,因此我们对代码进行修改,在发布者类中添加了一个Map,来进行事件的管理:
#include <iostream>
#include <vector>
#include <unordered_map>
#include <string>
using namespace std;
// 前向声明
class Publisher;
class Subscriber;
// 订阅者接口
class Subscriber {
public:
virtual void update(const string& topic, const string& message) = 0;
};
// 发布者类
class Publisher {
public:
// 添加订阅者
void subscribe(const string& topic, Subscriber* subscriber) {
subscribers_[topic].push_back(subscriber);
}
// 删除订阅者
void unsubscribe(const string& topic, Subscriber* subscriber) {
auto it = subscribers_.find(topic);
if (it != subscribers_.end()) {
auto& subscribers = it->second;
for (auto it2 = subscribers.begin(); it2 != subscribers.end(); ++it2) {
if (*it2 == subscriber) {
subscribers.erase(it2);
break;
}
}
}
}
// 发送消息给所有订阅者
void notify(const string& topic, const string& message) {
auto it = subscribers_.find(topic);
if (it != subscribers_.end()) {
auto& subscribers = it->second;
for (auto subscriber : subscribers) {
subscriber->update(topic, message);
}
}
}
private:
unordered_map<string, vector<Subscriber*>> subscribers_;
};
// 订阅者1
class Subscriber1 : public Subscriber {
public:
virtual void update(const string& topic, const string& message) {
cout << "Subscriber1 received message: " << message << " on topic: " << topic << endl;
}
};
// 订阅者2
class Subscriber2 : public Subscriber {
public:
virtual void update(const string& topic, const string& message) {
cout << "Subscriber2 received message: " << message << " on topic: " << topic << endl;
}
};
int main() {
Publisher publisher;
Subscriber1 subscriber1;
Subscriber2 subscriber2;
// 添加订阅者
publisher.subscribe("topic1", &subscriber1);
publisher.subscribe("topic2", &subscriber2);
// 发送消息
publisher.notify("topic1", "Hello, world!");
// 删除订阅者
publisher.unsubscribe("topic1", &subscriber1);
// 发送另一条消息
publisher.notify("topic1", "Goodbye!");
publisher.notify("topic2", "Hello, again!");
return 0;
}
//测试结果:
$ ./a.out
Subscriber1 received message: Hello, world! on topic: topic1
Subscriber2 received message: Hello, again! on topic: topic2
3.用C语言来实现
我们采用了链表来管理订阅者。
- 每个事件类型对应一个订阅者链表,每个链表节点包含了一个回调函数和指向下一个节点的指针;
- 在订阅和取消订阅时,我们需要遍历对应的链表,找到要添加或删除的节点,订阅时添加上对应的事件和回调处理;
- 在通知时,通知事件和具体的消息;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_EVENT_TYPE 10
#define MAX_EVENT_SIZE 100
typedef enum {
EVENT_TYPE_1,
EVENT_TYPE_2,
EVENT_TYPE_3,
EVENT_TYPE_4,
EVENT_TYPE_5,
EVENT_TYPE_6,
EVENT_TYPE_7,
EVENT_TYPE_8,
EVENT_TYPE_9,
EVENT_TYPE_10,
EVENT_TYPE_MAX
} EventType;
typedef struct {
char data[MAX_EVENT_SIZE];
} Event;
typedef struct SubscriberNode {
void (*callback)(Event *);
struct SubscriberNode *next;
} SubscriberNode;
typedef struct {
SubscriberNode *head;
SubscriberNode *tail;
} SubscriberList;
SubscriberList subscriber_list[EVENT_TYPE_MAX] = {{NULL, NULL}};
void publisher_subscribe(EventType event_type, void (*callback)(Event *)) {
SubscriberNode *node = (SubscriberNode *)malloc(sizeof(SubscriberNode));
node->callback = callback;
node->next = NULL;
SubscriberList *list = &subscriber_list[event_type];
if (!list->head) {
list->head = list->tail = node;
return;
}
list->tail->next = node;
list->tail = node;
}
void publisher_unsubscribe(EventType event_type, void (*callback)(Event *)) {
SubscriberList *list = &subscriber_list[event_type];
if (!list->head) {
return;
}
SubscriberNode *cur_node = list->head;
SubscriberNode *prev_node = NULL;
while (cur_node) {
if (cur_node->callback == callback) {
if (prev_node) {
prev_node->next = cur_node->next;
} else {
list->head = cur_node->next;
}
if (!cur_node->next) {
list->tail = prev_node;
}
free(cur_node);
return;
}
prev_node = cur_node;
cur_node = cur_node->next;
}
}
void publisher_notify(EventType event_type, Event *event) {
SubscriberList *list = &subscriber_list[event_type];
SubscriberNode *cur_node = list->head;
while (cur_node) {
cur_node->callback(event);
cur_node = cur_node->next;
}
}
void subscriber_callback1(Event *event) {
printf("Subscriber 1 received event: %s
", event->data);
}
void subscriber_callback2(Event *event) {
printf("Subscriber 2 received event: %s
", event->data);
}
int main() {
Event event1 = {"Event 1 data"};
Event event2 = {"Event 2 data"};
publisher_subscribe(EVENT_TYPE_1, subscriber_callback1);
publisher_subscribe(EVENT_TYPE_1, subscriber_callback2);
publisher_notify(EVENT_TYPE_1, &event1);
printf("
");
publisher_unsubscribe(EVENT_TYPE_1, subscriber_callback2);
publisher_notify(EVENT_TYPE_1, &event2);
return 0;
}
//测试结果:
$ ./a.out
Subscriber 1 received event: Event 1 data
Subscriber 2 received event: Event 1 data
Subscriber 1 received event: Event 2 data
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。