JDK Observer 设计模式之研究

    目前设计模式的介绍性文章越来越多,但设计模式的研究性文章仍然比较欠缺,这着实让人觉得有点遗憾。本文旨在抛砖引玉,具体分析一下javajdk自带的observer设计模式(下文如没特别指出,observer设计模式就意指javajdk自带的observer设计模式)的实现。

1.       Observer 设计模式概要

Observer设计模式在GOF里属于行为设计模式。JDK里提供的observer设计模式的实现由java.util.Observable类和java.util.Observer接口组成。从名字上可以清楚的看出两者在Observer 设计模式中分别扮演的角色:Observer是观察者角色,Observable是被观察目标(subject)角色。

Observable是一个封装subject基本功能的类,比如注册observerattach功能),注销observerdetatch功能)等。这些功能是任何一个扮演observerable角色的类都需要实现的,从这一点上来讲,JDK里将这些通用功能专门封装在一个类里,显得合情合理。通常情况下,我们的类只要从Observerable类派生就可以称为observerable角色类,使用非常简单。

2.       使用observer设计模式存在的困难

但我们不得不注意到,在项目实际开发当中,情况往往要复杂得多。java不支持多继承特性在很多时候是阻碍我们使用observer设计模式的绊脚石。比如说,我们设计的一个类已经是某个类的派生类,在这种情况下同时想让它扮演observerable角色将变得麻烦。如何实现“多继承”的效果是摆在我们面前的一大难题。下面我们首先分析一下Observable类。

3.       Observable类“触发通知”的原理

Observable必须“有变化”才能触发通知observer这一任务,这是它的本质体现。查看源码便可知一二。Observerable部分源码如下:

//……省略……

    private boolean changed = false;

//……省略……

public void notifyObservers(Object arg) {

//……省略……

        Object[] arrLocal;

           synchronized (this) {

              //……省略……

               if (!changed)

                return;

            arrLocal = obs.toArray();

            clearChanged();

        }

 

//……省略……

 

    protected synchronized void setChanged() {

           changed = true;

    }

 

    protected synchronized void clearChanged() {

           changed = false;

    }

正如粗的斜体标注部分所示,在notifyObservers(Object arg) 方法里if (!changed) return;语句告诉我们,若changed属性值为false,将直接返回,根本不会触发通知操作。并且我们注意到changed 属性被初始化为false,这将意味着如果我们不主动设置changed属性为true,将不会有任何变化,也就是说根本起不到“通知”作用。因此,设置changed属性的值是我们应用jdk observer 设计模式的关键所在。那么如何才能设置changed属性呢?从源码可以看出,唯一的入口是通过setChanged()。下面我们分析一下changed属性及相关的方法setChanged()clearChanged()

4.       Observable类的分析

Observable#changed属性的初始值为false,这很容易理解,不再详细陈述。细心的读者可能会注意到跟changed属性有关的两个方法setChanged()clearChanged(),它们的修饰符都是protected想强调的是,是protected,而不是public。但这样是否有其必要性和合理性?答案是肯定的。在前面的分析中,我已经提到,setChanged()方法是设置changed的唯一入口,它的修饰符定义为protected,就意味着通过定义Observable的对象,再设置changed属性将变得不可能。从这个意义上说,要想应用observer设计模式,必须继承Observable类方可。关于这一点,下文还会提及。但是,为什么不能定义成public?这似乎难以理解。因为定义成public,我们不就可以很方便地设置changed属性的值吗?为了弄清楚这个问题,我们还是看一下Observable里的相关的代码:

//……省略……

    public void notifyObservers(Object arg) {

//……省略……

        for (int i = arrLocal.length-1; i>=0; i--)

            ((Observer)arrLocal[i]).update(this, arg);

}

 

这段代码表达的意思是说找出所有已注册的Observer,再逐个进行“通知”,通过调用Observer#update(Observable,Object)方法进行通知。我们看到,update 第一个参数是this,我们同时还必须注意到,这段代码是Observable类里的代码。这就相当于是在一再强调,发出“通知”的,必须是observable自己(Observable类或者其派生类),其它任何类都不行。这就意味着我们的observable类继承Observable类是必要的,因为如果不继承,而采用组合的话,将无法保证能传递好this。换句话说,采用组合的方式使用Observable类,将变得几乎没有任何意义。同时,修饰符定义为protected,可以确保是在Obsrvable里进行触发通知的,不会在其它任何地方进行通知,这显得内敛性很强。如果将setChanged()修饰符定义为public,将无法保证正确“传递this”的硬性要求,这不符合“只有observalbe才能直接或间接通知observer”这一observable设计模式的硬性要求。由此我们可见一斑,jdk的很多理念的思想性是多么的强。

5.       解决使用observer设计模式存在的困难

借助adapter设计模式(详见本人发表的adapter设计模式相关文章)和java支持多接口特性基本可以解决“多继承”问题。基本思想是结合继承/实现和组合来达到效果。在上面的分析中,我们已经知道,Observable类必须继承使用,不能组合使用,因此我们只需要将需扮演成observerable角色的类装扮成adapter角色,将该类原继承的类装扮成adaptee角色即可。示例代码如下:

//欲充当observable角色的类的原来的代码:

public class MyObject extends BaseObject {

  public MyObject() {

public void method1(){}

  }

 

}

 

//充当observable角色后的代码:

public class MyObject extends Observable {

  private BaseObject baseObject = null;

  public MyObject(BaseObject baseObject) {

    this.baseObject = baseObject;

  }

}

6.       注意事项:

如果上例中的BaseObject也用到需要传递“this”的方法,那么上面的组合使用方法将有可能失效。这种情况是最糟糕的情况。此时可以考虑在BaseObject类这些“瓶颈”地方尽量采用接口代替类(包括抽象类)来解决。

 

关于作者

原名 顾乃忠, 现为广州一IT公司的高级技术人员,2001年大学毕业。专注JAVAC++相关技术的学习和研究。您可以通过 gunzh@126.com gunzh(beyondsoul) 联系。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
环境:Windows XP professional, JDK 1.6, Eclipse 3.3 Europa. 我们说学习Java应该从Swing开始,那么学习Swing最重要的思想就是对于观察者模式的理解(Observer Pattern)。因为,该设计模式Java Swing框架中贯穿了始终。对于C#的委托、代理概念所使用的Callback(回调模式--参见本人的“第一个C#程序Hello World ”资源)来看,Java Swing编程是纯粹的OO语言、是一种非常优雅的编程语言(本人认为)--而C#的学习好像非常强调所谓的OO思想,但是不是知道是什么OO思想?(个人观点,请勿激动)。 该示例是翻写一老外Observer Pattern的例子,本人觉得该示例是对该模式的非常精典的说明,于是花点功夫来翻写它,因为他的代码没有注释说明该模式的设计思想怎样体现在代码中去,所以,本人结合代码注释了关键代码的中文意义说明作者怎样表示该设计模式的,以方便Java程序员阅读和理解代码,从而理解该设计模式的思想(本人的注释非常详尽)。 目的,本人认为,作为Java程序员--不是指只会使用JBuilder(或者Websphare等)工具拖拽的java操作员--我们指至少能使用Eclipse工具书写Swing代码的Java编程员,或者使用ant工具编程的Java程序员,应该熟练掌握该设计模式!因为该设计模式贯穿Swing框架的始终。 阅读对象:本锦江中心中从S1阶段就想成为一个Java程序员的学员,并且在S1阶段已经非常认真的学习Java编程的学员。 注:不过该代码应该是本锦江中心Y2阶段Java方向学习的学员的参考代码,因为,该代码是由Y2阶段本教员会讲解。
工厂系列模式的优缺点: 让用户的代码和某个特定类的子类的代码解耦 用户不必知道它所使用的对象是怎样创建的,只需知道该对象有哪些方法 抽象工厂模式可以为用户创建一系列相关的对象,使用户和创建这些对象的类脱耦 MVC模式是不是一种设计模式?为什么 MVC不是设计模式,应该是框架/架构模式,因为它的定义是抽象的,没有足够的细节描 述使你直接去实现,而只能根据MVC的概念和思想,用几个设计模式组合实现。 举出一个生活中使用装饰者模式的例子,用程序实现思路 举个生活中的例子,俗话说"人在衣着马在鞍",把这就话用装饰者模式的语境翻译一下 ,"人通过漂亮的衣服装饰后,男人变帅了,女人变漂亮了;"。对应 上面的类图,这里人对应于ConcreteComponent,而漂亮衣服则对应于ConcreteDecorato r; 设计模式如何分类,每一个类别都有什么特征? 设计模式分为3类,分别是:创建型模式、行为型模式、结构型模式。 创建型特点:避免用户直接使用new运算符创建对象。 行为型特点:怎样合理的设计对象之间的交互通信,以及怎样合理的为对象分配职 结构型特点:主要用于处理类或对象的组合 Java jdk中使用了哪些设计模式 1.单例2.静态工厂3.工厂方法4.抽象工厂5.构造者6.原型7.适配器8桥接9.组合10.装 饰器11.外观12.享元14.代理15.迭代器16.观察者17.协调者18.模板方法19.策略20.责 任链21.命令22.空对象25.解释器 面向对象的设计原则有哪些? 开闭原则、面向抽象的原则(依赖倒转原则)、多用组合少用继承原则、高内聚- 低耦合原则。 观察者模式的推拉有什么不同?使用场景 推,具体主题将变化后的数据全部交给具体观察者。场景:当具体主题认为具体观察者 需要这些变换后的数据时,往往采用推数据方式; 拉,具体主题不将变化后的数据交给具体观察者,而是提供获得这些数据的方法。场景 :当具体主题不知道具体观察者是否需要这些变换后的数据时,往往采用拉数据的方式 。 策略模式和工厂模式有什么不同? 策略模式定义了一系列算法,将他们一个个封装,并且他们之间可以相互替换; 工厂模式定义一个创建对象的接口,让子类决定实例化哪一个类 5观察者模式的推拉有什么不同?适用场景 现在要说的分歧在这里: "推"的方式是指,Subject维护一份观察者的列表,每当有更新发生,Subject会把更新 消息主动推送到各个Observer去。 "拉"的方式是指,各个Observer维护各自所关心的Subject列表,自行决定在合适的时间 去Subject获取相应的更新数据。 "推"的好处包括: 1、高效。如果没有更新发生,不会有任何更新消息推送的动作,即每次消息推送都发生 在确确实实的更新事件之后,都是有意义的。 2、实时。事件发生后的第一时间即可触发通知操作。 3、可以由Subject确立通知的时间,可以避开一些繁忙时间。 4、可以表达出不同事件发生的先后顺序。 缺点:精确性较差,不能保证能把信息送到客户器。 "拉"的好处包括: 1、如果观察者众多,Subject来维护订阅者的列表,可能困难,或者臃肿,把订阅关系 解脱到Observer去完成。 2、Observer可以不理会它不关心的变更事件,只需要去获取自己感兴趣的事件即可。 3、Observer可以自行决定获取更新事件的时间。 4、拉的形式可以让Subject更好地控制各个Observer每次查询更新的访问权限。缺点: 不能够及时获取系统的变更。 6策略模式和工厂模式有什么不同? 一般情况下,策略模式是为了解决的是策略的切换与扩展,更简洁的说是定义策略族, 分别封装起来,让他们之间可以相互替换,策略模式让策略的变化独立于使用策略的客 户。可以应用的场景有优惠系统、工资计算系统等。而工厂模式主要解决的是资源的统 一分发,将对象的创建完全独立出来,让对象的创建和具体的使用客户无关。主要应用 在多数据库选择,类库文件加载等。很明显的是策略模式是开放的,作为一个主体你的 活动范围是全程的,大多数事情要你自己亲力亲为。而工厂模式作是封闭的,作为主体 的你的活动范围是有限的,很多事情都帮组你做好了,你直接"点"就可以了。 7装饰者模式和适配器模式的比较 1.关于新职责:适配器也可以在转换时增加新的职责,但主要目的不在此。装饰者模 式主要是给被装饰者增加新职责的。 2.关于原接口:适配器模式是用新接口来调用原接口,原接口对新系统是不可见或者 说不可用的。装饰者模式原封不动的使用原接口,系统对装饰的对象也通过原接口来完 成使用。(增加新接口的装饰者模式可以认为是其变种--"半透明"装饰者) 3.关于其包裹的对象:适配器是知道被适配者的详细情况的(就是那个类或那个接口 )。装饰者只知道其接口是什么,至于

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值