概述
观察者模式让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。
之前的文章已经详细阐述了这种设计模式的核心和注意事项,并完成了基类设计,请参见《C语言 - 观察者模式(基类部分)》,本文将结合实际案例论证这种设计模式,加深读者对观察者模式的理解和应用。
示例
★背景说明:水温在50℃~ 70℃时, 会发出警告:可以用来洗澡了!水温在100℃时也会发出警告:可以用来饮用了!在这里洗澡模式和饮用模式扮演了监听的角色, 而热水器则是被监听的对象。一旦热水器中的水温度发生变化, 监听者就能及时知道并做出相应的判断和动作。这就是程序设计中监听模式的生动展现。
★被观察者对象(热水器):
属性:温度(当前热水器水温)
行为:获取水温、设置水温。
继承:继承被观察者基类
★观察者对象(洗澡模式、喝水模式):
属性:无
行为:对应行为。
★包含头文件water_heater.h和源文件water_heater.c(均已验证通过)。
water_heater.h
/** * @Filename : water_heater.h * @Revision : $Revision: 1.0 $ * @Author : Feng * @Description : 观察者模式应用(C语言模拟C++) * @Explain : 热水器(被观察者) water_heater 洗澡模式(观察者) washing_mode 50 < 热水器温度 < 70 喝水模式(观察者) drinking_mode 热水器温度 >= 100 **/ #ifndef __WATERHEATER_H__ #define __WATERHEATER_H__ #include <stdio.h> #include <string.h> #include <stdlib.h> #include "observer.h" /* 被观察者(热水器)类定义 */ struct water_heater { struct oble oble; /* 继承被观察者基类 */ int temprature; /* 温度 */ int (*get)(struct water_heater *p_wheater); /* 获取温度 */ void (*set)(struct water_heater *p_wheater, int temp); /* 设置温度 */ }; /** * @创建热水器对象 * @temp:温度 * @成功返回类对象,失败返回NULL **/ struct water_heater *new_water_heater(int temp); /* 观察者(洗澡模式)类定义 */ struct washing_mode { struct ober ober; }; /* 观察者(喝水模式)类定义 */ struct drinking_mode { struct ober ober; }; /** * @创建洗澡模式对象 * @成功返回类对象,失败返回NULL **/ struct washing_mode *new_washing_mode(void); /** * @创建喝水模式对象 * @成功返回类对象,失败返回NULL **/ struct drinking_mode *new_drinking_mode(void); #endif
water_heater.c
/** * @Filename : water_heater.c * @Revision : $Revision: 1.0 $ * @Author : Feng * @Description : 观察者模式应用(C语言模拟C++) * @Explain : 热水器(被观察者) water_heater 洗澡模式(观察者) washing_mode 50 < 热水器温度 < 70 喝水模式(观察者) drinking_mode 热水器温度 >= 100 **/ #include "water_heater.h" /** * @获取热水器温度 * @p_wheater:热水器类 * @返回热水器温度 **/ static int _get_temp(struct water_heater *p_wheater) { return (p_wheater->temprature); } /** * @设置热水器温度,并通知观察者 * @p_wheater:热水器类 temp:温度 **/ static void _set_temp(struct water_heater *p_wheater, int temp) { p_wheater->temprature = temp; printf("current temprature is %d : ", p_wheater->temprature); p_wheater->oble.notify((struct oble *)p_wheater); printf("\n"); } /** * @创建热水器对象 * @temp:温度 * @成功返回类对象,失败返回NULL **/ struct water_heater *new_water_heater(int temp) { struct oble *p_ober; struct water_heater *p_wheater; p_wheater = (struct water_heater *)malloc(sizeof(struct water_heater)); if (p_wheater == NULL) return NULL; memset((char *)p_wheater, 0, sizeof(struct water_heater)); if ((p_ober = new_oble()) == NULL) { free(p_wheater); return NULL; } memcpy(&(p_wheater->oble), p_ober, sizeof(struct oble)); free(p_ober); p_wheater->temprature = temp; p_wheater->get = _get_temp; p_wheater->set = _set_temp; return p_wheater; } /** * @根据水温提示用户是否洗澡 * @p_oble:被观察者类(热水器) **/ static void _update_washing(struct ober *p_ober, struct oble *p_oble) { struct water_heater *p_wheater = (struct water_heater *)p_oble; if ((p_wheater->get(p_wheater) > 50) && (p_wheater->get(p_wheater) < 70)) printf("please washing..."); } /** * @根据水温提示用户是否喝水 * @p_oble:被观察者类(热水器) **/ static void _update_drinking(struct ober *p_ober, struct oble *p_oble) { struct water_heater *p_wheater = (struct water_heater *)p_oble; if (p_wheater->get(p_wheater) >= 100) printf("please drinking..."); } /** * @创建洗澡模式对象 * @成功返回类对象,失败返回NULL **/ struct washing_mode *new_washing_mode(void) { struct ober *p_ober; struct washing_mode *p_cwashingMode; p_cwashingMode = (struct washing_mode *)malloc(sizeof(struct washing_mode)); if (p_cwashingMode == NULL) return NULL; memset((char *)p_cwashingMode, 0, sizeof(struct washing_mode)); if ((p_ober = (struct ober *)malloc(sizeof(struct ober))) == NULL) { free(p_cwashingMode); return NULL; } p_ober->update = _update_washing; memcpy(&(p_cwashingMode->ober), p_ober, sizeof(struct ober)); free(p_ober); return p_cwashingMode; } /** * @创建喝水模式对象 * @成功返回类对象,失败返回NULL **/ struct drinking_mode *new_drinking_mode(void) { struct ober *p_ober; struct drinking_mode *p_cdrinkingMode; p_cdrinkingMode = (struct drinking_mode *)malloc(sizeof(struct drinking_mode)); if (p_cdrinkingMode == NULL) return NULL; memset((char *)p_cdrinkingMode, 0, sizeof(struct drinking_mode)); if ((p_ober = (struct ober *)malloc(sizeof(struct ober))) == NULL) { free(p_cdrinkingMode); return NULL; } p_ober->update = _update_drinking; memcpy(&(p_cdrinkingMode->ober), p_ober, sizeof(struct ober)); free(p_ober); return p_cdrinkingMode; } /** * @主函数,演示代码 **/ int main(void) { struct washing_mode *p_wash = new_washing_mode(); struct drinking_mode *p_drink = new_drinking_mode(); struct water_heater *p_water = new_water_heater(25); if ((p_water == NULL) || (p_wash == NULL) || (p_drink == NULL)) { printf("create observable class failed...\n"); return -1; } p_water->oble.add(&(p_water->oble), &(p_wash->ober)); p_water->oble.add(&(p_water->oble), &(p_drink->ober)); /* 50-70 洗澡, >=100喝水 */ p_water->set(p_water, 40); p_water->set(p_water, 60); p_water->set(p_water, 65); p_water->set(p_water, 120); printf("--------------------------\n"); /* 删除喝水,水温等于120°不响应 */ p_water->oble.rm(&(p_water->oble), &(p_drink->ober)); p_water->set(p_water, 65); p_water->set(p_water, 120); printf("--------------------------\n"); /* 增加喝水,水温等于120°响应 */ p_water->oble.add(&(p_water->oble), &(p_drink->ober)); p_water->set(p_water, 65); p_water->set(p_water, 120); return 0; }
结论
输入示例代码运行,结果如下:
feng:observer$ gcc -o water water_heater.c observer.c class_dll.c dll.c feng:observer$ ./water current temprature is 40 : current temprature is 60 : please washing... current temprature is 65 : please washing... current temprature is 120 : please drinking... -------------------------- current temprature is 65 : please washing... current temprature is 120 : -------------------------- current temprature is 65 : please washing... current temprature is 120 : please drinking... feng:observer$
分析:示例定义了热水器对象作为被观察者,同时定义了饮用模式和洗澡模式对象作为观察者,开始的时候,饮用模式和洗澡模式均监听热水器,所以当水温到达模式设定范围内时,自动触发相应的行为。一段时间后饮用模式不再监听热水器,所以无法触发饮水行为,再后来,饮用模式重新监听热水器,当水温到达设定范围时,又会自动触发饮用模式。
往期 · 推荐
帮你自动化办公的python-自动提取pdf指定页(项目概述)
也没想象中那么神秘的数据结构-一种通用化的双向链表设计(底层源码)
关注
更多精彩内容,请关注微信公众号:不只会拍照的程序猿,本人致力分享linux、设计模式、C语言、嵌入式、编程相关知识,也会抽空分享些摄影相关内容,同样也分享大量摄影、编程相关视频和源码,另外你若想要本文章源码请关注公众号:不只会拍照的程序猿,后台回复:设计模式源码,也可点击此处下载。