观察者(Observer)模式
观察者(Observer)模式是对象的行为型模式,又叫做发表-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-收听者(Source/Listener)模式或从属者(Dependents)模式。
在观察者模式里有如下的角色:
抽象主题(Subject)角色:
主题角色把所有的观察者对象的引用保存在一个列表里;每个主题都可以有任何数量的观察者。
主题提供一个接口可以加上或撤销观察者对象;主题角色又叫做抽象被观察者(Observable)角色;
抽象观察者(Observer)角色:
为所有的具体观察者定义一个接口,在得到通知时更新自己;
具体主题(ConcreteSubject)角色:
保存对具体观察者对象有用的内部状态;在这种内部状态改变时给其观察者发出一个通知;具体主题角色又叫作具体被观察者角色;
具体观察者(ConcreteObserver)角色:
保存一个指向具体主题对象的引用;和一个与主题的状态相符的状态。具体观察者角色实现抽象观察者角色所要求的更新自己的接口,
以便使本身的状态与主题的状态自恰。
示例摘自:http://blog.csdn.net/legendmohenote/article/details/5762350
其实在JDK中提供了一个Observable类,一个Observer接口,在此基础丰富自己的内容。
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) {
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);
}
}
这里只列出了一些常用的操作,大家如有不明白可以自己看
java.util包下的Observable,每一个方法前都有详细的解释。但是以上列出的方法对于这个例子来说已经够用了。
当我们自己的被观察者继承这个Observable类是,我们就自动的获取到被观察者的一切条件了。很方便是不是,这也是为什么sun要把Observable放到 java.util包中的原因,就是为了方便开发者。
讲完了被观察者再让我们来看看观察者,在上面的代码中有一个addObserver(Observer o)方法,由此我们可以看出来,给被观察者添加观察者时就是用这个方法,那么也就是说观察者一定是Observer的子类或者实现,我们看一下 java.util.Observer吧
public interface Observer {
/**
* This method is called whenever the observed object is changed. An
* application calls an <tt>Observable</tt> object's
* <code>notifyObservers</code> method to have all the object's
* observers notified of the change.
*
* @param o the observable object.
* @param arg an argument passed to the <code>notifyObservers</code>
* method.
*/
void update(Observable o, Object arg);
}
}
很好,这是一个接口,接口中就只有一个方法,update,方法中有两个参数,Observable和一个object,第一个参数就是被观察的对象,而第二个参数就得看业务需求了,需要什么就传进去什么。我们自己的观察者类必须实现这个方法,这样在被观察者调用notifyObservers操作时被观察者所持有的所有观察者都会执行update操作了(当然如果你override这个方法,你甚至可以指定何种情况下只执行某种observer了,是不是比较像责任链模式了)。
那么到这里我们也差不多可以把观察者模式应用到项目中去了。首先让我们来实现被观察者(因为实现被观察者非常简单)
public class MailObserver implements Observer{
/**
* 这个类取名为MailObserver,顾名思义,她是一个用来发送邮件的观察者
*/
public void update(Observable o, Object arg) {
System.out.println("发送邮件的观察者已经被执行");
}
}
public class JMSObserver implements Observer{
public void update(Observable o, Object arg) {
System.out.println("发送消息给jms服务器的观察者已经被执行");
}
}
如上所见,观察者的实现完全跟业务相关。是否复杂就得看你得业务是否复杂了。
接下来让我们再来实现被观察者,示例如下:
public class Subject extends Observable{
/**
* 业务方法,一旦执行某个操作,则通知观察者
*/
public void doBusiness(){
if (true) {
super.setChanged();
}
notifyObservers("现在还没有的参数");
}
public static void main(String [] args) {
//创建一个被观察者
Subject subject = new Subject();
//创建两个观察者
Observer mailObserver = new MailObserver();
Observer jmsObserver = new JMSObserver();
//把两个观察者加到被观察者列表中
subject.addObserver(mailObserver);
subject.addObserver(jmsObserver);
//执行业务操作
subject.doBusiness();
}
}
到此为止,我们已经简单得实现了
观察者模式,让我们来运行一下上面main方法,运行结果如下:
发送消息给jms服务器的观察者已经被执行
发送邮件的观察者已经被执行
有了jdk对 观察者模式的支持,程序员在实现基本的 观察者模式时应该说是易如反掌,这也从一个侧面反应出 观察者模式的地位,它的地位应该是非常重要的
如果上面的例子比较零散,那下面给出一个比较完整的例子
在java api中分别提供了Observable对象:java.util.Observable和Observer接口:java.util.Observer.下面用实例来实现一下观察者模式: 股票系统
所有的类如下:
StockData (Observable对象,也就是所股票数据发生了变化,它就要通知所有和它有关系的交易实体做相应的变化)
BigBuyer (Observer对象, 实现了Observer接口)
TradingFool (Observer对象, 实现了Observer接口)
StockQuote 测试类
在这个例子中一旦StockData对象的状态发生了变化,那BigBuyer和TradingFool都应该受到通知:
package com.zhj.observer1; import java.util.Observable; public class StockData extends Observable { private String symbol; private float close; private float high; private float low; private long volume; public void setStockData(String symbol, float close, float high, float low, long volume){ this.symbol = symbol; this.close = close; this.high = high; this.low = low; this.volume = volume; sendStockData(); } //setChanged();说明有变更,notifyObservers();通知注入过的观察者调用update方法 public void sendStockData() { setChanged(); notifyObservers(); } 。。。。这里省略setter getter方法 } 观察者BigBuyer.java: package com.zhj.observer1; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.util.Observable; import java.util.Observer; public class BigBuyer implements Observer { private String symbol; private float close; private float high; private float low; private long volume; //在初始化构造的时候,传入被观察者对象,将本对象加入到观察者列表中 public BigBuyer(Observable observable){ observable.addObserver(this); } @Override public void update(Observable observable, Object arg) { if(observable instanceof StockData) { StockData stockData = (StockData) observable; this.symbol = stockData.getSymbol(); this.close = stockData.getClose(); this.high = stockData.getHigh(); this.low = stockData.getLow(); this.volume = stockData.getVolume(); display(); } } public void display() { DecimalFormatSymbols dfs = new DecimalFormatSymbols(); DecimalFormat volumeFormat = new DecimalFormat("###,###,###,###",dfs); DecimalFormat priceFormat = new DecimalFormat("###.00",dfs); system.out.println("Big Buyer reports... "); System.out.println("\tThe lastest stock quote for " + symbol + " is:"); System.out.println("\t$" + priceFormat.format(close) + " per share (close)."); System.out.println("\t$" + priceFormat.format(high) + " per share (high)."); System.out.println("\t$" + priceFormat.format(low) + " per share (low)."); System.out.println("\t" + volumeFormat.format(volume) + " shares traded."); System.out.println(); } } 观察者:TradingFool.java: package com.zhj.observer1; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.util.Observable; import java.util.Observer; public class TradingFool implements Observer { private String symbol; private float close;
public TradingFool(Observable observable) {//在初始化构造的时候,传入被观察者对象,将本对象加入到观察者列表中
observable.addObserver(this);
}
@Override
public void update(Observable observable, Object arg) {
if(observable instanceof StockData) {
StockData stockData = (StockData)observable;
this.symbol = stockData.getSymbol();
this.close = stockData.getClose(); display();
} }
public void display() {
DecimalFormatSymbols dfs = new DecimalFormatSymbols();
DecimalFormat priceFormat = new DecimalFormat("###.00",dfs);
System.out.println("Trading Fool says... ");
System.out.println("\t" + symbol + " is currently trading at $" + priceFormat.format(close) + " per share.");
System.out.println(); }}
客户端:测试端:StokeQuote.javapackage com.zhj.observer1;
public class StokeQuote {
public static void main(String[] args) {
System.out.println();
System.out.println("-- Stock Quote Application --");
System.out.println();
//创建被观察者
StockData stockData = new StockData();
//创建两个观察者
new TradingFool(stockData);
new BigBuyer(stockData);
stockData.setStockData("JUPM",16.10f,16.15f,15.34f,(long)481172);
stockData.setStockData("SUNW",4.84f,4.90f,4.79f,(long)68870233);
stockData.setStockData("MSFT",23.17f,23.37f,23.05f,(long)75091400);
}
}