设计模式之观察者模式
介绍
观察者模式是一种使用频率非常高的设计模式,最常用的地方就是订阅-发布系统。 这个模式的重要作用就是将观察者和被观察者解耦,使他们之间的依赖更小甚至没有。
观察者模式又被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
使用场景
- 关联行为场景,这个关联是可拆分的。将观察者和被观察者封装在不同的对象中,可以各自独立的变化。
- 当一个对象改变时,有其他对象要进行相应的变化,但是他并不知道有多少个对象需要变化。
角色介绍
- 抽象主题(Subject):它把所有观察者对象的引用保存到一个集合,每个主题都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,一般用抽象类与接口来实现。
- 具体主题(ConcreteSubject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。一般实现了抽象接口。
- 抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己,通常以接口方式实现。
- 具体观察者(ConcreteObserver):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调,一般实现抽象观察者实现。
一般Subject与Oberver是一对多的,也就是一个Subject可以注册很多个Observer。只需要Observer将自己注册到Subject中即可。
Java API中已经内置了观察者模式类,Observable类与Obeserver接口。分别对应了Subject以及Observer。在使用时需要注意的是,在调用notifyObeservers()方法之前一定要先调用setChanged方法,否则观察者无法接收到通知。
Java API中的观察者模式有一个很明显的缺点。由于Obervable是一个类,而Java又只允许单继承。导致你使用继承者模式又想要获取另一个父类的特性时,只能选择适配器模式或者内部类。这违背了设计模式的原则:多用组合,少用继承。
示例
这里,MyPerson类是一个被观察者,继承自Observable类:
public class MyPerson extends Observable {
private String name;
private int age;
private String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
setChanged();
notifyObservers();
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
setChanged();
notifyObservers();
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
setChanged();
notifyObservers();
}
@Override
public String toString() {
return "MyPerson [name=" + name + ", age=" + age + ", sex=" + sex + "]";
}
}
而MyObserver是一个观察者,实现了Obeserver接口及其update方法:
public class MyObserver implements Observer {
private int id;
private MyPerson myPerson;
public MyObserver(int id) {
System.out.println("我是观察者---->" + id);
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public MyPerson getMyPerson() {
return myPerson;
}
public void setMyPerson(MyPerson myPerson) {
this.myPerson = myPerson;
}
@Override
public void update(Observable observable, Object data) {
System.out.println("观察者---->" + id + "得到更新");
this.myPerson = (MyPerson) observable;
System.out.println(((MyPerson) observable).toString());
}
}
这样,在观察者接收到通知后,便会执行update方法进行更新操作
public class MainActivity extends Activity {
private Button mBtnAddObserver;
private Button mBtnChangeData;
private MyPerson mObservable;
private List<MyObserver> mObservers;
private int i;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtnAddObserver = (Button) findViewById(R.id.btn_add_observer);
mBtnChangeData = (Button) findViewById(R.id.btn_change_data);
mObservable = new MyPerson();
mObservers = new ArrayList<MyObserver>();
//按下添加Obeserver
mBtnAddObserver.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Observer observer = new MyObserver(i);
i++;
mObservable.addObserver(observer);
mObservers.add(observer);
}
});
//按下改变Observable(同时会发送通知)
mBtnChangeData.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mObservable.setName("a" + i);
mObservable.setAge(10 + i);
mObservable.setSex("男" + i);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
for(Obeserver observer : mObservers){
mObservable.deleteObserver(observer);
}
}
}
Android中的观察者模式
我们要更新ListView的数据时,会使用到notifyDataSetChanged()方法,之后ListView的数据便会更新。这就比较类似这里的观察者模式了,ListView观察着Adapter中的内容,内容变化则ListView就会刷新
我们可以查看BaserAdapter的方法,会发现下面的几个方法。显然这里就是使用了观察者模式。当调用了notifyDataSetChanged后,被观察者就会发出通知。
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();
}
......
}
Android用了一种很巧妙的方法解决了前面提到的单继承的问题,通过了对象适配器模式的方式。让BaseAdapter间接成为了被观察者。
其中这些注册和注销观察者等等方法是通过DataSetObservable这个类调用的。这个类继承自Observable。
public class DataSetObservable extends Observable<DataSetObserver> {
public void notifyChanged() {
synchronized(mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
......
}
Observable中有一个protected final ArrayList<T> mObservers = new ArrayList<T>();
, 用来保存注册的观察者。mDataSetObservable.registerObserver(observer)
和mDataSetObservable.unregisterObserver(observer)
分别就是增加和删除。
在notifyChanged
方法中,遍历这个集合,调用每一个观察者的onChanged()
方法。
而这些观察者则是在ListView的setAdapter()
调用的过程中注册的。
public class ListView extends AbsListView {
public void setAdapter(ListAdapter adapter) {
//如果已经有了一个adapter,注销这个adapter之前的观察者,
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
......
if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter);
} else {
//将新的adapter赋给mAdapter
mAdapter = adapter;
}
......
super.setAdapter(adapter);
if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
//保存之前的数据个数
mOldItemCount = mItemCount;
//获取新的个数
mItemCount = mAdapter.getCount();
checkFocus();
//创建数据集观察者
mDataSetObserver = new AdapterDataSetObserver();
//注册观察者
mAdapter.registerDataSetObserver(mDataSetObserver);
...
}
} else {
...
}
requestLayout();
}
}
这里AdapterDataSetObserver是ListView的父类AbsListView的内部类 。
public abstract class AbsListView extends AdapterView<ListAdapter> implements TextWatcher,
ViewTreeObserver.OnGlobalLayoutListener, Filter.FilterListener,
ViewTreeObserver.OnTouchModeChangeListener,
RemoteViewsAdapter.RemoteAdapterConnectionCallback {
class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
@Override
public void onChanged() {
super.onChanged();
if (mFastScroll != null) {
mFastScroll.onSectionsChanged();
}
}
······
}
}
AdapterDataSetObserver是AdapterView.AdapterDataSetObserver的子类,因此查看super.onChanged()
public abstract class AdapterView<T extends Adapter> extends ViewGroup {
class AdapterDataSetObserver extends DataSetObserver {
private Parcelable mInstanceState = null;
@Override
public void onChanged() {
mDataChanged = true;
mOldItemCount = mItemCount;
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();
//重新布局
requestLayout();
}
......
}
}
当ListView数据变化时,调用Adapter的notifyDataSetChange方法,这个方法调用DataSetObservable的notifyChanged方法,这个方法又会调用所有观察者的onChanged方法,onChanged再调用重新布局View的方法,完成刷新数据的功能。
优缺点分析
- 优点
- 解除了观察者和被观察者的耦合,而且依赖的都是抽象,容易应对业务变化,各自的变化不会影响另一个。
- 增强系统灵活性、可拓展性。
- 缺点
- Java中的消息默认是顺序执行,如果其中一个观察者卡顿,会造成整个系统效率变低,可以考虑异步。
- 可能会引起无用的操作甚至错误的操作
广告时间
我是N0tExpectErr0r,一名广东工业大学的大二学生
欢迎来到我的个人博客,所有文章均在个人博客中同步更新哦
http://blog.N0tExpectErr0r.cn 。