オブザーバーモード
1シミュレーションシーン
スマートスピーカーを開発したいとします。スマートスピーカーには、「天気予報サービス」、「スケジュールサービス」など、多くのサービスがあります。
これらのサービスはすべてクラウドサーバーに接続する必要があります。つまり、すべてのサービスでスマートスピーカーのIPアドレスを使用する必要があります。スマートスピーカーのIPアドレスは特別な「IP管理サービス」で管理されています。これにより、ユーザーが「IP管理サービス」を通じてスマートスピーカーのIPアドレスを変更すると、「天気予報サービス」と「スケジュールサービス」は、IPアドレスが変更されたことをどのようにして知るのでしょうか。
この問題を解決するには2つの方法があります。
最初の方法:
「天気予報サービス」「スケジュールサービス」から「IP管理サービス」へIPアドレスの変更の有無をお問い合わせください。
このアプローチの問題は、「天気予報サービス」と「スケジュールサービス」がIPアドレスを検索するのはいつですか。クエリの頻度が低く、IPアドレスが時間内に更新されないため、ビジネスが失敗する可能性があります。しかし、パッケージを送信せずに一度チェックすることはできませんよね?
したがって、この道路は基本的に機能しません。
2番目の方法を見てください。
「IP管理サービス」は、IPアドレスが変更になったときに、「天気予報サービス」や「スケジュールサービス」に新しいIPアドレスを積極的に通知します。
この方法は以前の問題を解決しますが、「IP管理サービス」はどのサービスに通知するかをどのようにして知るのですか?IPアドレスを必要とする新しいサービスが追加されたとすると、「IP管理サービス」を変更する必要がありますか?
また、各サービスがIPアドレスを受け取るためのインターフェースも異なる場合がありますが、「IP管理サービス」はサービスごとにインターフェースを実装していますか?
オブザーバーモードでは、上記のすべての問題を解決できます。
オブザーバーモデルがこれらの問題をどのように解決するかを見てみましょう。
2オブザーバーモードの概要
オブザーバーモードの実現は、主に「登録」と「通知」の2つのリンクに分かれています。
「登録」の場合:
「IP管理サービス」は、オブザーバーサービスを登録および削除するためのインターフェイスを提供します。「ウェザーサービス」「スケジュールサービス」など、IPアドレスを必要とするサービスは、統一されたインターフェースで「IP管理サービス」に登録されます。登録により、「IP管理サービス」と「天気予報サービス」、「スケジュールサービス」などの依存関係を構築します。
これにより、「IP管理サービス」が通知するサービスをどのように認識するかという問題が解決されます。
登録すると、「天気予報サービス」「スケジュールサービス」などのサービスが、「IP管理サービス」に新しいIPアドレスを通知するコールバック機能を提供します。コールバック関数は、定義された統一インターフェースに従って実装されます。
これは、IPアドレスを受け取る各サービスのインターフェースが異なる可能性があるという問題です。
IPアドレスの使用を必要とする新しいサービスを追加する場合、「IP管理サービス」に登録して、いつでも登録インターフェースを提供できます。「IP管理サービス」のインターフェースや実装への影響はありません。
これにより、IPアドレスを必要とする新しいサービスを追加するときに「IP管理サービス」を変更する問題が解決されます。
「通知」の場合:
「IP管理サービス」は、「天気予報サービス」「スケジュールサービス」などのサービス登録時に提供されるコールバック関数を呼び出すだけで、IPアドレスが変更されて新しいIPアドレスが通知されます。
これにより、「天気予報サービス」と「スケジュールサービス」がIPアドレスを照会するタイミングがわからないという問題が解決されます。
「登録」と「通知」の2つのリンクがあるため、オブザーバーモデルは「パブリッシュ/サブスクライブモデル」とも呼ばれます。「公開」は「通知」、「購読」は「登録」に対応します。
3オブザーバーパターンを使用した詳細な実装
参加者
- 件名:IpSubject
オブザーバーを登録および削除するためのインターフェースを提供する、抽象的な観測ターゲット
- ConcreteSubject:IpMngService
具体的な観測対象は、上記の例では「IP管理サービス」です
- オブザーバー: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