目录
一、定义
1.观察者模式
2.UML类图
3.观察者模式中的角色
二、使用场景
三、简单实现
四、观察者模式在java.util包中的应用
五、观察者模式在Button中的应用
六、观察者模式在ListView中的应用
七、观察者模式的优缺点
观察者模式的优点
观察者模式的缺点
一、定义
1.观察者模式
定义对象一种一对多的依赖关系,使得当一个对象改变状态的时候,所有依赖它的对象都会得到通知并自动更新。观察者模式是一种使用频率非常高的设计模式,最常用的地方就是订阅-发布系统。
2.UML类图
3.观察者模式中的角色
- Subject(抽象主题): 又叫抽象被观察者,把所有的观察者对象引用保存到一个集合里,每个主题都可以由任何数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
- ConcreteSubject(具体主题):又叫具体被观察者,将有关的状态存入具体观察者对象,在内部状态改变时,给所有的登记过的观察者发出通知。
- Observer(抽象观察者):为所有的观察者定义一个接口,在收到主题通知时进行更新。
- ConcrereObserver(具体观察者):实现抽象观察者定义的更新接口,在得知主题更改通知时更新自身的状态。
二、使用场景
- 当一个对象的数据更新了需要通知其他对象,但是又不希望和被通知的对象形成耦合;
- 当一个对象数据更新了,这个对象需要让其他对象也更新数据但是不知道有多少个对象需要更新。
- 跨系统的消息交换场景,如消息队列、事件总线的处理机制。
三、简单实现
1.被观察者抽象类
public interface Observable<T> {//绑定观察者void addObserver(Observer observer);//移除观察者void removeObserver(Observer observer);//被观察者发出了改变void notifyObservers(String message);
}
2.被观察者实现类
public class ServerObservable implements Observable<String>{private List<Observer> observers = new ArrayList<>();private String message;@Overridepublic void addObserver(Observer observer) {//添加观察者observers.add(observer);}@Overridepublic void removeObserver(Observer observer) {//删除观察者observers.remove(observer);} @Overridepublic void notifyObservers(String message) {//通知观察者有更新的数据for (Observer observer:observers){observer.update(message);}}}
3.观察者抽象类
public interface Observer {//被观察者改变接受消息,做响应void update(String message);
}
4.观察者实现类
public class User implements Observer{String info;public User(String s) {info = s;}//用于更新数据@Overridepublic void update(String message) {System.out.println(info + message);}
}
5.测试代码
//创建观察者 User user1 = new User("第一位观察者");User user2 = new User("第二位观察者");User user3 = new User("第三位观察者");//创建被观察者ServerObservable observable = new ServerObservable();observable.add(user1);observable.add(user2);observable.add(user3);//通知更新observable.notifyObservers("需要更新数据");
运行,从结果看当被观察者发出更新数据的通知后所有注册的观察者都会立刻响应到:
四、观察者模式在java.util
包中的应用
观察者模式在JDK中就有典型应用,比如java.util.Observable和java.util.Observer类。在使用时,被观察者需要继承java.util.Observable类,观察者需要实现java.util.Observer接口。下面通过一个简单的Demo看一下如何使用JDK中的观察者模式,UML图如下。
观察者Student的代码
import java.util.Observable;
import java.util.Observer;public class Student implements Observer {private String name;public Student(String name) {this.name = name;}@Overridepublic void update(Observable o, Object arg) {System.out.println(name + (String)arg);}
}
被观察者Teacher的代码
import java.util.Observable;public class Teacher extends Observable {public void publishMessage(String message) {// mark as value changedsetChanged();// trigger notificationnotifyObservers(message);}
}
输出结果如下:
JDK中的Observable基类和Observer接口为我们的简单使用观察者模式提供了方便,但它有一个非常明显的缺点:被观察者Teacher需要继承Observable这个基类,但是如果Teacher已经继承其他的类就不能同时再继承Observable类。解决这个矛盾的思路有两种:一是自定义观察者模式,将add、delete、notify等方法写进Teacher类;二是使用代理模式,在Teacher中维护一个Observable类的对象,并且实现同名的方法。
五、观察者模式在Button中的应用
在Android中我们遇到的最常见的观察者模式就是各种控件的监听。
//注册观察者button.setOnClickListener(new View.OnClickListener() {//观察者实现类@Overridepublic void onClick(View view) {}});
在这段代码中,Button就是主题也就是被观察者,通过new出的View.OnClickListenerd对象就是具体的观察者;onClickListener就是抽象的观察者接口;最后通过setOnClickListener把观察者注册到被观察者中,源码如下:
//在单击视图时调用的回调的接口定义。public interface OnClickListener {//在单击视图时调用。void onClick(View v);}
//注册观察者
public void setOnClickListener(@Nullable OnClickListener l) {if (!isClickable()) {setClickable(true);}getListenerInfo().mOnClickListener = l;}//执行点击事件
public boolean performClick() {// We still need to call this method to handle the cases where performClick() was called// externally, instead of through performClickInternal()notifyAutofillManagerOnClick();final boolean result;final ListenerInfo li = mListenerInfo;if (li != null && li.mOnClickListener != null) {playSoundEffect(SoundEffectConstants.CLICK);//调用观察者执行onClickli.mOnClickListener.onClick(this);result = true;} else {result = false;}sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);notifyEnterOrExitForAutoFillIfNeeded(true);return result;}
六、观察者模式在ListView中的应用
在使用ListView时,如果数据改变了需要调用setDataSetChanged()方法来通知ListView更新UI。换一种说法,ListView的UI是观察者,ListView对应的adapter中的数据就是被观察者。它的实现方式就是注册一个观察者到adapter中,以实现监听adapter的数据变化的目的。
首先看一下listView.setAdapter(adapter);在这个方法中ListView会创建一个
AdapterDataSetObserver类的观察者并且调用adapter的registerDataSetObserver()。
@Overridepublic void setAdapter(ListAdapter adapter) {//首先确保移除了所有的观察者if (mAdapter != null && mDataSetObserver != null) {mAdapter.unregisterDataSetObserver(mDataSetObserver);}.....if (mAdapter != null) {....//创建AdapterDataSetObserver观察者mDataSetObserver = new AdapterDataSetObserver();//将观察者注册到adaptermAdapter.registerDataSetObserver(mDataSetObserver);....}}
继续看Adapter的registerDataSetObserver方法,发现最终的结果就是把观察者注册到DataSetObservable这个发布者里。
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {@UnsupportedAppUsage//被观察者private final DataSetObservable mDataSetObservable = new DataSetObservable();public void registerDataSetObserver(DataSetObserver observer) {//注册观察者mDataSetObservable.registerObserver(observer);}public void unregisterDataSetObserver(DataSetObserver observer) {//移除mDataSetObservable.unregisterObserver(observer);}
...
}
DataSetObservable继承于android.database.Observable,在DataSetObservable中实现了notifyChanged()方法。
package android.database;public class DataSetObservable extends Observable<DataSetObserver> {public void notifyChanged() {synchronized(mObservers) {//通知所有的观察者,调用onChangedfor (int i = mObservers.size() - 1; i >= 0; i--) {mObservers.get(i).onChanged();}}}public void notifyInvalidated() {synchronized (mObservers) {for (int i = mObservers.size() - 1; i >= 0; i--) {mObservers.get(i).onInvalidated();}}}
}
现在我们看一下adapter.notifyDataSetChanged(),其实就是调用了被观察者的notifyChanged()通知观察者onChanged刷新UI。
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {public void notifyDataSetChanged() {mDataSetObservable.notifyChanged();}
}
七、观察者模式的优缺点
观察者模式的优点
解除观察值与主题之间的耦合,让耦合的双方都依赖于抽象,而不是依赖于具体。从而使各自的变化不会影响另一边的变化。
易于扩展,对于同一主题新增观察者时无需修改原有的代码。
观察者模式的缺点
1、依赖关系并未完全解除,抽象主题依然依赖抽象观察者。
2、使用观察者模式需要考虑开发效率和运行效率的问题,程序中包含一个被观察者、多个观察者,开发、调试等会比较复杂,而且java中消息的通知是顺序执行的,如果一个观察者卡顿,会影响整体的执行效率。
3、可能会引起多余的数据通知。
参考资源
Android 源码中的观察者模式 - 掘金 (juejin.cn)
Android设计模式03-观察者模式 - 简书 (jianshu.com)