观察者模式
简介:
出版者+订阅者=观察者模式
Subject
主题接口,也即可观察者(Observable),对象使用此接口注册为观察者,或者把自己从观察着中删除。每个主题可以有多个观察者。
ConcreteSubject
一个具体主题实现了主题接口,除了注册和撤销之外,具体主题还实现了notifyObservers()方法,这个方法用来在主题状态改变时更行所有的观察者。具体主题也可能有设置和获取状态的方法。
Observer
所有潜在的观察者必须实现观察者接口,这个接口只有update()方法,当主题改变时,它被调用。
ConcreteObserver
具体的观察者可以是任何实现了Observer接口的类。观察者必须注册具体主题,一边接收更新。
基本原则:为了交互对象之间的松耦合设计而努力
观察者模式的定义:观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有的依赖者都会收到通知并自动更新。
观察者模式中,一对多的关系体现在哪里?
这个模式中,主题是具有状态的对象,并且可以控制这个状态,也就是说,是一个具有“状态”的主题。
另一方面,观察者使用这些状态,虽然这些状态并不属于他们。有多个观察者,依靠主题来告诉他们主题状态何时变了。
这就产生了一个关系,一个“主题”对多个“观察者”的关系
松耦合设计威力体现在哪里?
当两个对象松耦合,他们依然可以交互,但是不太清楚彼此的细节。观察者模式提供了一种对象设计,让主题和观察者松耦合。
类图:
实例:
主题1:游戏新闻对象:GameMsg
package com.mylearn.designmodel.observer; import java.util.Observable; /** * Created by IntelliJ IDEA. * User: yingkuohao * Date: 13-10-31 * Time: 上午11:58 * CopyRight:360buy * Descrption: * To change this template use File | Settings | File Templates. */ public class GameMsg extends Observable { private String msg; private static String flag = "game"; public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; sendMsg(); } private void sendMsg() { super.setChanged(); //开关 super.notifyObservers(); //通知消费者 } } |
主题2:头条新闻对象:HeadMsg
package com.mylearn.designmodel.observer; import java.util.Observable; /** * Created by IntelliJ IDEA. * User: yingkuohao * Date: 13-10-31 * Time: 上午11:58 * CopyRight:360buy * Descrption: * To change this template use File | Settings | File Templates. */ public class HeadMsg extends Observable { private String msg; private static String flag = "head"; public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; sendMsg(); } private void sendMsg() { super.setChanged(); super.notifyObservers(); //通知消费者,notify方法将遍历Observable中的Vector,找到其中的Observer,调用其update方法。 } } |
观察者A:只订阅游戏新闻
package com.mylearn.designmodel.observer; import java.util.Observable; import java.util.Observer; /** * Created by IntelliJ IDEA. * User: yingkuohao * Date: 13-10-31 * Time: 下午12:02 * CopyRight:360buy * Descrption: * To change this template use File | Settings | File Templates. */ public class ObserverA implements Observer { private Observable observable; public Observable getObservable() { return observable; } public void setObservable(Observable observable) { this.observable = observable; observable.addObserver(this); //加入观察者 ,Observable类中维护了一个Vector,用于保存注册的观察者信息, } /** * 相当于一个锚链接,当Observable中有新消息时,便会通知观察者,调用观察者的update方法。 * * @param o * @param arg */ public void update(Observable o, Object arg) { if (o instanceof GameMsg) { //主动拉消息 String msg = ((GameMsg) o).getMsg(); display(msg); } } public void display(String msg) { System.out.println("oye!get a game msg:" + msg); } } |
观察者B:只订阅头条新闻
package com.mylearn.designmodel.observer; import java.util.Observable; import java.util.Observer; /** * Created by IntelliJ IDEA. * User: yingkuohao * Date: 13-10-31 * Time: 下午12:02 * CopyRight:360buy * Descrption: * To change this template use File | Settings | File Templates. */ public class ObserverB implements Observer { private Observable observable; public Observable getObservable() { return observable; } public void setObservable(Observable observable) { this.observable = observable; observable.addObserver(this); //加入观察者 } public void update(Observable o, Object arg) { if (o instanceof HeadMsg) { String msg = ((HeadMsg) o).getMsg(); display(msg); } } public void display(String msg) { System.out.println("oye!get a head msg:" + msg); } } |
Test类:
package com.mylearn.designmodel.observer; /** * Created by IntelliJ IDEA. * User: yingkuohao * Date: 13-10-31 * Time: 下午12:06 * CopyRight:360buy * Descrption: * To change this template use File | Settings | File Templates. */ public class Test { public static void main(String args[]) { GameMsg gameMsg =new GameMsg(); HeadMsg headMsg =new HeadMsg(); ObserverA observerA =new ObserverA(); ObserverB observerB =new ObserverB(); observerA.setObservable(gameMsg); observerB.setObservable(headMsg); gameMsg.setMsg("dota 更新了!"); headMsg.setMsg("普京当选了!"); } } |
源码:
Observable类:抽象主题对象(即被观察者的父类)
public class Observable { private boolean changed = false; //开关,用于通知观察者 private Vector obs; //定义一个容器,保存所有的观察者
/** Construct an Observable with zero Observers. */ 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(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(); // 当一个主题执行notify方法时,会把changed设为false,这样其他线程就会进不来,除非其他线程执行了setChange方法,所以,我们的实例中调用notify方法时都要先改变changed状态 } //遍历观察者集合,分别执行callback方法,即update方法,这个是由我们的Observer接口约定的 for (int i = arrLocal.length-1; i>=0; i--) ((Observer)arrLocal[i]).update(this, arg); } //由此可以看到我们的notify方法跟Object的notify,wait一点关系都没有,只不过是方法的回调。 |
优点:
主题和观察者之间解耦,主题不用知道具体的观察者是谁,当新加观察者或删除观察者时,主题不用做任何改动,主题和观察者都是可插拔的。
缺点:
1. Observable是一个类而不是一个接口,无法实现多继承。
2. Observable把很多方法如setChanged方法保护起来,我们无法用聚合的方式把Observable聚合到自己定义的对象中来,违反了“多用组合,少用继承”的原则。