在设计一组依赖的对象与它们所依赖的对象之间一致(同步)的交互模型时, 观察者模式(Observer Pattern)很有用。它可以使依赖对象的状态与它们所依赖的对象的状态保持同步。这组依赖的对象指的是观察者(Observer),它们所依赖的对象 称为主题(Subject)。为了实现观察者(Observer)与主题(Subject)的状态保持同步,应使用观察者模式。
观察者(Observer)模式是对象的行为型模式,又叫做发表-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-收听者(Source/Listener)模式或从属者(Dependents)模式。推荐采用发布者--订阅者(publisher--subscriber)模型,以使这组观察者和主题对象之间有清晰的界限, 对应关系为: publisher--subscriber -->> Subject--Observer。
常见的实例有:订阅模式(RSS),数据更新操作update(),Android端常用的广播模式
在观察者模式里有如下的角色:
抽象主题(Subject)角色:
主题角色是将观察者的对象的引用保存在一个集合或者列表中;每个主题都可以有任何数量的观察者。主题提供一个接口可以添加观察者和删除观察者的操作;主题角色又叫做抽象被观察者(Observable)角色;
抽象主题角色,有时又叫做抽象被观察者角色(Observable),采用接口或者抽象类来实现。
抽象观察者(Observer)角色:
这个角色就是用来实现数据更新行为的,是为观察者提供接口的行为,得到操作的请求后更新自己。
抽象观察者角色,可以用一个抽象类或者一个接口实现;在具体的情况下也不排除使用具体类实现。
具体主题(ConcreteSubject)角色:
保存对具体观察者对象有用的内部状态;在这种内部状态改变时给其观察者发出一个通知;具体主题角色又叫作具体被观察者角色;
具体主题角色,通常用一个具体子类实现。
具体观察者(ConcreteObserver)角色:
保存一个指向具体主题对象的引用;和一个与主题的状态相符的状态。具体观察者角色实现抽象观察者角色所要求的更新自己的接口,以便使本身的状态与主题的状态自恰。
1) Subject 需要为注册和注销观察者分别提供一个接口(如add, remover接口)。
2) Observer(观察者)需要提供一个可以从Subject接受通知的接口(如update,operate接口)。
Subject在状态发生变化时,利用改接口通知相应的Observer。
3) 下面的两点也需要满足:
(1) 拉模型(In the pull model)--主题需要提供一个接口,可以使观察者查询主题获得需要的状态信息来更新自己的状态。
(2) 推模型(In the push model)--主题发送观察者可能关注的状态信息。
<span style="font-family:Courier New;font-size:18px;">public interface OberverMethod { void updata(); }</span>
<span style="font-family:Courier New;font-size:18px;color:#6633ff;background-color: rgb(255, 255, 255);">public class ObjectOne implements OberverMethod{ public void updata() { System.out.println("车号:闽AAA888,有大量的现金和黄金,保安提高警惕,贴身保护"); } }</span>
2)盗贼角色
<span style="background-color: rgb(255, 255, 255);"><span style="font-family:Courier New;font-size:18px;color:#6633ff;">public class ObjectTwo implements OberverMethod{ public void updata() { System.out.println("车号:闽AAA888,有大量的现金和黄金,注意随时准备炸弹,大干一票"); } }</span></span>3)盗贼角色
<span style="font-family:Courier New;font-size:18px;color:#6633ff;">public class ObjectThress implements OberverMethod{ public void updata() { System.out.println("车号:闽AAA888,有大量的现金和黄金,特警提高警惕,贴身保护"); } }</span>
<span style="font-family:Courier New;">public interface Watche { void addWatcher(OberverMethod watcher); void removeWatcher(OberverMethod watcher); void notifyWatchers(); }</span>
<span style="font-family:Courier New;">public class OperacteWatche implements Watche { private List<OberverMethod> list = new ArrayList<OberverMethod>(); public synchronized void addWatcher(OberverMethod watcher){ if(watcher == null){ throw new NullPointerException(); } if(!list.contains(watcher)){ list.add(watcher); } } public void removeWatcher(OberverMethod watcher) { list.remove(watcher); } public void notifyWatchers() { if(list != null && list.size() > 0){ for(OberverMethod entity : list ){ if(entity != null){ entity.updata(); } } } } }</span>5.测试
<span style="font-family:Courier New;">Watche watche = new OperacteWatche(); OberverMethod method1 = new ObjectOne(); OberverMethod method2 = new ObjectTwo(); OberverMethod method3 = new ObjectThress(); System.out.println("全部通知"); watche.addWatcher(method1); watche.addWatcher(method2); watche.addWatcher(method3); watche.notifyWatchers(); System.out.println("只通知保安和警察"); watche.removeWatcher(method2); watche.notifyWatchers();</span>
全部通知 车号:闽AAA888,有大量的现金和黄金,保安提高警惕,贴身保护 车号:闽AAA888,有大量的现金和黄金,注意随时准备炸弹,大干一票 车号:闽AAA888,有大量的现金和黄金,特警提高警惕,贴身保护 只通知保安和警察 车号:闽AAA888,有大量的现金和黄金,保安提高警惕,贴身保护 车号:闽AAA888,有大量的现金和黄金,特警提高警惕,贴身保护
在Java语言的java.util库里面,提供了一个Observable类以及一个Observer接口(Java.util.Observer/ Java.util.Observable),构成Java语言对观察者模式的支持。我们只需要直接实现他们就可以。
Observer接口
这个接口只定义了一个方法,update()。当被观察者对象的状态发生变化时,这个方法就会被调用。这个方法的实现应当调用每一个被观察者对象的notifyObservers()方法,从而通知所有的观察对象。
<span style="font-family:Courier New;font-size:18px;color:#6600cc;"></span><pre style="background-color:#2b2b2b;color:#a9b7c6;font-family:'Courier New';font-size:13.5pt;"><span style="color:#629755;"><em></em></span><pre style="background-color:#2b2b2b;color:#a9b7c6;font-family:'Courier New';font-size:13.5pt;"><span style="color:#ccc92c;">package </span>java.<span style="background-color:#344134;">util</span><span style="color:#cc7832;">;</span>/** * { @code Observer} is the interface to be implemented by objects that * receive notification of updates on an { @code Observable} object. * * @see Observable */ public interface Observer { /** * This method is called if the specified { @code Observable} object's * { @code notifyObservers} method is called (because the { @code Observable} * object has been updated. * * @param observable * the { @link Observable} object. * @param data * the data passed to { @link Observable#notifyObservers(Object)}. */ void update(Observable observable , Object data) ;}
Observable类
直译:观察是用来通知一组观察对象时,一个变化
发生。在创作上,一系列的观察者是空的。发生变化后,
应用程序可以调用{@链接# notifyobservers() }方法。这将
因为所有的注册代码update() } {”方法的调用
观察员。未指定调用顺序。这个实现会
称为观察员的命令,他们注册。子类是完全
免费的,他们称之为更新方法。
从源码中可以看出,被观察者类都是Java.util.Observable的子类,java.util.Observable提供了公开的方法来提供支持观察者对象,常规的方法有addObserver(Observer);deleteObserver(Observer );以及通知更新的方法notifyObservers(); 在使用的使用同时要注意一个特别重要的方法,setChanged();在通知更新前如果不调用这个方法的话就不会执行notifyObservers()方法,所以不会出现更新操作。由于该方法是保护的方法,所以我们通常的使用时继承Observable类来执行操作。
package java.util;/** * Observable is used to notify a group of Observer objects when a change * occurs. On creation, the set of observers is empty. After a change occurred, * the application can call the {@link #notifyObservers()} method. This will * cause the invocation of the {@code update()} method of all registered * Observers. The order of invocation is not specified. This implementation will * call the Observers in the order they registered. Subclasses are completely * free in what order they call the update methods. * * @see Observer */public class Observable { List<Observer> observers = new ArrayList<Observer>(); boolean changed = false; /** * Constructs a new {@code Observable} object. */ public Observable() { } /** * Adds the specified observer to the list of observers. If it is already * registered, it is not added a second time. * * @param observer * the Observer to add. */ public void addObserver(Observer observer) { if (observer == null) { throw new NullPointerException("observer == null"); } synchronized (this) { if (!observers.contains(observer)) observers.add(observer); } } /** * Clears the changed flag for this {@code Observable}. After calling * {@code clearChanged()}, {@code hasChanged()} will return {@code false}. */ protected void clearChanged() { changed = false; } /** * Returns the number of observers registered to this {@code Observable}. * * @return the number of observers. */ public int countObservers() { return observers.size(); } /** * Removes the specified observer from the list of observers. Passing null * won't do anything. * * @param observer * the observer to remove. */ public synchronized void deleteObserver(Observer observer) { observers.remove(observer); } /** * Removes all observers from the list of observers. */ public synchronized void deleteObservers() { observers.clear(); } /** * Returns the changed flag for this {@code Observable}. * * @return {@code true} when the changed flag for this {@code Observable} is * set, {@code false} otherwise. */ public boolean hasChanged() { return changed; } /** * If {@code hasChanged()} returns {@code true}, calls the {@code update()} * method for every observer in the list of observers using null as the * argument. Afterwards, calls {@code clearChanged()}. * <p> * Equivalent to calling {@code notifyObservers(null)}. */ public void notifyObservers() { notifyObservers(null); } /** * If {@code hasChanged()} returns {@code true}, calls the {@code update()} * method for every Observer in the list of observers using the specified * argument. Afterwards calls {@code clearChanged()}. * * @param data * the argument passed to {@code update()}. */ @SuppressWarnings("unchecked") public void notifyObservers(Object data) { int size = 0; Observer[] arrays = null; synchronized (this) { if (hasChanged()) { clearChanged(); size = observers.size(); arrays = new Observer[size]; observers.toArray(arrays); } } if (arrays != null) { for (Observer observer : arrays) { observer.update(this, data); } } } /** * Sets the changed flag for this {@code Observable}. After calling * {@code setChanged()}, {@code hasChanged()} will return {@code true}. */ protected void setChanged() { changed = true; }}
<span style="font-family:Courier New;font-size:18px;color:#6633ff;background-color: rgb(255, 255, 255);">public class ObjectOne implements Observer{ // public void updata() { // System.out.println("车号:闽AAA888,有大量的现金和黄金,保安提高警惕,贴身保护"); // } public void update(Observable o, Object arg) { System.out.println("车号:闽AAA888,有大量的现金和黄金,保安提高警惕,贴身保护"); } }</span>
<span style="font-family:Courier New;font-size:18px;color:#6633ff;">public class ObjectTwo implements Observer{ public void updata() { System.out.println("车号:闽AAA888,有大量的现金和黄金,注意随时准备炸弹,大干一票"); } public void update(Observable o, Object arg) { System.out.println("车号:闽AAA888,有大量的现金和黄金,注意随时准备炸弹,大干一票"); } }</span>
<span style="font-family:Courier New;font-size:18px;color:#6633ff;">public class ObjectThress implements Observer{ // public void updata() { // System.out.println("车号:闽AAA888,有大量的现金和黄金,特警提高警惕,贴身保护"); // } public void update(Observable o, Object arg) { System.out.println("车号:闽AAA888,有大量的现金和黄金,特警提高警惕,贴身保护"); } }</span>
<span style="font-family:Courier New;font-size:18px;color:#6633ff;">public class OperateObservable extends Observable { @Override public synchronized void addObserver(Observer o) { super.addObserver(o); } @Override protected synchronized void clearChanged() { super.clearChanged(); } @Override public synchronized void deleteObserver(Observer o) { super.deleteObserver(o); } @Override public synchronized void deleteObservers() { super.deleteObservers(); } @Override public void notifyObservers() { super.setChanged(); super.notifyObservers(); } @Override public void notifyObservers(Object arg) { super.notifyObservers(arg); } @Override protected synchronized void setChanged() { super.setChanged(); } }</span>测试:
<span style="font-family:Courier New;font-size:18px;color:#6633ff;">OperateObservable observer = new OperateObservable(); <span style="white-space:pre"> </span>Observer roleone= new ObjectOne(); <span style="white-space:pre"> </span>Observer roletwo = new ObjectTwo(); <span style="white-space:pre"> </span>Observer rolethree = new ObjectThress(); <span style="white-space:pre"> </span>observer.addObserver(roleone); <span style="white-space:pre"> </span>observer.addObserver(roletwo); <span style="white-space:pre"> </span>observer.addObserver(rolethree); <span style="white-space:pre"> </span>System.out.println("全部通知"); //<span style="white-space:pre"> </span>observer.setChanged(); <span style="white-space:pre"> </span>observer.notifyObservers(); <span style="white-space:pre"> </span>observer.deleteObserver(roletwo); <span style="white-space:pre"> </span>System.out.println("只通知保安和警察"); //<span style="white-space:pre"> </span>observer.setChanged(); <span style="white-space:pre"> </span>observer.notifyObservers();</span>
运行结果:
全部通知 车号:闽AAA888,有大量的现金和黄金,特警提高警惕,贴身保护 车号:闽AAA888,有大量的现金和黄金,注意随时准备炸弹,大干一票 车号:闽AAA888,有大量的现金和黄金,保安提高警惕,贴身保护 只通知保安和警察 车号:闽AAA888,有大量的现金和黄金,特警提高警惕,贴身保护 车号:闽AAA888,有大量的现金和黄金,保安提高警惕,贴身保护
观察者模式的效果有以下的优点:
第一、观察者模式在被观察者和观察者之间建立一个抽象的耦合。被观察者角色所知道的只是一个具体观察者列表,每一个具体观察者都符合一个抽象观察者的接口。被观察者并不认识任何一个具体观察者,它只知道它们都有一个共同的接口。
由于被观察者和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。如果被观察者和观察者都被扔到一起,那么这个对象必然跨越抽象化和具体化层次。
第二、观察者模式支持广播通讯。被观察者会向所有的登记过的观察者发出通知。
观察者模式有下面的缺点:
第一、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
第二、如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃。在使用观察者模式是要特别注意这一点。
第三、如果对观察者的通知是通过另外的线程进行异步投递的话,系统必须保证投递是以自恰的方式进行的。
第四、虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎么发生变化的,仅仅知道发生了什么变化而已。