简 介
定义:定义对象间的一种一对多的依赖关系,使得每当一个对象的状态发生变化时,其相关依赖的对象都会得到通知并被自动更新。
观察者模式是一种对象行为型模式。可以看做是一种通知机制,实现一处通知,多处更新。
别名:发布-订阅模式、源-监听器模式、从属者模式等。
观察者模式的结构
4个角色:
● Subject 目标,即被观察者,提供 添加观察者对象、删除观察者对象、调用观察者对象的方法 的接口,通常为抽象类
● Observer 观察者,提供 更新 的接口,通常为一个接口类,在Subject状态发生变化时,这个更新的方法就会被触发
● ConcreteSubject 具体目标,Subject的具体实现类。可在此类中定义具体的业务逻辑
● ConcreteObserver 具体观察者,Observer的具体实现类。实现收到Subject状态变化信号后要处理的具体逻辑
实现步骤
我们来实现一个老师布置家庭作业给学生的例子,每当老师布置一个作业,所有的学生就会得到通知。
● Observer 观察者
public interface Observer {
void receiveHomework(String string);
}
● Subject 目标
public interface Subject {
void addObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
● ConcreteSubject 具体目标
public class Teacher implements Subject {
private List<Observer> observers = new ArrayList<>();
private String info;
@Override
public void addObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.receiveHomework(getInfo());
}
}
public String getInfo() {
return info;
}
public void publishHomeWork(String info) {
this.info = info;
System.out.println("This is the homework: " + info);
this.notifyObservers();
}
}
● ConcreteObserver 具体观察者
public class Student implements Observer {
private String name;
private String studentId;
private Teacher teacher;
public Student(String name, String id, Teacher teacher) {
this.name = name;
this.studentId = id;
this.teacher = teacher;
teacher.addObserver(this);
}
@Override
public void receiveHomework(String string) {
System.out.println(name + "收到了作业:" + string);
}
}
测试一下哈
public class Test {
public static void main(String[] args) {
Teacher teacher = new Teacher();
Student katherine = new Student("Katherine", "101", teacher);
Student bob = new Student("Bob", "102", teacher);
Student tom = new Student("Tom", "103", teacher);
teacher.publishHomeWork("数学课本第5页习题3");
teacher.publishHomeWork("数学课本第7页习题1");
teacher.removeObserver(bob);
teacher.publishHomeWork("数学课本第9页习题3");
}
}
//This is the homework: 数学课本第5页习题3
//Katherine收到了作业:数学课本第5页习题3
//Bob收到了作业:数学课本第5页习题3
//Tom收到了作业:数学课本第5页习题3
//This is the homework: 数学课本第7页习题1
//Katherine收到了作业:数学课本第7页习题1
//Bob收到了作业:数学课本第7页习题1
//Tom收到了作业:数学课本第7页习题1
//This is the homework: 数学课本第9页习题3
//Katherine收到了作业:数学课本第9页习题3
//Tom收到了作业:数学课本第9页习题3
Java中内置的观察者模式
import java.util.Observable;//抽象类
import java.util.Observer;//接口
我们实现一个书店通知读者来新书的例子,即读者观察书店的情况。
● 具体目标继承Observable抽象类
public class BookStore extends Observable {
private String info;
public String getInfo() {
return info;
}
public void pushlishBookInfo(String info) {
this.info = info;
System.out.println("书店出了新书:" + info);
setChanged();//目标发通知之前,先调用setChanged()方法,标志状态改变
notifyObservers();//发出通知,所有观察者收到通知
}
}
● 观察者实现Observer接口
public class Reader implements Observer {
private BookStore store;
private String name;
public Reader(BookStore store, String name) {
this.store = store;
this.name = name;
this.store.addObserver(this);//调用addObserver(Observer o)进行添加观察者
}
@Override
public void update(Observable o, Object arg) {
System.out.println(name + "收到了" + ((BookStore) o).getInfo() + "的信息");
}
}
● 测试
public class Test {
public static void main(String[] args) {
BookStore store = new BookStore();
Reader reader1 = new Reader(store, "Katherine");
Reader reader2 = new Reader(store, "Bob");
store.pushlishBookInfo("奇特的一生");
}
}
//书店出了新书:奇特的一生
//Bob收到了奇特的一生的信息
//Katherine收到了奇特的一生的信息
Android源码ListView的观察者模式分析
先来对号入座一下,再一起分析
● Observable<T>--------------Subject 目标
● DataSetObserver-------------Observer 观察者
● DataSetObservable----------ConcreteSubject 具体目标
● AdapterDataSetObserver----ConcreteObserver 具体观察者
● Observable<T>泛型的抽象类,提供了注册、撤销(一个或者全部)的接口。
public abstract class Observable<T> {
/**
* The list of observers. An observer can be in the list at most
* once and will never be null.
*/
protected final ArrayList<T> mObservers = new ArrayList<T>();
/**
* Adds an observer to the list. The observer cannot be null and it must not already
* be registered.
* @param observer the observer to register
* @throws IllegalArgumentException the observer is null
* @throws IllegalStateException the observer is already registered
*/
public void registerObserver(T observer) {
if (observer == null) {
throw new IllegalArgumentException("The observer is null.");
}
synchronized(mObservers) {
if (mObservers.contains(observer)) {
throw new IllegalStateException("Observer " + observer + " is already registered.");
}
mObservers.add(observer);
}
}
/**
* Removes a previously registered observer. The observer must not be null and it
* must already have been registered.
* @param observer the observer to unregister
* @throws IllegalArgumentException the observer is null
* @throws IllegalStateException the observer is not yet registered
*/
public void unregisterObserver(T observer) {
if (observer == null) {
throw new IllegalArgumentException("The observer is null.");
}
synchronized(mObservers) {
int index = mObservers.indexOf(observer);
if (index == -1) {
throw new IllegalStateException("Observer " + observer + " was not registered.");
}
mObservers.remove(index);
}
}
/**
* Remove all registered observers.
*/
public void unregisterAll() {
synchronized(mObservers) {
mObservers.clear();
}
}
}
● DataSetObservable,提供了通知各个Observer的方法。
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) {
// since onChanged() is implemented by the app, it could do anything, including
// removing itself from {@link mObservers} - and that could cause problems if
// an iterator is used on the ArrayList {@link mObservers}.
// to avoid such problems, just march thru the list in the reverse order.
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
/**
* Invokes {@link DataSetObserver#onInvalidated} on each observer.
* Called when the data set is no longer valid and cannot be queried again,
* such as when the data set has been closed.
*/
public void notifyInvalidated() {
synchronized (mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onInvalidated();
}
}
}
}
● DataSetObserver,提供了更新通知的接口
public abstract class DataSetObserver {
/**
* This method is called when the entire data set has changed,
* most likely through a call to {@link Cursor#requery()} on a {@link Cursor}.
*/
public void onChanged() {
// Do nothing
}
/**
* This method is called when the entire data becomes invalid,
* most likely through a call to {@link Cursor#deactivate()} or {@link Cursor#close()} on a
* {@link Cursor}.
*/
public void onInvalidated() {
// Do nothing
}
}
● AdapterDataSetObserver,实现了onChanged()方法和onInvalidated()方法
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();
}
@Override
public void onInvalidated() {
mDataChanged = true;
if (AdapterView.this.getAdapter().hasStableIds()) {
// Remember the current state for the case where our hosting activity is being
// stopped and later restarted
mInstanceState = AdapterView.this.onSaveInstanceState();
}
// Data is invalid so we should reset our state
mOldItemCount = mItemCount;
mItemCount = 0;
mSelectedPosition = INVALID_POSITION;
mSelectedRowId = INVALID_ROW_ID;
mNextSelectedPosition = INVALID_POSITION;
mNextSelectedRowId = INVALID_ROW_ID;
mNeedSync = false;
checkFocus();
requestLayout();
}
public void clearSavedState() {
mInstanceState = null;
}
}
我们来看看ListView的setAdapter(ListAdapter adapter)方法,假设我们给这个方法传了BaseAdapter的子类对象,BaseAdapter的源码如下
//BaseAdapter实现了ListAdapter的接口,然后关联了DataSetObserver的注册、撤销、通知等方法
//所以这里就相当于Subject。。。
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;
}
}
那我们使用ListView的setAdapter(ListAdapter adapter)方法的时候,ListView就new出了一个观察者对象,并且进行了注册。
public void setAdapter(ListAdapter adapter) {
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
resetList();
mRecycler.clear();
if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter);
} else {
mAdapter = adapter;
}
......
if (mAdapter != null) {
......
mDataSetObserver = new AdapterDataSetObserver();//创建观察者对象
mAdapter.registerDataSetObserver(mDataSetObserver);//注册观察者对象
......
}
} else {
mAreAllItemsSelectable = true;
checkFocus();
// Nothing selected
checkSelectionChanged();
}
requestLayout();
}
当ListView的数据产生改变时,我们会调用adapter的notifyDataChanged()方法,然后这个方法会调用mDataSetObservable.notifyChanged()方法,即DataSetObservable的notifyChanged()方法,即AdapterDataSetObserver的onChanged()方法。进而完成刷新操作。
观察者模式的优缺点
优点:
观察者和目标之间是轻度的关联关系,且为抽象耦合,对于两者来说都比较容易扩展。
常用的触发机制,形成一条触发链。
缺点:
因为是触发链,所以当观察者比较多的时候,可能会产生性能问题。
在链式结构中,容易产生循环引用的错误,造成系统假死。
参考链接:
http://blog.csdn.net/fangchongbory/article/details/7774044
https://www.cnblogs.com/fingerboy/p/5468994.html
http://www.jianshu.com/p/d55ee6e83d66
微信公众号,一起来玩耍,纯属娱乐,哈哈哈~~~