观察者模式
1 模拟场景
假设我们要开发一个智能音箱。智能音箱上有很多服务,比如:“天气预报服务”、“日程安排服务”,等等。
这些服务都需要和云端服务器连接,也就是说都需要使用智能音箱的IP地址。而智能音箱的IP地址是有专门的“IP管理服务”管理的。这就产生了一个问题:当用户通过“IP管理服务”修改了智能音箱的IP地址后,“天气预报服务”、“日程安排服务”怎么知道IP地址变化了呢?
解决这个问题无非两条路:
第一条路:
由“天气预报服务”、“日程安排服务”向“IP管理服务”查询IP地址有没有变化。
这种做法的问题是:“天气预报服务”、“日程安排服务”该在什么时候查询IP地址呢?查询频率低了,IP地址更新不及时,可能导致业务失败。但也不能没发一个包就查询一次吧?
所以,这条路基本行不通。
再看第二条路:
“IP管理服务”在IP地址变化时主动将新的IP地址告知给“天气预报服务”、“日程安排服务”。
这个方法解决了前面的问题,但 “IP管理服务”怎么知道要告知给哪些服务呢?假设现在又新增一个需要使用IP地址的服务,难道需要修改“IP管理服务”?
另外,每个服务接收IP地址的接口也可能不同,难道“IP管理服务”要为每个服务实现一条接口?
观察者模式可以解决上面提到的所有问题。
我们来看下观察者模式是怎么解决这些问题的。
2 观察者模式简介
观察者模式的实现主要分为“注册”和“通知”两个环节:
对于“注册”:
“IP管理服务”提供注册和删除观察者服务的接口。“天气预报服务”、“日程安排服务”及其它需要使用IP地址的服务都按统一的接口向“IP管理服务”进行注册。通过注册建立起“IP管理服务”和“天气预报服务”、“日程安排服务”等服务之间的依赖关系。
这就解决了“IP管理服务”怎么知道要告知给哪些服务的问题。
注册时,“天气预报服务”、“日程安排服务”等服务提供一个告知新的IP地址的回调函数给“IP管理服务”。该回调函数按定义好的统一的接口实现。
这就每个服务接收IP地址的接口可能不同的问题。
如果新增一个需要使用IP地址的服务,随时都可以“IP管理服务”提供注册接口进行注册。对“IP管理服务”的接口和实现都没有任何影响。
这就解决了新增一个需要使用IP地址的服务需要修改“IP管理服务”的问题。
对于“通知”:
“IP管理服务”仅在IP地址变化时调用“天气预报服务”、“日程安排服务”等服务注册时提供的回调函数告知新的IP地址。
这就解决了“天气预报服务”、“日程安排服务”不知道该在什么时候查询IP地址的问题。
因为“注册”和“通知”两个环节,所以观察者模式也被称作“发布-订阅模式”。“发布”对应“通知”,“订阅”对应“注册”。
3 使用观察者模式的详细实现
参与者
- Subject: IpSubject
抽象观察目标,提供注册和删除观察者的接口
- ConcreteSubject: IpMngService
具体观察目标,也就是上述例子中的“IP管理服务”
- Observer: IpObserver
抽象观察者,定义“IP管理服务”告知IP地址的回调函数接口。
- ConcreteObserver: WeatherReportService, ScheduleService
具体观察者,实现告知IP地址的回调函数
UML
IpSubject示例代码
ip_subject.h
#ifndef IP_SUBJECT_H
#define IP_SUBJECT_H
#include "ip_observer.h"
struct IpSubject {
struct IpObserver *ipObserverList[50];
void (*Attach)(struct IpSubject *this, struct IpObserver *ipObserver);
void (*Detach)(struct IpSubject *this, struct IpObserver *ipObserver);
void (*Notify)(struct IpSubject *this, char *ip);
};
// 构造函数
void IpSubject(struct IpSubject *this);
// 析构函数
void _IpSubject(struct IpSubject *this);
#endif
ip_subject.c
#include "ip_subject.h"
#include <stdio.h>
static void Attach(struct IpSubject *this, struct IpObserver *ipObserver)
{
int i;
for (i = 0; i < sizeof(this->ipObserverList) / sizeof(struct IpObserver *); i++) {
if (this->ipObserverList[i] == NULL) {
this->ipObserverList[i] = ipObserver;
return;
}
}
printf("err: list full\n");
}
static void Detach(struct IpSubject *this, struct IpObserver *ipObserver)
{
int i;
for (i = 0; i < sizeof(this->ipObserverList) / sizeof(struct IpObserver *); i++) {
if (this->ipObserverList[i] == ipObserver) {
this->ipObserverList[i] = NULL;
}
}
}
static void Notify(struct IpSubject *this, char *ip)
{
int i;
for (i = 0; i < sizeof(this->ipObserverList) / sizeof(struct IpObserver *); i++) {
if (this->ipObserverList[i] != NULL) {
this->ipObserverList[i]->UpdateIp(this->ipObserverList[i], ip);
}
}
}
// 构造函数
void IpSubject(struct IpSubject *this)
{
int i;
for (i = 0; i < sizeof(this->ipObserverList) / sizeof(struct IpObserver *); i++) {
this->ipObserverList[i] = NULL;
}
this->Attach = Attach;
this->Detach = Detach;
this->Notify = Notify;
}
// 析构函数
void _IpSubject(struct IpSubject *this)
{
int i;
for (i = 0; i < sizeof(this->ipObserverList) / sizeof(struct IpObserver *); i++) {
this->ipObserverList[i] = NULL;
}
this->Attach = NULL;
this->Detach = NULL;
this->Notify = NULL;
}
IpMngService示例代码
ip_mng_service.h
#ifndef IP_MNG_SERVICE_H
#define IP_MNG_SERVICE_H
#include "ip_subject.h"
struct IpMngService {
struct IpSubject parent;
char ip[20];
void (*SetIp)(struct IpMngService *this, char * ip);
void (*GetIp)(struct IpMngService *this, char *ip);
};
// 构造函数
void IpMngService(struct IpMngService *this);
// 析构函数
void _IpMngService(struct IpMngService *this);
#endif
ip_mng_service.c
#include "ip_mng_service.h"
#include <string.h>
#include <stdio.h>
static void SetIp(struct IpMngService *this, char *ip)
{
strcpy(this->ip, ip);
printf(" IP地址修改为:%s\n", this->ip);
this->parent.Notify(&(this->parent), ip);
}
static void GetIp(struct IpMngService *this, char *ip)
{
strcpy(ip, this->ip);
}
// 构造函数
void IpMngService(struct IpMngService *this)
{
IpSubject(&(this->parent));
memset(this->ip, 0, sizeof(this->ip));
this->SetIp = SetIp;
this->GetIp = GetIp;
}
// 析构函数
void _IpMngService(struct IpMngService *this)
{
_IpSubject(&(this->parent));
memset(this->ip, 0, sizeof(this->ip));
this->SetIp = NULL;
this->GetIp = NULL;
}
IpObserver示例代码
ip_observer.h
#ifndef IP_OBSERVER_H
#define IP_OBSERVER_H
struct IpObserver {
void (*UpdateIp)(struct IpObserver *this, char *ip);
};
#endif
WeatherReportService示例代码
weather_report_service.h
#ifndef WEATHER_REPORT_SERVICE_H
#define WEATHER_REPORT_SERVICE_H
#include "ip_observer.h"
struct WeatherReportService {
struct IpObserver parent;
char ip[20];
};
// 构造函数
void WeatherReportService(struct WeatherReportService *this);
// 析构函数
void _WeatherReportService(struct WeatherReportService *this);
#endif
weather_report_service.c
#include "weather_report_service.h"
#include <stdio.h>
#include <string.h>
static void UpdateIp(struct WeatherReportService *this, char *ip)
{
strcpy(this->ip, ip);
printf(" “天气预报服务”的IP地址已更新为:%s\n", this->ip);
}
// 构造函数
void WeatherReportService(struct WeatherReportService *this)
{
memset(this->ip, 0, sizeof(this->ip));
this->parent.UpdateIp = (void(*)(struct IpObserver*, char*))UpdateIp;
}
// 析构函数
void _WeatherReportService(struct WeatherReportService *this)
{
memset(this->ip, 0, sizeof(this->ip));
this->parent.UpdateIp = NULL;
}
ScheduleService示例代码
schedule_service.h
#ifndef SCHEDULE_SERVICE_H
#define SCHEDULE_SERVICE_H
#include "ip_observer.h"
struct ScheduleService {
struct IpObserver parent;
char ip[20];
};
// 构造函数
void ScheduleService(struct ScheduleService *this);
// 析构函数
void _ScheduleService(struct ScheduleService *this);
#endif
schedule_service.c
#include "schedule_service.h"
#include <stdio.h>
#include <string.h>
static void UpdateIp(struct ScheduleService *this, char *ip)
{
strcpy(this->ip, ip);
printf(" “日程管理服务”的IP地址已更新为:%s\n", this->ip);
}
// 构造函数
void ScheduleService(struct ScheduleService *this)
{
memset(this->ip, 0, sizeof(this->ip));
this->parent.UpdateIp = (void(*)(struct IpObserver*, char*))UpdateIp;
}
// 析构函数
void _ScheduleService(struct ScheduleService *this)
{
memset(this->ip, 0, sizeof(this->ip));
this->parent.UpdateIp = NULL;
}
客户端代码示例
#include "ip_mng_service.h"
#include "weather_report_service.h"
#include "schedule_service.h"
#include <stdio.h>
void main()
{
struct IpMngService ipMngService;
IpMngService(&ipMngService);
struct WeatherReportService weatherReportService;
WeatherReportService(&weatherReportService);
struct ScheduleService scheduleServiec;
ScheduleService(&scheduleServiec);
printf("仅将“天气预报服务”注册为观察者,效果如下:\n");
ipMngService.parent.Attach(&(ipMngService.parent), (struct IpObserver*)&weatherReportService);
ipMngService.SetIp(&ipMngService, "10.78.100.111");
printf("\n");
printf("再将“日程管理服务”也注册为观察者,效果如下:\n");
ipMngService.parent.Attach(&(ipMngService.parent), (struct IpObserver*)&scheduleServiec);
ipMngService.SetIp(&ipMngService, "10.78.100.222");
}
客户端显示示例
-bash-4.2# ./test
仅将“天气预报服务”注册为观察者,效果如下:
IP地址修改为:10.78.100.111
“天气预报服务”的IP地址已更新为:10.78.100.111
再将“日程管理服务”也注册为观察者,效果如下:
IP地址修改为:10.78.100.222
“天气预报服务”的IP地址已更新为:10.78.100.222
“日程管理服务”的IP地址已更新为:10.78.100.222