观察者模式与推拉模型
作者卡奴达摩
连接:http://blog.csdn.net/zhengzhb/article/details/7471978
定义:定义对象间一种一对多的依赖关系,使得当每一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。
类型:行为类模式
类图:
在软件系统中经常会有这样的需求:如果一个对象的状态发生改变,某些与它相关的对象也要随之做出相应的变化。比如,我们要设计一个右键菜单的功能,只要在软件的有效区域内点击鼠标右键,就会弹出一个菜单;再比如,我们要设计一个自动部署的功能,就像eclipse开发时,只要修改了文件,eclipse就会自动将修改的文件部署到服务器中。这两个功能有一个相似的地方,那就是一个对象要时刻监听着另一个对象,只要它的状态一发生改变,自己随之要做出相应的行动。其实,能够实现这一点的方案很多,但是,无疑使用观察者模式是一个主流的选择。
观察者模式的结构
在最基础的观察者模式中,包括以下四个角色:
- 被观察者:从类图中可以看到,类中有一个用来存放观察者对象的Vector容器(之所以使用Vector而不使用List,是因为多线程操作时,Vector在是安全的,而List则是不安全的),这个Vector容器是被观察者类的核心,另外还有三个方法:attach方法是向这个容器中添加观察者对象;detach方法是从容器中移除观察者对象;notify方法是依次调用观察者对象的对应方法。这个角色可以是接口,也可以是抽象类或者具体的类,因为很多情况下会与其他的模式混用,所以使用抽象类的情况比较多。
- 观察者:观察者角色一般是一个接口,它只有一个update方法,在被观察者状态发生变化时,这个方法就会被触发调用。
- 具体的被观察者:使用这个角色是为了便于扩展,可以在此角色中定义具体的业务逻辑。
- 具体的观察者:观察者接口的具体实现,在这个角色中,将定义被观察者对象状态发生变化时所要处理的逻辑。
观察者模式代码实现
abstract class Subject { private Vector<Observer> obs = new Vector<Observer>(); public void addObserver(Observer obs){ this.obs.add(obs); } public void delObserver(Observer obs){ this.obs.remove(obs); } protected void notifyObserver(){ for(Observer o: obs){ o.update(); } } public abstract void doSomething();
} class ConcreteSubject extends Subject { public void doSomething(){ System.out.println("被观察者事件反生"); this.notifyObserver(); }
}
interface Observer { public void update();
}
class ConcreteObserver1 implements Observer { public void update() { System.out.println("观察者1收到信息,并进行处理。"); }
}
class ConcreteObserver2 implements Observer { public void update() { System.out.println("观察者2收到信息,并进行处理。"); }
} public class Client { public static void main(String[] args){ Subject sub = new ConcreteSubject(); sub.addObserver(new ConcreteObserver1()); //添加观察者1 sub.addObserver(new ConcreteObserver2()); //添加观察者2 sub.doSomething(); }
}
运行结果
被观察者事件反生
观察者1收到信息,并进行处理。
观察者2收到信息,并进行处理。
通过运行结果可以看到,我们只调用了Subject的方法,但同时两个观察者的相关方法都被同时调用了。仔细看一下代码,其实很简单,无非就是在Subject类中关联一下Observer类,并且在doSomething方法中遍历一下Observer的update方法就行了。
观察者模式的优点
观察者与被观察者之间是属于轻度的关联关系,并且是抽象耦合的,这样,对于两者来说都比较容易进行扩展。
观察者模式是一种常用的触发机制,它形成一条触发链,依次对各个观察者的方法进行处理。但同时,这也算是观察者模式一个缺点,由于是链式触发,当观察者比较多的时候,性能问题是比较令人担忧的。并且,在链式结构中,比较容易出现循环引用的错误,造成系统假死。
总结
Java语言中,有一个接口Observer,以及它的实现类Observable,对观察者角色常进行了实现。我们可以在jdk的api文档具体查看这两个类的使用方法。
做过VC++、JavaScript DOM或者AWT开发的朋友都对它们的事件处理感到神奇,了解了观察者模式,就对事件处理机制的原理有了一定的了解了。如果要设计一个事件触发处理机制的功能,使用观察者模式是一个不错的选择,AWT中的事件处理DEM(委派事件模型Delegation Event Model)就是使用观察者模式实现的。
作者:RayChase
连接:http://raychase.iteye.com/blog/1337015
观察者模式,指的是定义一种对象间的一对多的关系,当一个对象的状态发生变化的时候,所有依赖于它的对象都将得到通知并更新自己。
现在要说的分歧在这里:
“推”的方式是指,Subject维护一份观察者的列表,每当有更新发生,Subject会把更新消息主动推送到各个Observer去。
“拉”的方式是指,各个Observer维护各自所关心的Subject列表,自行决定在合适的时间去Subject获取相应的更新数据。
“推”的好处包括:
1、高效。如果没有更新发生,不会有任何更新消息推送的动作,即每次消息推送都发生在确确实实的更新事件之后,都是有意义的。
2、实时。事件发生后的第一时间即可触发通知操作。
3、可以由Subject确立通知的时间,可以避开一些繁忙时间。
4、可以表达出不同事件发生的先后顺序。
“拉”的好处包括:
1、如果观察者众多,Subject来维护订阅者的列表,可能困难,或者臃肿,把订阅关系解脱到Observer去完成。
2、Observer可以不理会它不关心的变更事件,只需要去获取自己感兴趣的事件即可。
3、Observer可以自行决定获取更新事件的时间。
4、拉的形式可以让Subject更好地控制各个Observer每次查询更新的访问权限。
----------------------------------------------------------------------------------------------------------------------------------
2012-2-27 补充:
事实上“推”和“拉”可以比较的内容太多了,比如:
客户端通常是不稳定的,服务端是稳定的,如果消息由客户端主动发起去获取,它很容易找到服务端的地址,可以比较容易地做到权限控制(集中在服务端一处),服务端也可以比较容易地跟踪客户端的位置和状态,反之则不行;
互联网页面的访问就是一个最好的“拉”的模式的例子;
通常我们希望把压力分散到各个客户端上去,服务端只做最核心的事情,只提供内容,不管理分发列表;
……
还有一个idea是关于“推”和“拉”结合的形式,例如,服务端只负责通知某一些数据已经准备好,至于是否需要获取和什么时候客户端来获取这些数据,完全由客户端自行确定。
、、、、、、、转者、、、、、、、
#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <algorithm>/**********************************************************************************
最开始使用智能指针实现的,但是发现了一些"问题":
1、智能指针没有复制兼容性原则:指向子类的智能指针不能向指向父类的智能指针赋值/初始化
2、指向基类的智能指针甚至不能用来指向原生的子类对象
除非自己实现转换函数,且转换函数的频繁调用成为必然,但实际上不应该频繁的转换类型一些注意事项,观察者在自身析构之前,必须向主题取消注册,以免产生不必要的麻烦
***********************************************************************************/
class AbsSubject; //前向声明class AbsObserver
{
public:virtual void Update(std::string report) = 0; //推送内容方式,注意 主题与观察者是分离的,不要盲目追求效率而声明参数为引用virtual void Updata(AbsSubject *sj) = 0; //拉取内容方式,想要知道具体主题的具体内容,需自己去取感兴趣的项,此方式需求客户遵守约定,并且不在运行时修改成员变量virtual ~AbsObserver(){};
};class AbsSubject
{
public:virtual void RegisterObserver(AbsObserver *ob) = 0; //由于允许子类自行选取数据结构存储观察者,因此定义为纯虚函数,此外,此处也助于减少行为的继承virtual void CancelObserver(AbsObserver *ob) = 0;virtual void Notify() = 0;virtual ~AbsSubject(){}; //AbsSubject的子类中可能拥有指向观察者的指针,但不应该在析构函数中释放这些指针,毕竟主题与观察者是独立的
};class Secretary : public AbsSubject //专用秘书,单人使用
{
private:AbsObserver *pLeader;std::string plan;void Notify(){if( NULL != pLeader ){pLeader->Update(plan);}}public:Secretary() : pLeader(NULL){ }explicit Secretary(AbsObserver *ob){RegisterObserver(std::move(ob)); }void RegisterObserver(AbsObserver *ob){pLeader = ob;}void CancelObserver(AbsObserver *ob){pLeader = NULL;}void SetPlan(std::string NewPlan) {plan = NewPlan;Notify();}
};class Reception : public AbsSubject //勤劳的前台MM
{
private:std::vector<AbsObserver *> member;std::string bossEvent;std::string managerEvent;std::string HREvent;public:Reception(){}explicit Reception(AbsObserver *ob){RegisterObserver(ob);}void RegisterObserver(AbsObserver *ob){member.push_back(std::move(ob)); //避免拷贝}void CancelObserver(AbsObserver *ob){std::vector<AbsObserver *>::const_iterator it;it = std::find(member.cbegin(), member.cend(), ob);if( it != member.cend() ){member.erase(it);}}void Notify(){for (auto &it : member){if( NULL != it ){it->Updata(this);}}}void HasEvent(std::string bossEvent, std::string managerEvent, std::string HREvent){this->bossEvent = bossEvent;this->managerEvent = managerEvent;this->HREvent = HREvent;Notify();}std::string GetBossEvent()const{return bossEvent;}std::string GetMnagerEvent()const{return managerEvent;}std::string GetHREvent()const{return HREvent;}
};class Boss : public AbsObserver
{
private:std::string plan;
public:void Update(std::string report) //推送数据,推送方式下观察者不能选择性获取自己感兴趣的内容{plan = report;std::cout << "Boss " << report << std::endl;}void Updata(AbsSubject * sj) //拉取数据,此方式可由观察者选择自己感兴趣的内容{if( Reception * re = dynamic_cast<Reception *>(sj) ){ std::cout << "Boss " << re->GetBossEvent() << std::endl; //挑自己感兴趣的}}
};class Manager : public AbsObserver
{
public:void Update(std::string report){std::cout << report << std::endl;}void Updata(AbsSubject * sj){if (Reception * re = dynamic_cast<Reception *>(sj)){std::cout << "Manager " << re->GetMnagerEvent() << std::endl;}}void DoSomething(){std::cout << "DoSomething" << std::endl;}
};class HR : public AbsObserver
{
public:void Update(std::string report){std::cout << report << std::endl;}void Updata(AbsSubject * sj){if( Reception * re = dynamic_cast<Reception *>(sj) ){std::cout << "HR " <<re->GetHREvent() << std::endl;}}void DoSomething(){std::cout << "DoSomething" << std::endl;}
};int main(int argc, char **argv)
{Boss boss;Secretary secretary(&boss);Reception *reception = new Reception;if( NULL == reception )return -1;Manager manager;HR *hr = new HR;if( NULL == hr )return -1;reception->RegisterObserver(&manager);reception->RegisterObserver(hr);reception->RegisterObserver(&boss);hr->DoSomething();secretary.SetPlan(std::string("phone call"));reception->HasEvent(std::string("visit"), std::string("expressage"), std::string("interview"));manager.DoSomething();secretary.CancelObserver(&boss);reception->CancelObserver(hr);reception->CancelObserver(&manager);reception->CancelObserver(&boss);secretary.SetPlan(std::string("nothing"));reception->HasEvent(std::string("nothing"), std::string("nothing"), std::string("nothing"));delete reception;delete hr;return 0;
}
观察者模式与推拉模型相关推荐
- rsync推拉模型及结合inotify实现推模型自动同步
一.前言 无论使用什么操作系统下,都经常有同步文件的需求,不管发生在本地,还是发生在本地和远程主机之间.那么应该怎么做呢? 使用拷贝类的命令,本地使用cp命令,复制到远程主机使用scp这样的命令,保证 ...
- su联合推拉插件_[实习小记一一SU建模]
鉴于本菜鸡在实习阶段一直在SU建模,那就讲讲我在SU建模的心路历程吧. 本菜鸡之前还是SU小白时(俗称白斩鸡,并且现在也很白),只会死命地用推拉.来到公司初探SU,第一个高层建筑便花费我半个月的时间( ...
- Spring boot项目(问答网站)之timeline的推拉两种模式
Timeline介绍 所谓timeline就是当用户打开主页看到的随着时间轴发生的一系列时间的整合,主要包含: 关注用户的最新动态 热门推荐 广告推荐整合等等. 推.拉模式 推模式: 当一个用户关注了 ...
- 坯子库曲面推拉教程_一招曲面流动,搞定99%异形建模
曲面流动可以建什么模型? 这样的▼ 这样的▼ 还有这样的▼ 那究竟如何使用曲面流动呢? 本文告诉你! 曲面流动是什么? 曲面流动功能相当于Rhino(犀牛)中的"沿曲面流动"命令, ...
- 坯子库曲面推拉教程_坯子助手下载_坯子助手最新版下载-下载之家
坯子助手官方版是一款相当实用的SketchUp入门级中文插件集合,坯子助手官方版功能全面,提供了绘制墙体.参数开窗等建筑构件,还支持修复直线.选连续线等线面工具,线转柱体.z轴放样.模型切 ...
- php微博互粉,PHP+redis实现微博的拉模型案例详解
本文实例讲述了PHP+redis实现微博的拉模型.分享给大家供大家参考,具体如下: 上回写了一篇推模型的内容,这回分享一篇拉模型的内容. 拉模型 拉模型就是展示微博的时候,获取自己的所有关注的人,然后 ...
- 【sketchup 2021】草图大师的基础使用【矩形与橡皮擦、直线与圆、手绘线与多边形、圆形与扇形、推拉工具】
文章目录 矩形与橡皮擦 矩形普通使用 矩形进阶使用[画立面和分割面] 擦除 连续擦除 选中后不想删除了... 隐藏/显示边线 柔化边线/取消柔化 另一种擦除:删除 直线与圆 直线工具 鼠标绘制 默认绘 ...
- su联合推拉使用方法_联合推拉插件讲解 - 草图大师—景观元素建模教程 - 园林景观设计学院 - 勤学网...
课时:71节课 时长:20.5小时 课级:全部等级 在很多人认为,景观指的是绿植物和建筑小品,其实不然,笼统的来说,景观是一个社区的概念,可以笼统的说组成社区的所有元素都可以做为景观来营造,都是景观的 ...
- 地推拉新项目怎么赚钱?揭秘日入1k的玩法
5月份,团队开始测试地推拉新,被动收益稳定在每个月3w左右,忙完618后,就开始决定放大这个项目. 在7月18日-7月24日,我们召集了训练营中对地推项目感兴趣的小伙伴,组织了第一期线下实战内训. 说 ...
最新文章
- 揭开AS程序的外纱(四) -- 全屏模式的那点事
- PPTV Docker集群的网络方案选型
- IDEA或Webstorm设置Terminal终端字体大小
- js点击按钮改变字体大小并给他颜色_如何在Elementor中修改文本的字体、大小、颜色、样式...
- 开发自测,到底该从哪里做起?
- Python入门--字符串的切片操作
- svpwm矢量控制电机相电压波形_SVPWM调制中的6个非零基础电压矢量的幅值到底是Udc还是2/3Udc ? 电压利用率为什么是1?...
- 404终结者——IPFS新引擎Filenet
- hive和hadoop版本对应关系
- Issue during builing Android app with ant under Win7
- RSS订阅微信公众号初探-feed43
- python进行谱曲_使用LSTM-GAN为歌词谱曲
- 网上音乐商店项目文档
- Linux jar包在screen开机自启
- IntelliJ IDEA 破解方法
- SQL执行顺序与书写顺序
- caffe 搭建参数服务器(1)—— 用MPI实现多节点同时训练一个模型
- 微信电脑版收到的表格文件保存到桌面打开后不显示内容
- mysql 8.0 警告日志大量写入sha256_password‘ is deprecated and will be removed
- java在浏览器闪退_浏览器连续闪退,作者来看看错误日志