前言:
观察者模式在实际项目中使用的也是非常频繁的,它最常用的地方是GUI系统(Graphical User Interface 即图形用户界面)、订阅——发布系统等。因为这个模式的一个重要作用就是解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。以GUI系统来说,应用的UI具有易变性,尤其是前期随着业务的改变或者产品的需求修改,应用界面也经常性变化,但是业务逻辑基本变化不大,此时,GUI系统需要一套机制来应对这种情况,使得UI层与具体的业务逻辑解耦,观察者模式此时就派上用场了。
举个例子,新闻里喜闻乐见的警察抓小偷,警察需要在小偷伸手作案的时候实施抓捕。在这个例子里,警察是观察者,小偷是被观察者,警察需要时刻盯着小偷的一举一动,才能保证不会漏过任何瞬间
定义观察者
public interface Watcher {//长眼睛的
void Discover(String s);//好像发现了什么
}
定义被观察者接口,需要被观察的行为
public interface Mistakable {//可能会搞事的
void add(Watcher watcher);//引起一些长眼睛的人注意,注册
void steal(String s);//搞事
}
被观察者实现被观察者接口里面的方法
public class Tom implements Mistakable {//Tom就是可能搞事的人,现在他打算搞点什么
// 技术不行,可能被好多人看到
private List mWatchers = new ArrayList<>();
@Override
public void add(Watcher watcher) {
mWatchers.add(watcher);//被看到了
}
@Override
public void steal(String s) {//开始偷东西的时候被以下这些人发现了,然后发出了一些信号
for (Watcher watcher : mWatchers) {
watcher.Discover(s);
}
}
}
实现观察者接口并实现发出信号的方法
public class MainActivity extends AppCompatActivity implements Watcher {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.tv);
mTextView.setText("听说tom要搞一个炸弹,有人在监视他");
new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
doSomeThing();//搞事
return false;
}
}).sendEmptyMessageDelayed(0, 3000);
}
private void doSomeThing() {
Tom tom = new Tom();//tom来了
tom.add(this);//引起注意了
tom.steal("偷到了一台三桑note7");//开始搞事了
}
@Override
public void Discover(String s) {
mTextView.setText("tom被发现" + s + ",诶不抓了不抓了~");
}
}
概述
观察者模式又被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
模式中的角色
抽象主题(Subject):它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
具体主题(ConcreteSubject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。
抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
具体观察者(ConcreteObserver):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。
1.Subject 和 Observer 是一个一对多的关系,也就是说观察者只要实现 Observer 接口并把自己注册到 Subject 中就能够接收到消息事件;
2.Java API有内置的观察者模式类:java.util.Observable 类和 java.util.Observer 接口,这分别对应着 Subject 和 Observer 的角色;
3.使用 Java API 的观察者模式类,需要注意的是被观察者在调用 notifyObservers() 函数通知观察者之前一定要调用 setChanged() 函数,要不然观察者无法接到通知;
4.使用 Java API 的缺点也很明显,由于 Observable 是一个类,java 只允许单继承的缺点就导致你如果同时想要获取另一个父类的属性时,你只能选择适配器模式或者是内部类的方式,而且由于 setChanged() 函数为 protected 属性,所以你除非继承 Observable 类,否则你根本无法使用该类的属性,这也违背了设计模式的原则:多用组合,少用继承。
Android源码中的模式实现
在以前,我们最常用到的控件就是ListView了,而ListView最重要的一个点就是Adapter,在我们往ListView添加数据后,我们都会调用一个方法: notifyDataSetChanged(), 这个方法就是用到了我们所说的观察者模式。
跟进这个方法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);
}
/**
* Notifies the attached observers that the underlying data has been changed
* and any View reflecting the data set should refresh itself.
* 当数据集用变化时通知所有观察者
*/
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
}
可以发现,当数据发生变化时候,notifyDataSetChanged中会调用mDataSetObservable.notifyChanged()方法
public class DataSetObservable extends Observable {
/**
* Invokes onChanged on each observer. Called when the data set being observed has
* changed, and which when read contains the new state of the data.
*/
public void notifyChanged() {
synchronized(mObservers) {
// 调用所有观察者的onChanged方式
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
}
mDataSetObservable.notifyChanged()中遍历所有观察者,并且调用它们的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) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
// 获取数据的数量
mItemCount = mAdapter.getCount();
checkFocus();
// 注意这里 : 创建一个一个数据集观察者
mDataSetObserver = new AdapterDataSetObserver();
// 将这个观察者注册到Adapter中,实际上是注册到DataSetObservable中
mAdapter.registerDataSetObserver(mDataSetObserver);
// 代码省略
} else {
// 代码省略
}
requestLayout();
}
在设置Adapter时会构建一个AdapterDataSetObserver,最后将这个观察者注册到adapter中,这样我们的被观察者、观察者都有了。
AdapterDataSetObserver定义在ListView的父类AbsListView中,代码如下 :
class AdapterDataSetObserver extends AdapterView.AdapterDataSetObserver {
@Override
public void onChanged() {
super.onChanged();
if (mFastScroll != null) {
mFastScroll.onSectionsChanged();
}
}
@Override
public void onInvalidated() {
super.onInvalidated();
if (mFastScroll != null) {
mFastScroll.onSectionsChanged();
}
}
}
它由继承自AbsListView的父类AdapterView的AdapterDataSetObserver, 代码如下 :
class AdapterDataSetObserver extends DataSetObserver {
private Parcelable mInstanceState = null;
// 调用Adapter的notifyDataSetChanged的时候会调用所有观察者的onChanged方法,核心实现就在这里
@Override
public void onChanged() {
mDataChanged = true;
mOldItemCount = mItemCount;
// 获取Adapter中数据的数量
mItemCount = getAdapter().getCount();
// Detect the case where a cursor that was previously invalidated has
// been repopulated with new data.
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方法。这就是一个观察者模式!
总结:AdapterView中有一个内部类AdapterDataSetObserver,在ListView设置Adapter时会构建一个AdapterDataSetObserver,并且注册到Adapter中,这个就是一个观察者。而Adapter中包含一个数据集可观察者DataSetObservable,在数据数量发生变更时开发者手动调用AdapternotifyDataSetChanged,而notifyDataSetChanged实际上会调用DataSetObservable的notifyChanged函数,该函数会遍历所有观察者的onChanged函数。在AdapterDataSetObserver的onChanged函数中会获取Adapter中数据集的新数量,然后调用ListView的requestLayout()方法重新进行布局,更新用户界面。
模式总结
优点
观察者模式解除了主题和具体观察者的耦合,让耦合的双方都依赖于抽象,而不是依赖具体。从而使得各自的变化都不会影响另一边的变化。
缺点
依赖关系并未完全解除,抽象通知者依旧依赖抽象的观察者。
适用场景
当一个对象的改变需要给变其它对象时,而且它不知道具体有多少个对象有待改变时。
一个抽象某型有两个方面,当其中一个方面依赖于另一个方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。
与监听器的区别
(1)Observer的实现相对简单,event-listener需要实现三个角色,observer-observable需要实现两个角色。
a、event-Listener所要实现的三个角色:事件源、事件对象、事件监听器
b、observer-observable实现两个角色:事件源、事件,执行逻辑时通知observer执行update方法可传观察者和参数,简化了监听器
(2)Observable的api已经把对观察者的注册,删除等定义好了,而且是线程安全的。而event-listener需要使用者自己实现。
(3)两者都需要自己定义并实现触发事件的通知。但Observable需要注意要在通知Observer之前调用jdk提供的setChanged()。
(4)event-listener是传统的c/s界面事件模型,分事件源和事件(状态)角色,事件源要经过事件的包装、成为事件的属性之一再传递给事件监听/处理者,这个事件监听者就相当于观察者。Observer更简洁一些。两者在思想上是统一的,很多框架仍然使用了event-listener模式,比如spring框架的ApplicationEvent,ApplicationListener。