作者卡奴达摩

连接: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;
}

观察者模式与推拉模型相关推荐

  1. rsync推拉模型及结合inotify实现推模型自动同步

    一.前言 无论使用什么操作系统下,都经常有同步文件的需求,不管发生在本地,还是发生在本地和远程主机之间.那么应该怎么做呢? 使用拷贝类的命令,本地使用cp命令,复制到远程主机使用scp这样的命令,保证 ...

  2. su联合推拉插件_[实习小记一一SU建模]

    鉴于本菜鸡在实习阶段一直在SU建模,那就讲讲我在SU建模的心路历程吧. 本菜鸡之前还是SU小白时(俗称白斩鸡,并且现在也很白),只会死命地用推拉.来到公司初探SU,第一个高层建筑便花费我半个月的时间( ...

  3. Spring boot项目(问答网站)之timeline的推拉两种模式

    Timeline介绍 所谓timeline就是当用户打开主页看到的随着时间轴发生的一系列时间的整合,主要包含: 关注用户的最新动态 热门推荐 广告推荐整合等等. 推.拉模式 推模式: 当一个用户关注了 ...

  4. 坯子库曲面推拉教程_一招曲面流动,搞定99%异形建模

    曲面流动可以建什么模型? 这样的▼ 这样的▼ 还有这样的▼ 那究竟如何使用曲面流动呢? 本文告诉你! 曲面流动是什么? 曲面流动功能相当于Rhino(犀牛)中的"沿曲面流动"命令, ...

  5. 坯子库曲面推拉教程_坯子助手下载_坯子助手最新版下载-下载之家

          坯子助手官方版是一款相当实用的SketchUp入门级中文插件集合,坯子助手官方版功能全面,提供了绘制墙体.参数开窗等建筑构件,还支持修复直线.选连续线等线面工具,线转柱体.z轴放样.模型切 ...

  6. php微博互粉,PHP+redis实现微博的拉模型案例详解

    本文实例讲述了PHP+redis实现微博的拉模型.分享给大家供大家参考,具体如下: 上回写了一篇推模型的内容,这回分享一篇拉模型的内容. 拉模型 拉模型就是展示微博的时候,获取自己的所有关注的人,然后 ...

  7. 【sketchup 2021】草图大师的基础使用【矩形与橡皮擦、直线与圆、手绘线与多边形、圆形与扇形、推拉工具】

    文章目录 矩形与橡皮擦 矩形普通使用 矩形进阶使用[画立面和分割面] 擦除 连续擦除 选中后不想删除了... 隐藏/显示边线 柔化边线/取消柔化 另一种擦除:删除 直线与圆 直线工具 鼠标绘制 默认绘 ...

  8. su联合推拉使用方法_联合推拉插件讲解 - 草图大师—景观元素建模教程 - 园林景观设计学院 - 勤学网...

    课时:71节课 时长:20.5小时 课级:全部等级 在很多人认为,景观指的是绿植物和建筑小品,其实不然,笼统的来说,景观是一个社区的概念,可以笼统的说组成社区的所有元素都可以做为景观来营造,都是景观的 ...

  9. 地推拉新项目怎么赚钱?揭秘日入1k的玩法

    5月份,团队开始测试地推拉新,被动收益稳定在每个月3w左右,忙完618后,就开始决定放大这个项目. 在7月18日-7月24日,我们召集了训练营中对地推项目感兴趣的小伙伴,组织了第一期线下实战内训. 说 ...

最新文章

  1. 揭开AS程序的外纱(四) -- 全屏模式的那点事
  2. PPTV Docker集群的网络方案选型
  3. IDEA或Webstorm设置Terminal终端字体大小
  4. js点击按钮改变字体大小并给他颜色_如何在Elementor中修改文本的字体、大小、颜色、样式...
  5. 开发自测,到底该从哪里做起?
  6. Python入门--字符串的切片操作
  7. svpwm矢量控制电机相电压波形_SVPWM调制中的6个非零基础电压矢量的幅值到底是Udc还是2/3Udc ? 电压利用率为什么是1?...
  8. 404终结者——IPFS新引擎Filenet
  9. hive和hadoop版本对应关系
  10. Issue during builing Android app with ant under Win7
  11. RSS订阅微信公众号初探-feed43
  12. python进行谱曲_使用LSTM-GAN为歌词谱曲
  13. 网上音乐商店项目文档
  14. Linux jar包在screen开机自启
  15. IntelliJ IDEA 破解方法
  16. SQL执行顺序与书写顺序
  17. caffe 搭建参数服务器(1)—— 用MPI实现多节点同时训练一个模型
  18. 微信电脑版收到的表格文件保存到桌面打开后不显示内容
  19. mysql 8.0 警告日志大量写入sha256_password‘ is deprecated and will be removed
  20. java在浏览器闪退_浏览器连续闪退,作者来看看错误日志

热门文章

  1. AI转型中的思考和洞见
  2. Mac字体路径,解决matplotlib中文显示问题,超级好用~
  3. ubuntu禁止指定软件包更新
  4. 面向接口编程VS《倚天屠龙记》里张三丰教无忌打太极
  5. 屏保:毛雷尔玫瑰屏保
  6. RGB图转为灰度图的方法
  7. 炉石传说 历代无面斩杀宇宙龙术
  8. Revit 绘制幕墙系统
  9. 艾克姆 STC15W4K56S4 IAP15W4K58S4 51开发板 大赛esp8266开发板 STC15W4K56S4
  10. brpc源码解析(四)—— Bthread机制