`
01404421
  • 浏览: 228650 次
  • 性别: Icon_minigender_1
  • 来自: 西安
文章分类
社区版块
存档分类
最新评论

观察者模式:犀利哥与ShouShou从基础学习Observable&Observer

阅读更多

最近在学习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() {    //设置修改状态为true
	changed = 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的汉字不让发...

8
4
分享到:
评论
11 楼 Caixiaopig 2010-03-19  
被标题党吸引了~
10 楼 xzhenwangjie 2010-03-18  
犀利大神~
9 楼 yaofeng911 2010-03-18  
   
8 楼 skyuck 2010-03-18  
好吧 我承认我是来看犀利哥的
7 楼 leesonhomme 2010-03-17  
看见犀利哥还有shoushou才进来的
6 楼 banfry 2010-03-15  
内容和模式都很新颖。。
5 楼 jhq198 2010-03-13  
犀利哥把我引来的 谢谢了
4 楼 xiaoyiz 2010-03-12  
那犀利的眼神中带着一丝忧郁,那唏嘘的胡渣子透出来的性感,没错,那就是传说中的“犀利哥”
。。。冲着 “犀利哥” 来的
3 楼 dongya1987 2010-03-12  
我是冲着犀利哥来的
2 楼 5xnl 2010-03-12  
好东东,赞一个。
1 楼 avanry 2010-03-11  
很shou,很犀利

相关推荐

    shoushou_satn5o_shoushou_回收小程序_回收_小程序.zip

    【标题】:“shoushou_satn5o_shoushou_回收小程序_回收_小程序.zip”这一标题表明我们正在处理一个与“回收小程序”相关的压缩文件。这个文件可能包含了一个用于处理回收服务的小程序的全部代码和资源。"shoushou...

    shoushou_satn5o_shoushou_回收小程序_回收_小程序_源码.zip

    【标题】:“shoushou_satn5o_shoushou_回收小程序_回收_小程序_源码.zip”这个标题暗示我们正在处理一个与回收业务相关的小程序源代码。小程序是一种轻量级的应用,通常用于提供特定服务,无需用户下载安装即可在...

    shoushou-master(1)_微信小程序模板js代码前台前端H5页面源码.zip

    【标题】"shoushou-master(1)_微信小程序模板js代码前台前端H5页面源码.zip" 提供的是一款基于微信小程序的前端源码模板,主要用于构建H5页面。这个项目可能包含了一系列用于构建交互式用户界面的JavaScript代码,...

    图书超市.rar

    shoushou" insert into users values "qingshou2" "shoushou2" insert into book values "孙悟空与林黛玉不得不说的故事" 1 50 20 insert into book values "猪八戒与...

    物品回收微信小程序.rar

    这个项目的源代码被存放在名为"shoushou-master"的压缩包中,表明这是一个开源项目,用户可以下载并根据自己的需求进行定制和二次开发。 在微信小程序的开发过程中,主要涉及以下几个重要的知识点: 1. 微信小程序...

    物品回收微信小程序源码+截图

    总的来说,这个物品回收微信小程序源码为开发者提供了一个完整的解决方案,涵盖从用户界面到后台逻辑的全部实现,开发者可以通过深入研究源码,学习微信小程序开发的实践案例,同时也可以根据业务需求进行定制化开发...

    商密234源码C语言,商密234源码C语言,商密234源码C语言,商密234源码C语言

    7. **标准兼容性**:学习如何使C语言实现的代码符合相关的国家和国际加密标准,以便与其他系统进行互操作。 通过对"商密234源码C语言"的深入研究,不仅可以提升密码学理论知识,还能增强实际编程技能,为参与安全...

    微信小程序 生活服务 物品回收 (源代码+截图)

    微信小程序 生活服务 物品回收 (源代码+截图)微信小程序 生活服务 物品回收 (源代码+截图)微信小程序 生活服务 物品回收 (源代码+截图)微信小程序 生活服务 物品回收 (源代码+截图)微信小程序 生活服务 物品...

    小程序源码 生活服务(带后台)

    小程序源码 生活服务(带后台)小程序源码 生活服务(带后台)小程序源码 生活服务(带后台)小程序源码 生活服务(带后台)小程序源码 生活服务(带后台)小程序源码 生活服务(带后台)小程序源码 生活服务(带...

    USB3.0_PPT_628.rar

    这份PPT可以作为学习USB 3.0基础知识和深入理解的参考资料,对于电子工程师、硬件爱好者、电脑用户以及IT专业人员来说都非常有价值。通过深入学习,我们可以更好地理解和利用USB 3.0技术,从而提高工作效率,享受更...

    USB协议详解,USB协议详解,USB协议详解

    USB协议历经多次迭代,从USB 1.1到USB 2.0,再到USB 3.0,不断提升传输速度和功能。 1. USB 1.1与USB 2.0的区别 USB 1.1是USB协议的早期版本,最大传输速率为12Mbps(全速模式),而USB 2.0在2004年发布,将最大...

    USB3.0spec中文译本.rar

    USB3.0还引入了SuperSpeed模式,这个模式下设备间的数据交换更加高效,减少了数据延迟。同时,USB3.0接口支持全双工通信,允许数据同时在两个方向上传输,而USB2.0则是半双工,即同一时间只能单向传输数据。 在电源...

    css设计指南第三版

    本书会详细探讨盒模型,这是CSS布局的基础,包括内容(content)、内边距(padding)、边框(border)和外边距(margin)。理解盒模型对于创建响应式和自适应布局至关重要。此外,还会介绍浮动(float)和定位(positioning)...

    Vmware DRS 配置

    在具体使用DRS之前,需要了解VMware Infrastructure的基础架构以及它与分布式服务的交互原理。VMware ESX Server是虚拟化环境的核心组件,它直接安装在物理服务器硬件上,提供了一个强大的虚拟化层。该层负责把物理...

    asm1352data sheet

    ASM1352数据表 ASM1352是一个USB3.1到双SATA桥接控制器, 由ASMedia技术公司设计和制造。该公司是台湾的一家知名半导体公司,专门生产各种USB控制器、SATA控制器和其他相关产品。 ASM1352是一款高性能的USB3.1到双...

    7816-4协议标准

    7816-4协议,需要拿去用,资料真实,可信,

    USB3.0 中文资料,CY3014中文资料

    USB3.0 中文资料,CY3014中文资料,USB3.0 中文资料,CY3014中文资料,USB3.0 中文资料,CY3014中文资料,密码123456

    商密SM4-SM3-SM2FPGA实现源码.rar

    SM4/SM3/SM2商用密码算法FPGA实现下载

Global site tag (gtag.js) - Google Analytics