观察者模式介绍
观察者模式是一个使用率非常高的模式,它最常用的地方就是GUI系统、订阅–发布系统。因为这个模式的一个重要作用就是解耦,将观察者和被观察者解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。
观察者模式的定义:
定义对象间一种一对多的依赖关系,使得每当一个对象改变状态时,则所有依赖于它的对象都会得到通知并且自动更新。
使用情景:
第一、当一个抽象模型有两个方面,其中一个方面的操作依赖于另一个方面的状态变化。
第二、如果在更改一个对象的时候,需要同时连带改变其他的对象,而且不知道究竟应该有多少对象需要被连带改变。
第三、当一个对象必须通知其他的对象,但是你又希望这个对象和其他被他通知的对象是松散耦合的。
观察者模式的UML类图,如图
角色介绍:
(1)Subject(被观察者) 被观察的对象。当需要被观察的状态发生变化时,需要通知队列中所有观察者对象。Subject需要维持(添加,删除,通知)一个观察者对象的队列列表。
(2)ConcreteSubject 被观察者的具体实现。包含一些基本的属性状态及其他操作。
(3)Observer(观察者) 接口或抽象类。当Subject的状态发生变化时,Observer对象将通过一个callback函数得到通知。
(4)ConcreteObserver 观察者的具体实现。得到通知后将完成一些具体的业务逻辑处理。
JDK提供了对observer设计模式的支持:
- 通过java.util.Observable类扮演Subject角色,一个类只需通过继承java.util.Observable类便可担当ConcreteSubject角色;
- 通过java.util.Observer接口扮演Observer角色,一个类只需实现java.util.Observer接口便可担当ConcreteObserver角色。
- java.util.Observable的实现类通过调用setChanged()方法以及notifyObservers(Object)方法便可简单地通知Observer。
观察者模式的实现(两种方式,推模型和拉模型):
推模型:目标对象主动向观察者推送目标的详细信息,推送的信息通常是目标对象的全部或部分数据。
拉模型:目标对象在通知观察者时候,只传递少量信息。
如果观察者需要更具体的信息,由观察者主动到目标对象中获取,相当于观察者主动从目标拉数据;
一般这种模型的实现中,会把目标对象通过update方法传递给观察者。
二者比较:推模型是假定目标对象知道观察者需要的数据,使得观察者对象难以复用;拉模型是目标对象不知道观察者具体需要什么数据,因此把自身传递给观察者,由观察者来取,基本上适应各种情况的需要。
观察者模式的简单实现
玩LOL游戏的知道腾讯服务器会定时更新游戏版本,比如增加新英雄了等,在这个场景中,千千万万玩游戏的Player就是观察者,腾讯服务器LOLServer其实就是被观察者,当LOLSever有更新时会通知所有安装了LOL客户端的Player,代码实现如下:
先写一个玩家Player类,作为观察者:
import java.util.Observable;
import java.util.Observer;
public class Player implements Observer{//继承Observer接口
public String name;//姓名
public Player(String aName){
name=aName;
}
//重写父类的update()方法
@Override
public void update(Observable o, Object arg) {
// TODO Auto-generated method stub
System.out.println("Hello,"+name+",LOL更新啦,内容:"+arg);
}
@Override
public String toString() {
return "玩家:"+name;
}
}
然后LOLServer类,作为被观察者:
import java.util.Observable;
public class LOLServer extends Observable{
public void postNewPublication(String content){
//标识内容或者状态发生改变,不可少
setChanged();
//通知所有观察者
notifyObservers(content);//推模型,没有参数的时候是拉模式
}
}
具体被观察者将更新内容发布到每一个观察者Test类:
public class Test {
public static void main(String[] args) {
//1 创建被观察的角色
LOLServer lolServer=new LOLServer();
//2 创建观察者
Player player1=new Player("李彦宏");
Player player2=new Player("马云");
Player player3=new Player("马化腾");
//3 将观察者注册到可观察对象的观察者列表中
lolServer.addObserver(player1);
lolServer.addObserver(player2);
lolServer.addObserver(player3);
//4 发布消息
lolServer.postNewPublication("死亡如风,常伴吾身!");
}
}
打印结果如下:
Hello,马化腾,LOL更新啦,内容:死亡如风,常伴吾身!
Hello,马云,LOL更新啦,内容:死亡如风,常伴吾身!
Hello,李彦宏,LOL更新啦,内容:死亡如风,常伴吾身!
Android源码中的观察者模式
ListView是Android中最重要的控件之一,而ListView最重要的一个功能就是Adapter。通常,在我们往ListView中添加数据后,都会调用Adapter的notifyDataSetChanged,这个方法定义在BaseAdapter中,具体代码如下:
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
private final DataSetObservable mDataSetObservable = new DataSetObservable();
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
public void unregisterDataSetObserver(DataSetObserver observer)
{
mDataSetObservable.unregisterObserver(observer);
}
//当数据集变化时,通知所有的观察者
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
}
这里BaseAdapter 就是一个观察者模式,那么BaseAdapter 是如何运作的呢?谁是观察者?谁是被观察者呢?被观察者是如何发布新的内容的?首先,我们看看 mDataSetObservable.notifyChanged()函数中看看:
//数据集观察者
public class DataSetObservable extends Observable<DataSetObserver> {
/**
* Invokes {@link DataSetObserver#onChanged} on each observer.
* Called when the contents of the data set have changed. The recipient
* will obtain the new contents the next time it queries the data set.
*/
public void notifyChanged() {
synchronized(mObservers) {
//调用所有观察者的onChaged函数来通知他们被观察者发生了变化
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
这个代码很简单,就是在for循环中遍历所有的观察者,并且调用他们的onChanged方法,从而告知观察者发生了变化。
那么这些观察者是从哪里来的呢?其实这些观察者就是ListView通过setAdapter方法设置Adapter产生的,相关代码如下:
@Override
public void setAdapter(ListAdapter adapter) {
//如果已经有了一个Adapter,那么先注销该adapter对应的观察者
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
//代码省略
super.setAdapter(adapter);
if (mAdapter != null) {
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();//获取数据的总数
mDataChanged = true;
checkFocus();
//创建一个数据集观察者
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);//将这个观察者注册到Adapter中,实际是注册到了DataSetObservable中
//代码省略
requestLayout();
}
在代码中我们可以看到,在设置adapter的时候会构建一个AdapterDataSetObserver,这就是上面说到的观察者,最后将AdapterDataSetObserver这个观察者注册到Adapter中,这样我们的观察者和被观察者都已经有了,那么AdapterDataSetObserver实际上是个什么东西呢?它是怎么运作的?
我们再看看AdapterDataSetObserver,它定义在ListView的父类AbsListView中,具体代码如下:
class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
@Override
public void onChanged() {
super.onChanged();
if (mFastScroller != null) {
mFastScroller.onSectionsChanged();
}
}
@Override
public void onInvalidated() {
super.onInvalidated();
if (mFastScroller != null) {
mFastScroller.onSectionsChanged();
}
}
}
它又继承自AbsListView的父类AdapterView的AdapterDataSetObserver,具体代码如下:
class AdapterDataSetObserver extends DataSetObserver {
private Parcelable mInstanceState = null;
//上面讲过,调用adapter的notifyDataSetChanged时会调用所有观察者的onChanged方法,核心实现就在这里
@Override
public void onChanged() {
mDataChanged = true;
mOldItemCount = mItemCount;
mItemCount = getAdapter().getCount();//获取adapter中数据的总量
if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
&& mOldItemCount == 0 && mItemCount > 0) { AdapterView.this.onRestoreInstanceState(mInstanceState);
mInstanceState = null;
} else {
rememberSyncState();
}
checkFocus();
//重新布局Listview、GridView等AdapterView组件
requestLayout();
}
public void clearSavedState() {
mInstanceState = null;
}
}
到这里我们知道了,当ListView的数据发生变化的时候,调用Adapter的notifyDataSetChanged函数,这个函数又会调用DataSetObservable的notifyChanged函数,这个函数又会调用所有观察者(AdapterDataSetObserver)的onChanged方法,在onChanged函数中又会调用ListView重新布局的函数requestLayout使得ListView重新刷新界面,这就是一个观察者模式。
总结:
观察者模式主要的作用就是对象解耦,将观察者与被观察者完全隔离,只依赖于Observer与Observable抽象
优点:
第一 观察者模式实现了观察者和目标之间的抽象耦合
第二 观察者模式实现了动态联动
第三 观察者模式支持广播通信
缺点:
可能引起无畏的操作(可以根据要通知的内容区别对待观察者)