设计模式分析和实现:观察者模式

15 篇文章 0 订阅
10 篇文章 0 订阅

观察者模式的应用非常广泛,项目中用到的register/unregister(订阅模式)、attach/detach,本质上都是观察者模式。

使用该模式的目的,是为了更好的实现某个实例的动作自动联动其他实例的动作。

使用时,分清楚观察者和被观察者,很重要。

【举栗子】
假设,我是孩子王,带着几个小屁孩。我要“邪恶”地“作威作福”,于是给他们分派任务!


【任务】小崽子们帮我盯着热水器,温度到60度,就提醒我洗澡;温度到100度,就提醒我沏茶。
【被观察者】热水器,它的功能就是让水温不断升高。
【观察者】
    小明关注热水器的温度,一旦到了60度, 就喊:大哥哥,可以洗澡了!
    小红关注热水器的温度,一旦到了100度,就喊:大哥哥,可以沏茶了!


本质上,热水器只要温度变了,就要通知到观察者小明和小红,小明和小红收到通知就做相关的动作(吆喝我!)。

如何实现呢?直接把模板抽象出来:定义两个结构体(和类很类似):

struct observable{ //被观察者的抽象。
    addObs;        //添加观察者的方法。
    rmObs;         //删除观察者的方法。
    notify;        //通知各个观察者的方法。
    
    list_obs;      //保存各个观察者的链表,小明和小红就在这里。
                   //一旦有事件(温度变化),就依次通知各个观察者。
}

struct observer{   //观察者的抽象
    update;        //通知各个观察者的方法。
}

接下来,定义几个真正的被观察者和观察者:

struct waterheater{   //热水器。
    observable;

    temperature;      //具体实例的特性,热水器,有个特性叫温度。
    set_temprature;  //烧水,使得温度升高。
}

struct wash{         //这是小明。
    observable;      //继承了基类。
    
    //可以加点其他特性,这里不需要,就不加了。
}

struct drink{       //这是小红
    observable;     //继承了基类
    
    //可以加点其他特性,这里不需要,就不加了。
}

这就弄好了,开始测试了:

    // 模拟个热水器出来,把热水器设置温度的方法给到热水器。
    heater = malloc waterheater; 

    // 模拟个小明出来,  把小明的动作“喊我洗澡”添加到小明的update方法中。
    ming = malloc wash; 

    // 模拟个小红出来,  把小红的动作“喊我沏茶”添加到小明的update方法中。
    hong = malloc drink;

    // 把小明和小红添加到热水器的通知链表里: 
    heater->addObs(ming);
    heater->addObs(hong);

    // 开始烧水了:
    heater->set_temprature(40);    // 会通知小明和小红,条件不满足,小明小红不做动作。
    heater->set_temprature(50);    // 会通知小明和小红,条件不满足,小明小红不做动作。
    heater->set_temprature(60);    // 会通知小明和小红,小明执行动作,喊我洗澡。
    heater->set_temprature(90);    // 会通知小明和小红,小明执行动作,喊我洗澡。
    heater->set_temprature(100);   // 会通知小明和小红,小明执行动作,喊我洗澡;小红执行动作,喊我沏茶。

    //可以有更多动作哟


【MORE】

    观察者和被观察者,未必在本地,也可以分处于不同网络节点上。
    比如,热水器就是server,小红小明就是各个client,小红小明分别可以向热水器注册(register),之后,热水器有动作就会分别向小红小明发送网络信息。
     小红小明有专门的线程,收到消息就根据条件执行动作。

其他类似的栗子还有很多,都可以放到火上烤一烤。比如: 
    服务器关机前会通知到所有终端,终端做些善后动作,比如保存数据到Database。
    一旦QQ在异地登录,就短信通知给owner。
    交通灯变灯就通知给行人或者车辆。
    网络接口被shut,通知给其他各个应用。
    

再说下应用场景:一个实例,它的某些变化,其他实例需要跟随,那用观察者模式通常比较合适。

具体的代码,放在这里吧,后续会更新到github上。

/* listen.c */

#include "../adt/dlist.h"

#ifndef __LISTEN_H__
#define __LISTEN_H__

// 关于ADT 偏移的操作:
#define offsetof(type,field)    ((char *) &((type *) 0)->field - (char *) 0)                                                          
#define container_of(ptr, type, member) (type*)((char*)ptr - offsetof(type, member))                                                  
            
// 先以typedef声明,再定义结构体,可以解决顺序倒挂问题:
// 比如StructA中包含B,但B这个函数定义又引用了StructA。                                                                                                                          
typedef struct observable_ observable_t;                                                                                              
typedef struct observer_ observer_t;                                                                                                  

// 被观察者需要实现的方法:添加/删除观察者、通知各个观察者                                                                                                               
typedef void (add_obs_t)(observable_t *self, observer_t *obs);                                                                        
typedef void (rm_obs_t) (observer_t *obs);                                                                                            
typedef void (notify_obs_t) (observable_t *self, observer_t *obs);                                                                    

// 观察者需要实现的方法:一般是上层传入的CB。
typedef void (update_t) (observable_t *self, observer_t *obs);                                                                        

// 观察者的数据结构
typedef struct observer_ {                                                                                                            
     
    // tips:使用struct而不是指针,这样它和类一起malloc和free.
    // 同时,因为线性连续,可以通过container_of直接拿到大结构体。                                                                                                 
    AdtDList_t hook;
                                                                                                                                  
    update_t *update;                                                                                                                 
} observer_t;                                                                                                                         

// 被观察者的数据结构
typedef struct observable_ {                                                                                                          
    AdtDList_t   observer_list;                                                                                                       
                                                                                                                                      
    add_obs_t    *add_obs;                                                                                                            
    rm_obs_t     *rm_obs;                                                                                                             
    notify_obs_t *notify_obs;                                                                                                         
} observable_t;                                                                                                                       
                                                                                                                          
void add_observer(observable_t *self, observer_t *observer);                                                                          
void rm_observer(observer_t *observer);                                                                                               
void notify_observers(observable_t *self, observer_t *observer);                                                                      
                                                                                                                                      
void observable_init(observable_t * obs);                                                                                             
void observer_init(observer_t * obs, update_t *cb);

#endif
/* listen.c */

#include "listen.h"
#include <stdio.h>

// 初始化被观察者
// 目的是初始化被观察者的链表(存储各个观察者)、方法(添加删除观察者以及通知观察者)
void observable_init(observable_t * obs)
{   
    if (obs) {
        obs->add_obs    = add_observer;
        obs->rm_obs     = rm_observer;
        obs->notify_obs = notify_observers;
        
        ADT_DLIST_HEAD_INIT(&obs->observer_list);
    }
}

// 初始化观察者
// 目的是初始化观察者的update方法,以及用以被挂载的hook
void observer_init(observer_t * obs, update_t *cb)
{
    if (obs) {
        obs->update    = cb;
        ADT_DLIST_NODE_INIT(&obs->hook);
    }
}

// 添加观察者
void add_observer(observable_t *self, observer_t *observer)
{
    if(!self) return;

    if (observer) {
        ADT_DLIST_LINK_NEXT(&self->observer_list, &observer->hook);
    }
}

// 删除观察者
void rm_observer(observer_t *observer)
{
    if (observer) {
        ADT_DLIST_UNLINK(&observer->hook);
        ADT_DLIST_NODE_INIT(&observer->hook);
    }
}

// 通知观察者
void notify_observers(observable_t *self, observer_t *observer/*UNUSED*/)
{
    AdtDList_t*     hook;
    observer_t*     obs;

    if(!self) return;

    if (observer) {
        //PULL mode.
    } else {
        if(!ADT_DLIST_IS_EMPTY(&self->observer_list)) {
            return;
        }

        ADT_DLIST_EACH_NEXT(&self->observer_list, hook) {
            obs = container_of(hook, observer_t, hook);
            obs->update(self, obs);
        }
    }
}
/* test.c */

#include "listen.h"
#include "stdlib.h"
#include "stdio.h"

typedef struct water_heater water_heater_t;
typedef void (set_temp_t)(water_heater_t *heater, int temp);

// 定义真正的实例类,集成了被观察者/观察者的基类(即struct),也可以添加自己的特性。
struct water_heater {
    observable_t    observable;

    int             index;
    int             temp;
    set_temp_t      *set_temp;
};

typedef struct washer {
    observer_t      observer;

    int             index;
} washer_t;

typedef struct drink {
    observer_t      observer;

    int             index;
} drink_t;

// 这是被观察者的特性方法
void heater_set_temp(water_heater_t *heater, int temp)
{
    if (heater) {
        heater->temp = temp;

        // 这里连接了观察者和被观察者
        heater->observable.notify_obs(&heater->observable, NULL);
    }
    return;
}

// 观察者被通知时,观察者需要执行的update函数:一个是wash,一个是drink
void washing(observable_t *observable, observer_t *observer)
{
    water_heater_t* heater = container_of(observable, water_heater_t, observable);

    if (heater->temp >= 60) {
        printf("washing... tmp:%d \n", heater->temp);
    }

    return;
}

void drinking(observable_t *observable, observer_t *observer)
{
    water_heater_t* heater = container_of(observable, water_heater_t, observable);

    if (heater->temp >= 100) {
        printf("drinking TEA... tmp:%d \n", heater->temp);
    }

    return;
}

int main()
{
    // 实例化一个热水器出来
    water_heater_t *heater = (water_heater_t *) malloc(sizeof(water_heater_t));
    if (!heater) return -1;

    // 初始化热水器的特性以及父类的初始化
    heater->set_temp = heater_set_temp;
    observable_init(&heater->observable);

    // 实例化两个观察者,并初始化观察者的父类。
    washer_t *washer = (washer_t *) malloc(sizeof(washer_t));
    if (!washer) return -1;
    observer_init(&washer->observer, washing);

    drink_t *drink = (drink_t *) malloc(sizeof(*drink));
    if (!drink) return -1;
    observer_init(&drink->observer, drinking);

    // 添加观察者
    heater->observable.add_obs(&heater->observable, &washer->observer);
    heater->observable.add_obs(&heater->observable, &drink->observer);

    // 热水器开始有动作,会被自动观察
    heater->set_temp(heater, 40);
    heater->set_temp(heater, 60);
    heater->set_temp(heater, 70);
    heater->set_temp(heater, 80);
    heater->set_temp(heater, 90);
    heater->set_temp(heater, 100);

    return 0;
}
/* dlist.h */

#ifndef ADT_DLIST_H_
#define ADT_DLIST_H_

#define ADT_DLIST_DEF(s_name)                                                 \
    struct {                                                                  \
        struct s_name* next;                                                  \
        struct s_name* prev;                                                  \
    } DLIST

#define ADT_DLIST_NEXT(node) ((node)->DLIST.next)

#define ADT_DLIST_PREV(node) ((node)->DLIST.prev)

#define ADT_DLIST_HEAD_INIT(head)                                             \
    do {                                                                      \
        ADT_DLIST_NEXT(head) = (head);                                        \
        ADT_DLIST_PREV(head) = (head);                                        \
    } while(0)

#define ADT_DLIST_NODE_INIT(node)                                             \
    do {                                                                      \
        ADT_DLIST_NEXT(node) = NULL;                                          \
        ADT_DLIST_PREV(node) = NULL;                                          \
    } while(0)

#define ADT_DLIST_LINK_NEXT(link, node)                                       \
    do {                                                                      \
        ADT_DLIST_PREV(ADT_DLIST_NEXT(link)) = (node);                        \
        ADT_DLIST_NEXT(node)                 = ADT_DLIST_NEXT(link);          \
        ADT_DLIST_PREV(node)                 = (link);                        \
        ADT_DLIST_NEXT(link)                 = (node);                        \
    } while(0)

#define ADT_DLIST_UNLINK(link)                                                \
    do {                                                                      \
        ADT_DLIST_PREV(ADT_DLIST_NEXT(link)) = ADT_DLIST_PREV(link);          \
        ADT_DLIST_NEXT(ADT_DLIST_PREV(link)) = ADT_DLIST_NEXT(link);          \
        ADT_DLIST_NODE_INIT(link);                                            \
    } while(0)

#define ADT_DLIST_EACH_NEXT(head, link)                                       \
    for (link = head; (link = ADT_DLIST_NEXT(link)) != (head); )

#define ADT_DLIST_EACH_NEXT_SAFE(head, link, save)                      \
    for ((void) (link = ADT_DLIST_NEXT(head)), save = ADT_DLIST_NEXT(link) ; \
         link != (head);                                                \
         (void) (link = save), (void) (save = ADT_DLIST_NEXT(link)))

#define ADT_DLIST_IS_EMPTY(head) (ADT_DLIST_NEXT(head) == head)

#define ADT_DLIST_IS_LINKED(node) (ADT_DLIST_NEXT(node) != NULL)

typedef struct AdtDList_s
{
    ADT_DLIST_DEF(AdtDList_s);
} AdtDList_t;

#endif
pwater > gcc test_listen.c listen.c  
                                                                                                                                                                                                             
pwater > ./a.out 
                                                                                                                                                                                                                                 
washing... tmp:60 
washing... tmp:70 
washing... tmp:80 
washing... tmp:90 
drinking TEA... tmp:100 
washing... tmp:100 

pwater >         


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值