观察者模式的定义:是定义对象的一种一对多的依赖关系,当一个对象的状态发生改变的时候,所有依赖于它的对象都得到通知并被自动更新,在这里被观察者是“一".观察者是”多
“
组成:抽象被观察者类Observable(也成之为目标类),具体的被观察者类(extends Observable),抽象的观察者类(Observable),具体的观察者类
在java里要实现观察者模式,就需要实现Observer接口,继承Observable类,其中观察者实现Observer接口,被观察者继承Observable类。其核心源码如下所示:
public class Observable {
private boolean changed = false;
private Vector obs;//持有一系列观察者
public Observable() {
obs = new Vector();
}
/**
* 添加观察者类
* @param o
*/
public synchronized void addObserver(Observer o) {
if (null == o) {
throw new NullPointerException();
}
if (!obs.contains(o)) {
this.obs.add(o);
}
}
/**
* 通知观察者
* @param object
*/
public void notifyObservers(Object object) {
Object[] observers;
synchronized (this) {
//判断是否执行更新操作
if (!this.changed) {
return;
}
observers = obs.toArray();
this.clearChanged();
}
//所有观察者执行更新操作
for (int i = observers.length - 1; i >= 0; i--) {
Observer observer = (Observer) observers[i];
observer.update(this, object);
}
}
}
public interface Observer {
/**
* 当被观察者的对象发生变化时,会调用该方法
* @param o 被观察的对象
* @param args
*/
void update(Observable o,Object args);
}
简单的一个举一个没有实际使用价值的例子:
考虑到如下场景:一个公司的股票(Share),有许多人投资者(Investor)购买,投资者们有一个共同的行为:
1、比较关注股票价格的变动
2、随着股票价格的变动每个投资者都作出自己的响应:卖出,继续持仓,买进等等行为
其实这种场景很适合使用观察者模式,至于为什么适合使用观察者模式,现在举个反例来从侧面说明观察者模式的优点:
假设有三个投资者:InvestorA、InverstorB、InverstorC,具体实现类如下:
public class InvestorA {
public void actionA(float price) {
System.out.println("看这股票价格貌似还有上涨的空间,先不卖继续持有");
}
}
public class InvestorB {
public void actionB(float price) {
System.out.println("内幕消息说这股票前景不乐观啊,卖了走人");
}
}
public class InvestorC {
public void doSomething(float price) {
System.out.println("听王大妈说这股票可以,我也买一股");
}
}
那么股票类如果价格发生变动想通知InvestorA、InverstorB、InverstorC价格发生变化,则必须持有这三个投资者对象的引用,其核心实现如下:
public class Share {
//股票发行价格
private float price = 1024f;
//股东或者投资者A
private InvestorA investorA;
private InvestorB investorB;
private InvestorC investorC;
public void setPice(float newPrice) {
if (this.price != newPrice) {
price = newPrice;
//通知投资者A价格变动
investorA.actionA(newPrice);
//通知投资者B价格变动
investorB.actionB(newPrice);
//通知投资者C价格变动
investorC.doSomething(newPrice);
}
}
public void setInvestorA(InvestorA investorA) {
this.investorA = investorA;
}
public void setInvestorB(InvestorB investorB) {
this.investorB = investorB;
}
public void setInvestorC(InvestorC investorC) {
this.investorC = investorC;
}
public static void main(String args[]) {
Share share = new Share();
share.setInvestorA(new InvestorA());
share.setInvestorB(new InvestorB());
share.setInvestorC(new InvestorC());
share.setPice(101f);
}
}
简单的股票系统实现了,但是该类有如下问题:
1、股票类必须持有各个投资对象的引用,耦合度太高
2、如果再有投资人D、E、F。。。的话,则Share必须再添加D、E、F。。。的引用,在提供响应的set等方法,代码量会激增
扩展性不强。
3、看看setPrice 通知各个类,InvestorA对象执行了actionA()、InvestorB对象执行了actionB()、InvsertorC对象执行了doSomething方法,更恐怖的是后续的投资者可能是执行了methodx(),xx(),yy() 等等乱七八糟的方法(本来是一致的行为,但是执行的方法不一样,导致了代码的臃肿)。且Share对象必须知道这些不同的投资人的这些方法名。
上面三点说明了这是何其糟糕的设计,下面看看用观察者模式的思维实现的上述系统:
观察者模式的实现:
首先设计一个ShareObserver接口,该接口提供了doAction(float newPrice)方法,该接口方法的主要作用就是当股票价格发生改变的时候通知投资者,不同的投资者根据最新股价来做自己的处理,也就是说投资者需要实现该接口。
public interface ShareObserver {
void doAction(float newPrice);
}
以InvestorA为例,作为一个股票的观察者,实现ShareObserver接口后的代码如下所示:
public class InvestorA implements ShareObserver {
@Override
public void doAction(float newPrice) {
System.out.println("看这股票价格貌似还有上涨的空间,先不卖继续持有");
}
}
股票类作为一个被观察者,其类经过修改后如下所示:
public class ShareObserable {
private float price = 100f;
//股票观察者集合类
List shareObservers = new ArrayList();
//添加股票投资者
public void addInverstor(ShareObserver shareObserver) {
shareObservers.add(shareObserver);
}
public static void main(String args[]) {
ShareObserable share = new ShareObserable();
share.addInverstor(new InvestorA());
share.addInverstor(new InvestorB());
share.addInverstor(new InvestorC());
share.updatePrice(101f);
}
public void updatePrice(float newPrice) {
if (this.price != newPrice) {
price = newPrice;
//通知投资者价格变动
for (ShareObserver shareObserver : shareObservers) {
shareObserver.doAction(newPrice);
}
}
}
}
明显感觉到了观察者模式带来的好处:
1、所有观察者要实现固定的接口,这样从代码设计上来说,每个观察者行为得到统一,比如都会执行上述的doAction方法
2、当新的投资人InverstorXX需要观察该股票的时候,同样需要实现ShareObserver接口,然后将该对象通过ShareOb se 的addInverstor
添加到观察者集合中即可,可扩展性得到了加强。
上面的例子只是利用观察者思想来简单的模拟观察者的实现,当然客户端也可以通过实现java.util包里的Observer和Observerable接口来完成上面的股票系统,博主就偷个懒不实现了,感兴趣的读者可以自己是实现。
到此为止,还有个问题不知道读者感知到了没有,就是上面的观察者接口ShareObserver能力很有限,只能用于特定的例子(比如上面的股票系统),而不能做其他的场景。换句话说ShareObserver 做的还不够抽象,所以还需要改进。
在android中也经常看到观察者模式的影子,比如ListView,具体可参考博主的《
ListView观察者模式的应用》
上面的观察者模式的代码结构如果用图表示的话,可以用如下图表示:
List:
其实,我们也可以将各个观察者分组或者设立tag,形成结构如下的更为强大的观察者模式:
Map: