package com.lg.design.obser;import java.util.ArrayList;import java.util.List;public class PublishingHouse { private ListPublishingHouse 有添加和删除订阅者Subscriber的方法,同时在生产报纸时来通知所有的订阅者。 订阅者:Subscriber如下:subscribers=new ArrayList (); public void add(Subscriber subscriber){ if(subscriber!=null && !subscribers.contains(subscriber)){ subscribers.add(subscriber); } } public void delete(Subscriber subscriber){ if(subscriber!=null){ subscribers.remove(subscriber); } } public void produceNewspaper(String newspaper) { notifySubscribers(newspaper); } private void notifySubscribers(String newspaper) { for(Subscriber subscriber:subscribers){ subscriber.receiveNewspaper(newspaper); } } }
package com.lg.design.obser;public interface Subscriber { public void receiveNewspaper(String newspaper); }订阅者是一个接口,订阅到报纸后如何处理不同的订阅者有不同的实现。 如某类订阅者 ASubscriber:
package com.lg.design.obser;public class ASubscriber implements Subscriber{ @Override public void receiveNewspaper(String newspaper) { System.out.println("A receive newspaper:"+newspaper); }}再如某类订阅者 BSubscriber:
package com.lg.design.obser;public class BSubscriber implements Subscriber{ @Override public void receiveNewspaper(String newspaper) { System.out.println("B receive newspaper:"+newspaper); }}测试方法如下:
public static void main(String[] args){ PublishingHouse publishingHouse=new PublishingHouse(); Subscriber a=new ASubscriber(); Subscriber b=new BSubscriber(); publishingHouse.add(a); publishingHouse.add(b); publishingHouse.produceNewspaper("第一天的报纸"); publishingHouse.produceNewspaper("第二天的报纸"); }输出的结果为:
A receive newspaper:第一天的报纸B receive newspaper:第一天的报纸A receive newspaper:第二天的报纸B receive newspaper:第二天的报纸观察者模式其实很简单,就是报社维护了一个订阅者集合,一旦有新的报纸,就会去遍历那个集合,调用集合里元素的通用方法receiveNewspaper(接口方法)。报社的produceNewspaper方法就是将上述过程进行了封装而已。 上述的例子很简单,同时有很多问题。下面来说说JDK自身已实现的观察者模式: 被观察者Observable(如报社),观察者Observer(如订阅者) 观察者Observer如下:
public interface Observer { void update(Observable o, Object arg);}被观察者Observable如下:
package java.util;public class Observable { private boolean changed = false; private Vector obs; public Observable() { obs = new Vector(); } public synchronized void addObserver(Observer o) { if (o == null) throw new NullPointerException(); if (!obs.contains(o)) { obs.addElement(o); } } public synchronized void deleteObserver(Observer o) { obs.removeElement(o); } public void notifyObservers() { notifyObservers(null); } public void notifyObservers(Object arg) { /* * a temporary array buffer, used as a snapshot of the state of * current Observers. */ Object[] arrLocal; synchronized (this) { /* We don't want the Observer doing callbacks into * arbitrary code while holding its own Monitor. * The code where we extract each Observable from * the Vector and store the state of the Observer * needs synchronization, but notifying observers * does not (should not). The worst result of any * potential race-condition here is that: * 1) a newly-added Observer will miss a * notification in progress * 2) a recently unregistered Observer will be * wrongly notified when it doesn't care */ if (!changed) return; arrLocal = obs.toArray(); clearChanged(); } for (int i = arrLocal.length-1; i>=0; i--) ((Observer)arrLocal[i]).update(this, arg); } public synchronized void deleteObservers() { obs.removeAllElements(); } protected synchronized void setChanged() { changed = true; } protected synchronized void clearChanged() { changed = false; } public synchronized boolean hasChanged() { return changed; } public synchronized int countObservers() { return obs.size(); }}它使用一个Vector集合来存放所有的Observer,具有添加删除Observer的功能。同时还有一个changed属性,用来标示状态是否发生变化,所以在执行notifyObservers通知前要把该状态改为true,为了保证线程安全,setChanged、hasChanged、clearChanged都加上了synchronized 进行同步。再来详细看下notifyObservers过程: arrLocal作为当时Observers的一个快照,在该方法中对状态的判断和改变进行了同步,然后按照倒序进行了事件的通知,即调用集合中每个Observer。 先说下我对jdk实现的观察者模式的看法: (1)加入changed属性,则需要在每次想要执行通知前,提前设置setChanged即changed=true,然后执行notifyObservers才有效,这使得我们在使用时必须使setChanged方法和notifyObservers方法进行synchronized操作,使之成为“原子操作”,不然在多线程环境中,会通知失败。如线程1执行setChanged,线程2执行setChanged,线程1执行notifyObservers,它就会把changed=false,此时线程2再执行notifyObservers,就会因为changed=false而直接退出,不会发出通知,changed属性的确给我们带来了同步的麻烦,也体现在了notifyObservers方法中,如下:
public void notifyObservers(Object arg) { /* * a temporary array buffer, used as a snapshot of the state of * current Observers. */ 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); }有了changed属性,必须要在该方法中对changed的判断进行同步,否则会造成如下现象:线程1设置了setChanged方法,在执行notifyObservers时还未执行到clearChanged方法,此时线程2不用调用setChanged方法,直接调用notifyObservers,也会实现通知。 (2)Observer接口的方法,void update(Observable o, Object arg);此时传递的是Observable对象,如MyObservable继承了Observable 对象,此时我们要获取MyObservable的数据就必须要对void update(Observable o, Object arg)传进来的Observable o进行强制类型转换,而我们应该可以利用泛型来更好的设计这一个接口,避免强制类型转换。如下:
public interface MyObserver(3)对于Observable的notifyObservers通知顺序,默认写死为倒序通知,不应该是这样的,而是,应该对开开放成接口,同时提供多种实现,如正序通知的实现和倒序通知的实现。如果还满足不了需求,我们就可以自定义顺序实现该接口,设置进Observable中。 (4)对于Observable内部使用Vector,也是不可扩展的一个地方,如果我们想换种集合就没法实现,并不是什么需求都会去使用Vector。 (5)观察者自己应该具有取消关注的权利,即微信中的订阅一样,自己有权利去关注和取消关注,所以有时观察者接口由希望有一个取消关注的方法,然而该方法的实现又很明白,不需要观察者自己来实现。这就使用到了java8中接口中默认方法的实现。如下所示:{ public void update(T t,Object args);}
public interface Observer { void update(Observable o, Object arg); default void unregister(Observable o){ o.deleteObserver(this); } default void register(Observable o){ o.addObserver(this); }}不知道这样写对不对, 所以对于Jdk自身实现的观察者模式扩展性并不好,同时大量使用synchronized来解决多线程问题,没有更好的使用多线程包中的同步技术。观察者模式本身很简单,所以有时候还是需要我们自己来写一个观察者模式。 以上纯属个人拙见,欢迎激烈讨论,共同进步。 若想转载请注明出处: 作者:iteye的乒乓狂魔