Modo observador
1 escena de simulación
Supongamos que queremos desarrollar un altavoz inteligente. Hay muchos servicios en altavoces inteligentes, como: "servicio de pronóstico del tiempo", "servicio de programación", etc.
Todos estos servicios deben estar conectados al servidor en la nube, lo que significa que todos deben usar la dirección IP del altavoz inteligente. La dirección IP de los altavoces inteligentes se gestiona mediante un "servicio de gestión de IP" especial. Esto plantea una pregunta: cuando el usuario modifica la dirección IP del altavoz inteligente a través del "servicio de gestión de IP", ¿cómo saben el "servicio de pronóstico del tiempo" y el "servicio de programación" que la dirección IP ha cambiado?
Hay dos formas de resolver este problema:
La primera forma:
Desde "servicio de pronóstico del tiempo", "servicio de programación" hasta "servicio de gestión de IP" para consultar si la dirección IP ha cambiado.
El problema con este enfoque es: ¿Cuándo deberían el "servicio de pronóstico del tiempo" y el "servicio de programación" buscar direcciones IP? La frecuencia de consulta es baja y la dirección IP no se actualiza a tiempo, lo que puede provocar fallas comerciales. Pero no puede verificarlo una vez sin enviar un paquete, ¿verdad?
Por tanto, este camino básicamente no funciona.
Mira la segunda forma:
El "servicio de administración de IP" informa proactivamente al "servicio de pronóstico del tiempo" y al "servicio de programación" de la nueva dirección IP cuando la dirección IP cambia.
Este método resuelve el problema anterior, pero ¿cómo sabe el "servicio de gestión de IP" qué servicios notificar? Suponiendo que ahora se agrega un nuevo servicio que requiere una dirección IP, ¿es necesario modificar el "servicio de administración de IP"?
Además, la interfaz a través de la cual cada servicio recibe una dirección IP también puede ser diferente ¿Implementa el "servicio de gestión de IP" una interfaz para cada servicio?
El modo de observador puede resolver todos los problemas mencionados anteriormente.
Veamos cómo el modelo del observador resuelve estos problemas.
2 Introducción al modo de observador
La realización del modo observador se divide principalmente en dos enlaces: "registro" y "notificación":
Para registro":
El "Servicio de gestión de IP" proporciona una interfaz para registrar y eliminar servicios de observador. El "Servicio meteorológico", el "Servicio de programación" y otros servicios que requieren el uso de direcciones IP se registran con el "Servicio de administración de IP" según una interfaz unificada. Establecer la relación de dependencia entre "servicio de gestión de IP" y "servicio de pronóstico del tiempo", "servicio de programación" y otros servicios mediante el registro.
Esto resuelve el problema de cómo el "servicio de gestión de IP" sabe qué servicios notificar .
Al registrarse, el "servicio de pronóstico del tiempo", el "servicio de programación" y otros servicios proporcionan una función de devolución de llamada para informar al "servicio de gestión de IP" de la nueva dirección IP. La función de devolución de llamada se implementa de acuerdo con una interfaz unificada definida.
Este es el problema de que la interfaz de cada servicio que recibe la dirección IP puede ser diferente .
Si agrega un nuevo servicio que requiere el uso de una dirección IP, puede registrarse con el "Servicio de administración de IP" para proporcionar una interfaz de registro en cualquier momento. No hay impacto en la interfaz y la implementación del "Servicio de administración de IP".
Esto resuelve el problema de modificar el "servicio de gestión de IP" al agregar un nuevo servicio que requiere una dirección IP .
Para "notificaciones":
El "servicio de gestión de IP" solo llama a la función de devolución de llamada proporcionada durante el registro de servicios tales como "servicio de pronóstico del tiempo" y "servicio de programación" cuando la dirección IP cambia para informar la nueva dirección IP.
Esto resuelve el problema de que el "servicio de pronóstico del tiempo" y el "servicio de programación" no saben cuándo consultar la dirección IP .
Debido a los dos enlaces de "registro" y "notificación", el modelo de observador también se denomina "modelo de publicación-suscripción". "Publicar" corresponde a "Notificación" y "Suscripción" corresponde a "Registro".
3 Implementación detallada utilizando el patrón de observador
Partícipe
- Asunto: IpSubject
Objetivo de observación abstracto, que proporciona una interfaz para registrar y eliminar observadores
- ConcreteSubject: IpMngService
El objetivo de observación específico es el "servicio de gestión de IP" en el ejemplo anterior
- Observador: IpObserver
El observador abstracto define la interfaz de la función de devolución de llamada para el "servicio de gestión de IP" para informar la dirección IP.
- ConcreteObserver: WeatherReportService, ScheduleService
Observadores específicos, implementan la función de devolución de llamada que informa la dirección IP
UML
Código de muestra de 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;
}
Código de muestra de 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;
}
Código de muestra de IpObserver
ip_observer.h
#ifndef IP_OBSERVER_H
#define IP_OBSERVER_H
struct IpObserver {
void (*UpdateIp)(struct IpObserver *this, char *ip);
};
#endif
Código de muestra de 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;
}
Código de muestra de 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;
}
Ejemplo de código de cliente
#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");
}
Ejemplo de visualización del cliente
-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