Observer(观察者)模式


前言

        本周参加了Observer(观察者)模式研讨会,本文的目的是对这个模式的总结和分享,希望对需要学习这个设计模式的同学有所帮助。本文一共分为两个部分,第一个部分是观察者模式的基础知识总结;第二个部分是分享观察者模式简单示例。

一、观察者模式总结

        

1、模式意图

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

2、参与者

Subject 目标
– 目标知道它的观察者。可以有任意多个观察者观察同一个目标;
– 提供注册和删除观察者对象的接口;
Observer 观察者
– 为那些在目标发生改变时需要获得通知的对象定义一个更新接口;
ConcreteSubject 具体目标
– 将有关状态存入各concrete observer对象;
– 当它的状态发生改变时,向其各个观察者发出通知 ;
ConcreteObserver 具体观察者
– 维护一个指向concretesubject对象的引用;
– 存储有关状态,这些状态应与目标的状态保持一致 ;
– 实现observer的更新接口,以使自身状态与目标状态保持一致 ;

3、结构、协作、适用性及效果

结构图:

在这里插入图片描述

协作:
1、当ConcreteSubject发生任何可能导致其观察者与其本身状态不一致的改变时,它将通知 它的各个观察者;
2、在得到一个具体目标的改变通知后 , ConcreteObserver对象可向目标对象查询信息。 ConcreteObserver使用这些信息以使它的状态与目标对象的状态一致。

在这里插入图片描述

适用性:
1、一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这二者封装在独立的对象中,以使它们可以独立地改变和复用。
2、对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变。
3、一个对象必须通知其他对象,而它又不能假定其他对象是谁。
     效果:
1、目标和观察者间的抽象耦合。

一个目标所知道的仅仅是它有一系列观察者,每个都符合抽象的observer类的简单接口。目标不知道任何一个观察者属于那个具体的类。这样目标和观察者之间的耦合是抽象的和最小的。

2、支持广播通信。

不像通常的请求,目标发送的通知不需要指定它的接收者。通知被自动广播给所有已向该目标对象登记的对象。

3、意外的更新。

由于一个观察者不知道其他观察者的存在,它可能对改变目标的最终代价一无所知。在目标上一个看似无害的操作可能会引起一系列对观察者以及依赖于这些观察者的对象的更新。


二、观察者模式简单示例

1、用例描述

         之前写的一篇关于[函数指针的博文](https://editor.csdn.net/md/?articleId=109698144),其内容是状态机解析出一个个的单词,由函数指针来处理解析出来的单词。这里利用这个例子,把统计单词总个数的功能模块设计为观察者observer_total_counter;把统计某一个单词出现个数的功能模块设计为观察者observer_word_counter。把单词设计为主题,当解析出一个单词的时候,触发主题状态改变,通知每个观察者改变自身状态。     

2、示例代码

         observer_pattern.h 观察者模式主题定义,观察者保存在一个双向列表中。
typedef void (*update_t)(void* ctx, const char* word);

	typedef struct _observer_t {
		void* ctx;
		update_t update;
	} observer_t;
	
	typedef struct _observer_node_t {
		observer_t observer;
		struct _observer_node_t* next_observer;
		struct _observer_node_t* previous_observer;
	} observer_node_t;

	typedef struct _subject_t {
	/**
	 * @property {char[]} word
	 * @annotation ["readable"]
	 * 单词。
	 */
	char word[51];
	/**
	 * @property {uint32_t} size
	 * @annotation ["readable"]
	 * 单词的长度。
	 */
	uint32_t size;

	/**
	 * @property {observer_node_t*} observer_list
	 * 观察者列表。
	 */
	observer_node_t* observer_list;
} subject_t;

         主题提供的常用接口定义,包含构造主题、析构主题、观察者的注册、移除及通知发送接口:
/**
 * @method observer_pattern_subject_init
 * 初始化subject对象。
 * @annotation ["constructor"]
 * @param {subject_t*} subject 主题对象。
 *
 * @return {subject_t*} 主题对象本身。
 */
subject_t* observer_pattern_subject_init(subject_t* subject);

/**
 * @method observer_pattern_notify
 * 主题状态改变,通知观察者。
 * @param {subject_t*} subject 主题对象。
 * @param {const char*} word 主题的内容(单词)。
 * @param {uint32_t} size 主题内容长度(单词长度)。
 *
 * @return {ret_t} RET_OK表述成功。
 */
ret_t observer_pattern_notify(subject_t* subject, const char* word, uint32_t size);

/**
 * @method observer_pattern_add_observer
 * 添加观察者。
 * @param {subject_t*} subject 主题对象。
 *@param {observer_t*} observer 观察者。
 *
 * @return {bool_t*} 成功返回TRUE。
 */
bool_t observer_pattern_add_observer(subject_t* subject, observer_t* observer);

/**
 * @method observer_pattern_remove_observer
 * 移除观察者。
 * @param {subject_t*} subject 主题对象。
 *@param {observer_t*} observer 观察者。
 * 
 * @return {bool_t*} 成功返回TRUE。
 */
bool_t observer_pattern_remove_observer(subject_t* subject, observer_t* observer);

/**
 * @method observer_pattern_subject_deinit
 * 析构subject对象。
 * @annotation ["destructor"]
 * @param {subject_t*} subject 主题对象。
 *
 * @return {subject_t*} 主题对象本身。
 */
ret_t observer_pattern_subject_deinit(subject_t* subject);

         observer_pattern.c 接口的具体实现:

/**
 * File:   observer_pattern.c
 * Author: 
 * Brief:  observer pattern
 *
 * Copyright (c) 2019 - 2020  Guangzhou ZHIYUAN Electronics Co.,Ltd.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * License file for more details.
 *
 */

 /**
  * History:
  * ================================================================
  * 2020-11-28 ZHANG ZHONGJI <zhangzhongji@zlgmcu.cn> created
  */

#include "observer_pattern.h"

subject_t* observer_pattern_subject_init(subject_t* subject) {
	return_value_if_fail(subject != NULL, NULL);
	memset(subject->word, '\0', sizeof(subject->word));
	subject->size = 0;
	subject->observer_list = NULL;
	return subject;
}

static observer_node_t* observer_exist(subject_t* subject, observer_t* observer) {
	return_value_if_fail(subject != NULL && observer != NULL, FALSE);
	observer_node_t* next_observer_node = subject->observer_list;
	while (next_observer_node != NULL) {
		if (next_observer_node->observer.ctx == observer->ctx &&
			next_observer_node->observer.update == observer->update) {
			return next_observer_node;
		}
		else {
			next_observer_node = next_observer_node->next_observer;
		}
	}
	
	return NULL;
}

ret_t observer_pattern_notify(subject_t* subject, const char* word, uint32_t size) {
	return_value_if_fail(subject != NULL && word != NULL, RET_OK);
	int32_t len = tk_min(sizeof(subject->word) - 1, size);
	memcpy(subject->word, word, len);
	subject->word[len] = '\0';
	subject->size = len;

	observer_node_t* observer_tmp = subject->observer_list;
	while (observer_tmp != NULL) {
		observer_tmp->observer.update(observer_tmp->observer.ctx, subject->word);
		observer_tmp = observer_tmp->next_observer;
	}

	return RET_OK;
}

bool_t observer_pattern_add_observer(subject_t* subject, observer_t* observer) {
	return_value_if_fail(subject != NULL && observer != NULL, FALSE);
	if (observer_exist(subject, observer) == NULL) {
		observer_node_t* observer_node = (observer_node_t*)malloc(sizeof(observer_node_t));
		return_value_if_fail(observer_node != NULL, FALSE);
		observer_node->observer.ctx = observer->ctx;
		observer_node->observer.update = observer->update;
		observer_node->next_observer = NULL;
		
		if (subject->observer_list == NULL) {
			subject->observer_list = observer_node;
			observer_node->previous_observer = NULL;
		} else {
			observer_node_t* observer_node_tmp = subject->observer_list;
			while (observer_node_tmp->next_observer != NULL) {
				observer_node_tmp = observer_node_tmp->next_observer;
			}
			observer_node_tmp->next_observer = observer_node;
			observer_node->previous_observer = observer_node_tmp;
		}
	}

	return TRUE;
}

bool_t observer_pattern_remove_observer(subject_t* subject, observer_t* observer) {
	return_value_if_fail(subject != NULL && observer != NULL, FALSE);
	observer_node_t* observer_node_tmp = observer_exist(subject, observer);
	if (observer_node_tmp != NULL) {
		if (observer_node_tmp->previous_observer == NULL) {
			subject->observer_list = observer_node_tmp->next_observer;
		}
		else {
			observer_node_t* previous_observer = observer_node_tmp->previous_observer;
			previous_observer->next_observer = observer_node_tmp->next_observer;
		}
	}

	return TRUE;
}

ret_t observer_pattern_subject_deinit(subject_t* subject) {
	return_value_if_fail(subject != NULL, RET_BAD_PARAMS);
	observer_node_t* next_observer = subject->observer_list;
	while (next_observer != NULL) {
		observer_node_t* next_observer_tmp = next_observer->next_observer;
		free(next_observer);
		next_observer = next_observer_tmp;
	}

	return RET_OK;
}


总结

         观察者模式的设计要点总结:
1、主题需要一个容器保存观察者,这里使用了双向列表保存观察者,使用双向列表的优点是,查找、添加、删除节点比较方便;
2、主题需要提供一对管理观察者的接口:注册一个观察者的接口和移除一个观察者的接口;
3、主题需要提供一个通知接口,当其状态改变时,应立即把通知发出,通过观察者提供的更新接口,更新每一个观察者的状态;
4、观察者需要提供一个接口,这个接口负责接收通知,并更新自身状态。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值