最近在学习javaAPI,看到了Observer和Observable,为了加深理解,写了个简单的例子。

Observer,从字面意思来理解,就是对象服务,声明了一个方法:

void update(Observable o, Object arg);

这个方法,在Observable的类中用setchanged()和notifyObservers(Object)才能被调用,而不需要我们自己编码来调用,实现了“自动监视”的效果。

Observable是一个Class,使用void addObserver(Observer o)方法可以为自己添加观察者,一个Observable可以注册多个Observer,在Observable中是用一个Vector来存储的。

Observable共有两个属性,除去存储Observable的Vector属性,还有一个boolean型的changed属性,来记录是否发生改变。

Observer和Observable的典型使用如下:

public class SomeThing extends Observable{    //被观察对象public void someThingHappen(){this.setChanged();           //设置改变状态this.notifyObservers();    //通知所有已注册的观察者     }
}public class MyObserver implements Observer {     //观察者public void update(Observable o, Object arg) {System.out.println("in update");}
}
public class Test {public static void main(String args[]){      SomeThing st=new SomeThing();//实例化被观察对象MyObserver mo=new MyObserver ();//实例化观察者st.addObserver(mo);//为被观察对象 注册一个观察者st.someThingHappen();//被观察者执行操作}
}

这样会输出“in update”。有点神奇吧。我们来看看Observable的源码。

private boolean changed = false;    //是否修改
private Vector obs;    //保存观察者的数组protected synchronized void setChanged() {    //设置修改状态为truechanged = true;
}public synchronized void addObserver(Observer o) {   //添加观察者if (o == null)throw new NullPointerException();if (!obs.contains(o)) {obs.addElement(o);}}public void notifyObservers() {             //通知观察者notifyObservers(null);}public void notifyObservers(Object arg) {   //通知观察者Object[] arrLocal;synchronized (this) {if (!changed)return;arrLocal = obs.toArray();clearChanged();}for (int i = arrLocal.length-1; i>=0; i--)                 //注意这里的循环次序((Observer)arrLocal[i]).update(this, arg);
}//....其他代码

从这里不难看出,为什么我们在Observable中必须先调用setChanged() 方法,再调用notifyObservers()方法了。

Observable类有两个私有变量。一个boolean型的标志位,setChange()将它设为真,只有它为真时,notifyObservers方法才会调用Observer的update方法,clearChange()设标志位为假,hasChange返回当前标志位的值。另一个是一个Vector,保存着一个所有要通知的Observer列表,addObserver添加Observer到列表,deleteObserver从列表中删除指定Observer,deleteObservers清空列表,countObservers返回列表中Observer的数目,在Observer对象销毁前一定要用deleteObserver将其从列表中删除,不然因为还存在对象引用的关系,Observer对象不会被垃圾收集,造成内存泄漏,并且已死的Observer仍会被通知到,有可能造成意料外的错误,而且随着列表越来越大,notifyObservers操作也会越来越慢。Observable的所有方法都是同步的,保证了在一个线程对其标志位、列表进行操作时,不会有其它线程也在操作它。Observable的notifyObservers(Object obj)形式可以再调用update时将参数传进去。

通知顺序通常时越晚加入列表的越先通知。update会被依次调用,由于一个update返回后下一个update才被调用,这样当update中有大量操作时,最好将其中的工作拿到另一个线程或者Observer本身同时也是一个Thread类,Observer先挂起,在update中被唤醒,这样会有一个隐患,Observer线程还没来得及挂起,update就被调用了,通知消息就这样被错过了,一种解决办法是在Observer中实现一个同步的队列结构,并有一个类来封装参数,update实现一个参数类的对象把接收到的通知消息的参数封装在里面,然后把其加进队列,run方法从队列中移除参数对象,并进行处理,这保证了没有通知信息被丢失。

在设计模式中,这一个接口一个Class构成的这种模式叫观察者模式。

为了加深记忆,写一个小例子娱乐一下:

public class Media implements Observer {//我们的观察者String name;String actionName;public Media(String name,String actionName) {this.name = name;//观察者名称this.actionName=actionName;//观察者手段}public Media(String name,String actionName,Observable ob) {this.name = name;this.actionName=actionName;ob.addObserver(this);}public void update(Observable o, Object arg) {SomeThing st=(SomeThing)o;System.out.println(name+actionName+":"+st.name+arg);}
}public class SomeThing extends Observable{//SomeThing,就是我们现在关注的一些东西String name;//名称public SomeThing(String name) {this.name = name;}public void someThingHappen(String action){this.setChanged();this.notifyObservers(action);}
}public class Test {public static void main(String args[]){ SomeThing xilige=new SomeThing("犀利哥");Media someBody=new Media("某网友","上传",xilige);Media net=new Media("网络","疯狂点击",xilige);Media cctv=new Media("CCTV","关注",xilige);xilige.someThingHappen("抽烟啦!");SomeThing ss=new SomeThing("ShouShou");ss.addObserver(cctv);ss.addObserver(net);ss.addObserver(someBody);ss.someThingHappen("出新专辑!");}}

打印结果:

CCTV关注:犀利哥抽烟啦!
网络疯狂点击:犀利哥抽烟啦!
某网友上传:犀利哥抽烟啦!
某网友上传:ShouShou出新专辑!
网络疯狂点击:ShouShou出新专辑!
CCTV关注:ShouShou出新专辑!

大家注意打印的次序,和注册观察者的次序是刚好相反的。

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

观察者模式的优点:

1.在被观察者和观察者之间建立一个抽象的松耦合;

2.支持广播通信。被观察者会向所有登记过的观察者发出通知。

观察者模式也有如下缺点:

1.如果一个被观察者对象有很多直接或间接的观察者的话,将所有的观察者都通知到会花费很多时间;

2.如果在被观察者之间有循环依赖的话,被观察者会触发它们进行循环调用,导致系统崩溃,在使用此模式时应特别注意这一点;

3.如果对观察者的通知是通过另外的线程进行异步投递的话,系统必须保证投递是以自恰的方式进行的;

4.虽然观察者模式可以使观察者知道所观察的对象发生了变化,但是观察者模式没有相应的机制让观察者知道所观察的对象是怎么发生变化的。

在《head First设计模式》一书中有详细的分析,设计模式在于理解,如果JDK提供的这个Observable不能满足或者限制了你的实现,你完全可以自己再实现一个。

另外大家可以注意到Observable的内聚是非常好的,体现了JDK编码的严格和规范,值得我们学习!

本来是汉字,无奈shoushou的汉字不让发...

观察者模式:犀利哥与ShouShou从基础学习ObservableObserver相关推荐

  1. Java基础学习——第六章 面向对象编程(下)

    Java基础学习--第六章 面向对象编程(下) 一.关键词:static 1. static关键字的引入 当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new ...

  2. JAVA基础学习精简心得笔记整理

    JAVA基础学习精简心得笔记整理 配置java环境变量 Java的运行过程  基本数据类型 引用数据类型 逻辑运算符 数组 方法重载 封装 继承 多态 多态的作用 单例设计模式 接口interface ...

  3. 【转】oracle PLSQL基础学习

    [转]oracle PLSQL基础学习 --oracle 练习: /**************************************************PL/SQL编程基础****** ...

  4. python创建对象的格式为_Python入门基础学习(面向对象)

    python基础学习笔记(四) 面向对象的三个基本特征: 封装:把客观事物抽象并封装成对象,即将属性,方法和事件等集合在一个整体内 继承:允许使用现有类的功能并在无须重新改写原来的类情况下,对这些功能 ...

  5. 虚幻引擎虚拟现实开发基础学习教程

    流派:电子学习| MP4 |视频:h264,1280×720 |音频:AAC,44.1 KHz 语言:英语+中英文字幕(根据原英文字幕机译更准确)|大小解压后:3.93 GB |时长:5h 15m 了 ...

  6. 动画产业基础学习教程 Rad How to Class – Animation Industry Fundamentals

    如何分类--动画产业基础 大小解压后:6.2G 含课程素材 1920X1080 mp4 语言:英语+中英文字幕(根据原英文字幕机译更准确) 信息: 绘画技巧.解剖学.角色设计.透视和整体讲故事--这门 ...

  7. Blender纹理基础学习视频教程 CGCookie – Fundamentals of Texturing in Blender

    Blender纹理基础学习视频教程 CGCookie – Fundamentals of Texturing in Blender Blender纹理基础学习视频教程 CGCookie – Funda ...

  8. ue5新手零基础学习教程 Unreal Engine 5 Beginner Tutorial - UE5 Starter Course

    ue5新手零基础学习教程 Unreal Engine 5 Beginner Tutorial - UE5 Starter Course! 教程大小解压后:4.96G 语言:英语+中英文字幕(机译)时长 ...

  9. 0基础学好python难不难_零基础学习Python难不难?Python有什么优势?

    原标题:零基础学习Python难不难?Python有什么优势? Python是一种计算机程序设计语言.首先,我们普及一下编程语言的基础知识.用任何编程语言来开发程序,都是为了让计算机干活,比如下载一个 ...

最新文章

  1. 手势追踪,高通走完其VR一体机的最后一里路
  2. 子恩域名授权系统2.0全解无加密无授权版盗版入库源码
  3. 分享我的Windows live writer 使用经验
  4. oracle修改字符集_oracle修改数据库字符集
  5. 蓝桥杯 ADV-109 算法提高 征税程序
  6. 互联网大牛们的电脑桌面,佩服!
  7. UI自动化,你值得拥有
  8. Linux下建立MySQL数据库,并安装RMySQL包,报错及解决
  9. python播放音乐同步歌词_Python 歌词解析器 音乐与歌词同步播放
  10. Vue源码解读(个人见解 + 网友理解)
  11. 线性表的链式存储结构(C语言实现)
  12. 【Web】CSS(No.21)Css经典案例(三)《爱宠知识》
  13. PhysX Setup
  14. 扩展系统功能——装饰模式(四):透明与半透明装饰模式,装饰模式注意事项,装饰模式总结
  15. 冒泡排序、插入排序、选择排序、希尔排序、堆排序、归并排序等常用排序算法的比较
  16. 苹果快捷键怎么调出来_ps常用10大快捷键
  17. 苹果高调入局中低端市场,国产手机如何“坚守”与“转身”?
  18. python读取多个文件g_Python多行正则表达式+多个条目在一个g中读取一个文件
  19. rpm包中的noarch,i386,i586都是什么意思
  20. 你眼中的程序员------加油程序员

热门文章

  1. 马斯克是如何成为表情包之王的
  2. TORCH 850热风焊台的使用
  3. python高级代码_python高级代码块
  4. CF9h重启的机器码
  5. 成功解决:MDK5编辑器中文乱码问题 以及设置漂亮的代码字体
  6. 如何选择软考中级科目?哪科比较好?
  7. 基于PyTorch+TextCNN实现英文长文本诗歌文本分类
  8. 微生物组-扩增子16S分析研讨会(参加线上直播课还可免费参加线下课 )
  9. linux一步一脚印---stat
  10. android中最新webview的功能,Android WebView实现截长图功能