一. 定義
觀察者模式定義:當對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並自動更新,也可以稱這種模式發佈-訂閱模式
二.應用場景
應用場景:Excel 中的數據與折線圖、餅狀圖、柱狀圖之間的關係;MVC 模式中的模型與視圖的關係;事件模型中的事件源與事件處理者等等
三.優缺點
主要優點有,降低了目標與觀察者之間的耦合關係,兩者之間是抽象耦合關係,目標與觀察者之間建立了一套觸發機制
當然也有缺點:當觀察者對象很多時,通知的發佈會花費很多時間,影響程序的效率
所謂技術利弊兩面,取決於具體應用需求
四.模式結構
觀察者模式的結構如下。
-
抽象主題(Subject)角色:也叫抽象目標類,它提供了一個用於保存觀察者對象的集合和增加、刪除觀察者對象的方法,以及通知所有觀察者的抽象方法。
-
抽象觀察者(Observer)角色:它是一個抽象類或接口,它包含了一個更新自己的抽象方法,當接到具體主題的更改通知時被調用
結構圖如下:
五.C語言實現
具體實現源碼如下:源碼github獲取
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
/**
* @brief 觀察者抽象類
* @note adapter 適配接口,適配主題 update_proc 更新處理接口
*/
typedef struct Observer_t
{
char name[32];
bool (*adapter)(char *themeName);
bool (*update_proc)(struct Observer_t *observerObj, char *themeName);
}Observer_t;
/**
* @brief 主題抽象類
* @note register_list 觀察者列表 add 觀察者註冊接口 del 註銷觀察者接口
* @note notify 通知觀察者接口,這裏可以實現爲同步或者異步通知
*/
typedef struct Subject_t
{
char name[32];
struct Observer_t *register_list[10];
bool (*add)(struct Subject_t *subjectobj, struct Observer_t * observerobj);
bool (*del)(struct Subject_t *subjectobj, struct Observer_t * observerobj);
bool (*notify)(struct Subject_t * subjectObj, char *event);
}Subject_t;
/**
* @brief adapter
* @param themeName 主題名
* @return true適配成功 otherwise false 適配失敗
* @note 對主題過濾,對適配的主題進行通知
*/
bool adapter(char *themeName)
{
if (0 == strcasecmp(themeName, "csdn-blog"))
return true;
else
return true;
}
/**
* @brief update_proc
* @param observerObj 觀察者 themeName 主題
* @return true
* @note 更新主題,或者處理對應事件
*/
bool update_proc(Observer_t *observerObj, char *themeName)
{
printf("[%s]收到[%s]主題跟新通知.....\n", observerObj->name, themeName);
return true;
}
/**
* @brief add
* @param subjectObj 主題對象
* @param observerObj 觀察者對象
* @return true 註冊進觀察者列表成功,else false 註冊失敗
* @note 觀察者對象註冊
*/
bool add(Subject_t *subjectObj, Observer_t *observerObj)
{
for(int i = 0; i < (sizeof(subjectObj->register_list)/sizeof(subjectObj->register_list[0])); i++)
{
if (subjectObj->register_list[i] == NULL)
{
subjectObj->register_list[i] = observerObj;
return true;
}
}
return false;
}
/**
* @brief del
* @param subjectObj 主題對象
* @param observerObj 觀察者對象
* @return true 註銷進觀察者列表成功,else false 註冊失敗
* @note 觀察者對象註銷
*/
bool del(Subject_t * subjectObj, Observer_t *observerObj)
{
for (int i = 0; i< (sizeof(subjectObj->register_list)/sizeof(subjectObj->register_list[0])); i++)
{
if (subjectObj->register_list[i] == observerObj)
{
subjectObj->register_list[i] = NULL;
free(observerObj);
return true;
}
}
return false;
}
/**
* @brief notify
* @param subjectObj 主題對象
* @param event 通知事件
* @return true
* @note 通知到所有觀察者
*/
bool notify(Subject_t * subjectObj, char *event)
{
for(int i = 0; i< (sizeof(subjectObj->register_list)/sizeof(subjectObj->register_list[0])); i++)
{
if (NULL == subjectObj->register_list[i])
continue;
if (subjectObj->register_list[i]->adapter(event))
subjectObj->register_list[i]->update_proc(subjectObj->register_list[i], \
subjectObj->name);
}
return true;
}
/**
* @brief Observer_new
* @param name 對象名字
* @return observerobj
* @note 創建一個觀察者對象
*/
Observer_t* Observer_new(char *name)
{
Observer_t * p;
p = malloc(sizeof(*p));
if (!p)
return NULL;
memcpy(p->name, name, strlen(name));
p->adapter = adapter;
p->update_proc = update_proc;
return p;
}
/**
* @brief Subject_new
* @param name 對象名字
* @return subjectobj
* @note 創建一個主題對象
*/
Subject_t* Subject_new(char * name)
{
Subject_t * p;
p = malloc(sizeof(*p));
if (!p)
return NULL;
memset(p, 0x00, sizeof(*p));
memcpy(p->name, name, strlen(name));
p->add = add;
p->del = del;
p->notify = notify;
}
int main(void)
{
Subject_t *subject;
Observer_t *observerA;
Observer_t *observerB;
Observer_t *observerC;
printf("start to create subjecter and observer.....\n");
// 創建一個主題
subject = Subject_new("creater");
//創建多個消費者
observerA = Observer_new("readerA");
observerB = Observer_new("readerB");
observerC = Observer_new("readerC");
// 添加消費者到觀察者列表
subject->add(subject, observerA);
subject->add(subject, observerB);
subject->add(subject, observerC);
// 通知給所有讀者
subject->notify(subject, "csdn-blog");
}
六.總結
當一個對象的改變需要同時改變其它對象,並且它不知道具體有多少對象有待改變的時候,應該考慮使用觀察者模式,同時觀察者模式所做的工作也是在解除耦合,各個對象互爲獨立工作。