我用C语言玩对象,偷偷关注着你的观察者模式(基类设计)

本文介绍了观察者模式,它定义了一对多依赖关系,主题对象状态变化时会通知观察者。包含被观察者和观察者两类对象,观察者管理可采用数组或链表,建议用链表。给出了被观察者和观察者对象的属性与行为示例,还提供了相关头文件和源文件。

概念

观察者模式(又被称为发布-订阅(Publish/Subscribe)模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。

注意

1、对于观察者模式,包含被观察者(有且只有一个)和观察者(一般为多个)两类对象。

2、观察者对象管理可采用数组和链表两种方式进行管理。

数组管理:定义时必须先确定大小,并且不可改变,所以管理起来相对不够灵活,优点是操作简单。

链表管理:有很强的灵活性,缺点是操作比较麻烦,对于基础稍差的小伙伴不是那么友好。

建议采用链表的方式管理,若对链表操作不是那么懂的小伙伴可翻阅《也没想象中那么神秘的数据结构-通用链表设计方法》,有现成的代码,可以直接使用。

介绍

意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。

如何解决:使用面向对象技术,可以将这种依赖关系弱化。

关键代码:在抽象类里有一个线性表存放观察者们。

核心

定义被观察者对象,并在对象里面定义线性表存放所有观察者对象,当被观察者状态改变时,遍历然后通知所有观察者对象,这种模式有点类似广播。

示例

★被观察者对象:

属性:观察者管理链 (包含所有添加的观察者,因为涉及到增加删除较多,建议使用链表的方式管理)

行为:增加观察者、删除观察者、通知观察者。

★观察者对象:

属性:无

行为:更新数据(在实际对象中实例化)。

★包含头文件observer.h和源文件observer.c(均已验证通过)。

 observer.h

/**
 * @Filename : observer.h
 * @Revision : $Revision: 1.0 $
 * @Author : Feng(更多编程相关的知识和源码见微信公众号:不只会拍照的程序猿,欢迎订阅)
 * @Description : 观察者模式基类实现(C语言模拟C++)
 * @Explain : 包含两个基类 observable(被观察者)   observer(观察者)
              以下使用简称 oble = observable     ober = observer
**/

#ifndef __OBSERVER_H__
#define __OBSERVER_H__

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "dll.h"
#include "class_dll.h"

struct oble;  /* 被观察者类 */
struct ober;  /* 观察者类 */

/* 类操作, 带参数 */
#define CLASS_CALL(THIS,func,args...) ((THIS)->func(THIS,args))

/* 类操作, 无参数 */
#define CLASS_CALL_0(THIS,func) ((THIS)->func(THIS))

/* 节点定义, 使用链表管理观察者 */
struct t_ober_node {
    struct t_node node;
    struct ober *p_ober;
};

/* 观察者类定义 ober = class observer */
struct ober {
    void (*update)(struct ober *p_ober, struct oble *p_oble); /* 数据更新 */
};

/* 被观察者类定义 oble = class observable */
struct oble {
    struct dll *p_ober_dll;     /* 观察者链 */

    int (*add)(struct oble *p_oble, struct ober *p_ober);   /* 添加观察者 */
    void (*rm)(struct oble *p_oble, struct ober *p_ober);   /* 移除观察者 */
    void (*notify)(struct oble *p_oble);                    /* 通知观察者 */
};

/**
 * @创建被观察者对象
 * @成功返回被观察者类地址,失败返回NUL
**/
struct oble *new_oble(void);

#endif

 observer.c

/**
 * @Filename : observer.c
 * @Revision : $Revision: 1.0 $
 * @Author : Feng(更多编程相关的知识和源码见微信公众号:不只会拍照的程序猿,欢迎订阅)
 * @Description : 观察者模式基类实现(C语言模拟C++)
 * @Explain : 包含两个基类 observable(被观察者)   observer(观察者)
              以下使用简称 oble = observable     ober = observer
**/

#include "observer.h"

/**
 * @判断节点中是否包含指定信息,用于链表创建,后续查找使用
 * @node:节点     key:指定信息
 * @包含返回0,否则返回-1
**/
static int _cmp_key(struct t_node *node, void *key)
{
    struct t_ober_node *p_node = NULL;

    if ((node == NULL) || (key == NULL))
        return -1;

    p_node = (struct t_ober_node *)node;

    return (p_node->p_ober == (struct ober *)key ? 0 : -1);
}

/**
 * @添加观察者
 * @p_oble:被观察者类     p_ober:观察者类
 * @包含返回0,否则返回-1
**/
static int _add_ober(struct oble *p_oble, struct ober *p_ober)
{
    struct t_ober_node *p_node = NULL;

    if ((p_oble == NULL) && (p_ober == NULL)) {
        printf("parameter error...\n");
        return -1;
    }

    if (NULL != CLASS_CALL(p_oble->p_ober_dll, find_key, p_ober)) {
        printf("the obsever is already exit...\n");
        return 0;
    }

    if ((p_node = (struct t_ober_node *)malloc(sizeof(struct t_ober_node))) == NULL) {
        printf("malloc node error...\n");
        return -1;      
    }

    p_node->p_ober = p_ober;
    CLASS_CALL(p_oble->p_ober_dll, add, (struct t_node *)p_node);
    return 0;
}

/**
 * @移除观察者
 * @p_oble:被观察者类     p_ober:观察者类
**/
static void _rm_ober(struct oble *p_oble, struct ober *p_ober)
{
    CLASS_CALL(p_oble->p_ober_dll, del_key, p_ober);
}

/**
 * @通知观察者
 * @p_oble:被观察者类
**/
static void _notify_ober(struct oble *p_oble)
{
    struct t_ober_node *p_node = NULL; 

    if (p_oble == NULL) {
        printf("parameter error...\n");
        return;
    }

    p_node = (struct t_ober_node *)CLASS_CALL_0(p_oble->p_ober_dll, get_first);
    while (p_node != NULL) {
        p_node->p_ober->update(p_node->p_ober, p_oble);
        p_node = (struct t_ober_node *)p_oble->p_ober_dll->get_next((struct t_node *)p_node);  
    } 
}

/**
 * @创建被观察者对象
 * @成功返回被观察者类地址,失败返回NUL
**/
struct oble *new_oble(void)
{
    struct dll *p_dll;
    struct oble *p_oble = (struct oble *)malloc(sizeof(struct oble));

    if (p_oble == NULL)
        return NULL;

    memset((char *)p_oble, 0, sizeof(struct oble));

    if ((p_dll = new_dll(_cmp_key, NULL)) == NULL) {
        free(p_oble);
        return NULL;
    }
    p_oble->p_ober_dll = p_dll;
    p_oble->add = _add_ober;
    p_oble->rm = _rm_ober;
    p_oble->notify = _notify_ober;

    return p_oble;
}

结论

本示例只提供了观察者模式的基类定义,后期验证还需要根据实际应用创建被观察者和观察者对象,加入具体应用逻辑代码,方可验证,本公众号也提供两种应用场景来验证此设计模式的有效性,请参见《我用C语言玩对象,观察者模式应用1-订报》和《我用C语言玩对象,观察者模式应用2-热水的用途》。

往期 · 推荐

也没想象中那么神秘的数据结构-先来后到的“队列”(链式队列)

也没想象中那么神秘的数据结构-先来后到的“队列”(循环队列)

也没想象中那么神秘的数据结构-后来居上的“栈”

我用C语言玩对象,观察者模式应用2-热水的用途

我用C语言玩对象,框架化的模板模式

关注

更多精彩内容,请关注微信公众号:不只会拍照的程序猿,本人致力分享linux、设计模式、C语言、嵌入式、编程相关知识,也会抽空分享些摄影相关内容,同样也分享大量摄影、编程相关视频和源码,另外你若想要本文章源码请关注公众号:不只会拍照的程序猿,后台回复:设计模式源码,也可点击此处下载

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不只会拍照的程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值