一、什么是观察者模式?
观察者模式:对象间的一种一对多的依赖关系。当一个对象的状态发生改变时,所有依赖于他的对象都得到通知并被自动更新。
观察者模式又称为发布——订阅者模式,根据这个名字来理解上面的一句话或许更好理解。观察者模式属于行为型设计模式。
二、观察者模式的UML图
观察者模式的角色介绍
Subject:抽象主题,也就是被观察者的角色,它把所有观察者对象保存在一个集合中。抽象主题提供了一个接口,用来增删观察者对象。
ConcreteSubject:具体主题,将有关状态存入具体观察者对象,在内部状态发生改变的时候,给所有注册过的观察者发送通知。
Observer:抽象观察者,定义了一个更新接口,得到Subject更改通知时更改自己。
ConcreteObserver:具体观察者,收到通知后更新自身的状态。
(下面是另一种结构,来自于MOOC)
观察者模式的结构和说明
三、为什么要使用观察者模式,什么地方使用它?
使用观察者模式的原因可以简单记为——解耦!使用观察者模式,使得被观察者和观察者之间的依赖很小,甚至于无依赖。比如Android开发中的UI层和具体业务逻辑层,如果可以做到无依赖,那么当我们需要更换UI界面的时候,具体业务层逻辑完全不需要修改。
观察者模式的使用场景:
- 关联行为场景,需要注意的是,关联行为是可拆分的,而不是组合关系;
- 事件多级触发场景;
- 跨系统的消息交换场景,如消息队列、事件总线的处理机制。
上面的这三行话,可能理解起来有些迷糊,也可以这样来记住:
- 当一个对象的改变需要同时改变其他对象的时候;
- 而且不知道具体有多少对象等待改变的时候,考虑使用观察者模式
四、观察者模式的简单实现
1.具体观察者
/**
* 程序员是观察者
* Observer是JDK中的内置类型,是抽象的观察者角色,即Observer
* 那么Coder就是具体的观察者
*/
public class Coder implements Observer{
public String name;
public Coder(String aName){
name = aName;
}
@Override
public void update(Observable observable, Object data) {
Log.d("zsf","您好," + name + ",您订阅的内容更新了!内容:" + data);
}
@Override
public String toString() {
return "码农:" + name;
}
}
2.具体被观察者
/**
* DevTechFrontier是被观察者,它有更新时,所有的Coder都会接到相应的通知
* Observable是JDK中内置的类型,这里是抽象的主题,即Subject
*/
public class DevTechFrontier extends Observable {
public void postNewPublication(String content){
//标识状态或者内容发生改变
setChanged();
//通知所有观察者
notifyObservers(content);
}
}
3.客户端代码测试:
public class SubjectObserverTestActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//被观察者角色
DevTechFrontier devTechFrontier = new DevTechFrontier();
//观察者
Coder oneCoder = new Coder("程序员1");
Coder twoCoder = new Coder("程序员2");
Coder threeCoder = new Coder("程序员3");
Coder fourthCoder = new Coder("程序员4");
//将观察者注册到可观察对象的观察列表中
devTechFrontier.addObserver(oneCoder);
devTechFrontier.addObserver(twoCoder);
devTechFrontier.addObserver(threeCoder);
devTechFrontier.addObserver(fourthCoder);
//发布消息
devTechFrontier.postNewPublication("新一期的科技报发布了!");
}
}
4.Log日志输出
04-06 18:32:46.873 8116-8116/? D/zsf: 您好,程序员1,您订阅的内容更新了!内容:新一期的科技报发布了!
04-06 18:32:46.873 8116-8116/? D/zsf: 您好,程序员2,您订阅的内容更新了!内容:新一期的科技报发布了!
04-06 18:32:46.873 8116-8116/? D/zsf: 您好,程序员3,您订阅的内容更新了!内容:新一期的科技报发布了!
04-06 18:32:46.873 8116-8116/? D/zsf: 您好,程序员4,您订阅的内容更新了!内容:新一期的科技报发布了!
Android中具体使用该模式的比如常用的ListView,每当需要更新内容的时候,我们会调用notifyDataSetChanged()方法。
源码:
/**
* Common base class of common implementation for an {@link Adapter} that can be
* used in both {@link ListView} (by implementing the specialized
* {@link ListAdapter} interface) and {@link Spinner} (by implementing the
* specialized {@link SpinnerAdapter} interface).
*/
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
private final DataSetObservable mDataSetObservable = new DataSetObservable();//数据集观察者
public boolean hasStableIds() {
return false;
}
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();
}
/**
* Notifies the attached observers that the underlying data is no longer valid
* or available. Once invoked this adapter is no longer valid and should
* not report further data set changes.
*/
public void notifyDataSetInvalidated() {
mDataSetObservable.notifyInvalidated();
}
public boolean areAllItemsEnabled() {
return true;
}
public boolean isEnabled(int position) {
return true;
}
public View getDropDownView(int position, View convertView, ViewGroup parent) {
return getView(position, convertView, parent);
}
public int getItemViewType(int position) {
return 0;
}
public int getViewTypeCount() {
return 1;
}
public boolean isEmpty() {
return getCount() == 0;
}
}
五、观察者模式的不足
需要考虑开发效率和运行效率,一个被观察者通知多个观察者,开发和调试困难增加;Java中消息的通知默认采用顺序执行,一个观察者卡顿,整体效率降低,这时考虑使用异步解决。