赞
踩
观察者模式是在代码框架中使用比较高的一个设计模式,常常又叫做订阅/发布模式。而通过这个设计模式通常我们就可以做到代码的解耦。
在现实生活中,比如当我们订阅了Android
官方资讯后,用户就可以收到来自这些网站的推荐消息。在这个场景中就是发布/订阅模式。而这种观察行为通常是一个被观察者,多个观察者。通过观察者模式可以实现一种一对多的关系,使得当被观察者的状态发生改变的时候,所有的观察者都可以得到通知,并作出相应的更新操作。
在Java
中Observer
和Observable
是JDK
内置实现的,当我们需要用到观察者模式的时候就可以考虑实现相关的方法。比如下面的案例。
首先定义一观察者,需要实现Observer
接口:
// 观察者
static class Person implements Observer{
private String name;
public Person(String n){
this.name = n;
}
@Override
public void update(Observable o, Object arg) {
System.out.println(this.name+": ==> " + arg);
}
}
以及对应的被观察者对象,继承自Observable
类:
// 被观察者
static class Message extends Observable{
public void messageHasChanged(String content){
setChanged();
notifyObservers(content);
}
}
最后我们就可以简单的测试了:
public static void main(String[] args) {
Message message = new Message(); // 被观察者
Person person1 = new Person("张三"); // 观察者
Person person2 = new Person("李四");
Person person3 = new Person("王五");
// 将二者关联
message.addObserver(person1);
message.addObserver(person2);
message.addObserver(person3);
// 发布消息
message.messageHasChanged("这是一个测试消息!");
}
测试结果:
可以看到是按照注册的倒序来更新消息。当然至于为什么会是这样,其实在源码中是将原本的观察者对象拷贝到一个数组中,然后数组从最后一个位置向前遍历得来的。接下来不妨看看Observer
接口和Observable
类的源代码。
Observer
接口非常简单,就一个未实现的方法update
:
public interface Observer {
void update(Observable o, Object arg);
}
Observable
类中定义了一个容器数组Vector
来存储观察者对象Observer
,在添加观察者对象的时候会对观察者做一个非空的判断。且定义了一个布尔类型的私有变量changed
来标识是否发生改变。整个代码逻辑比较简单,可以发现主要的逻辑在notifyObservers
方法中。在进行遍历通知观察者的时候,在该方法中首先对当前对象加了一个synchronized
锁,主要是为了判断当前被观察者对象是否改变,如果没有改变就返回。否则就将所有注册的观察者装载到一个Object
数组中(装载后将changed
变量重置为false
),然后逆序调用每个Observer
对象的update
方法。
public class Observable { private boolean changed = false; private Vector<Observer> obs; public Observable() { obs = new Vector<>(); } public synchronized void addObserver(Observer o) { if (o == null) throw new NullPointerException(); if (!obs.contains(o)) { obs.addElement(o); } } public synchronized void deleteObserver(Observer o) { obs.removeElement(o); } public void notifyObservers() { notifyObservers(null); } public void notifyObservers(Object arg) { Object[] arrLocal; synchronized (this) { if (!changed) return; arrLocal = obs.toArray(); clearChanged(); } for (int i = arrLocal.length-1; i>=0; i--) ((Observer)arrLocal[i]).update(this, arg); } ... }
为了直观的Idea
中查看到类图,不妨在一个包下面仿造上述两个类,然后再定义测试方法。最后使用Idea
的插件Sketch lt!
来进行plantuml
生成,最终看到结构。
定义为:
package com.weizu; public interface CustomObserver { void update(CustomObservable o, Object msg); } public class CustomObservable { private List<CustomObserver> mObservers; public CustomObservable(){ mObservers = new ArrayList<CustomObserver>(); } public synchronized void addObserver(CustomObserver o) { if (o == null) throw new NullPointerException(); if (!mObservers.contains(o)) { mObservers.add(o); } } public void notifyObservers(Object msg) { for (CustomObserver mObserver : mObservers) { mObserver.update(this, msg); } } }
测试用例:
public class Demo { // 观察者 static class Person implements CustomObserver{ private String name; public Person(String n){ this.name = n; } public void update(CustomObservable o, Object arg) { System.out.println(this.name+": ==> " + arg); } } // 被观察者 static class Message extends CustomObservable{ public void messageHasChanged(String content){ notifyObservers(content); } } public static void main(String[] args) { Message message = new Message(); // 被观察者 Person person1 = new Person("张三"); // 观察者 Person person2 = new Person("李四"); Person person3 = new Person("王五"); // 将二者关联 message.addObserver(person1); message.addObserver(person2); message.addObserver(person3); // 发布消息 message.messageHasChanged("这是一个测试消息!"); } }
然后使用下面的工具来生成weizu.plantuml
文件:
最后,拷贝weizu.plantuml
文件内容到http://www.plantuml.com/,即可看见下图:
不难发现其实上面的类图中涉及到了四种不同类型的角色,分别是被观察者、观察者、具体被观察者以及具体观察者对象。
在上面的代码中不难发现一件事情,那就是使用观察者模式确实可以降低代码的耦合关系。其主要思想不难看出主要是在被观察者中遍历观察者,后调用抽象方法update
。也就是说当观察者对象很多的时候,通知的发布就是一件非常耗时的事情了,可能回影响到程序的效率。
且在观察者和被观察者之间的关系也仅是抽象的调用关系,所以在这里依赖于抽象而没有依赖于具体的实现,符合依赖倒置原则。
在使用ListView
添加数据的时候,会调用Adapter
的notifyDataSetChanged
()方法。其实这里也是观察者模式的运用,不妨来简单看下源码。比如此时自定义的Adapter
继承自ArrayAdapter
,然后从这个自定义类的notifyDataSetChanged
方法开始追踪:
从上面的流程中可以清晰的看到,其实最终的观察者模式,且被观察者对象是Adapter
中的mDataSetObservable
对象,该对象继承自Observable
对象。从Observable
定义的mObservers
为泛型类型可以看出观察者的对象这里为了通用性,直接申明了泛型。
那么,被观察者是在什么地方进行观察者的注册的呢?在Adapter
中的观察者又是什么对象?带着这两个问题,这里继续看源码。
因为前面提到了,被观察这对象是Adapter
中的mDataSetObservable
对象,为了找到在什么地方注册的观察者,所以我们需要找到被观察者的注册方法,也就是:
public abstract class Observable<T> { protected final ArrayList<T> mObservers = new ArrayList<T>(); 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); } } ... }
那么,不妨按住Ctrl
+鼠标左键进行追踪:
可以看到前面我们看过的类BaseAdapter
中调用了这个方法,所以这里切换到这个方法中:
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
...
}
使用类似的方法对类BaseAdapter
中的registerDataSetObserver
方法进行追踪,但其实没有结果了。所以这里尝试换个对象,因为适配器更新的对象为ListView
的每个Item
,按道理讲观察者对象应该就是这些Item
。所以这里找到ListView
的setAdapter
方法,这里因为我本地并没有下载相关的源码配置,所以这里androidxref来查看源码。比如ListView。
// ListView.java public void setAdapter(ListAdapter adapter) { // 如果有Adapter,且观察者存在就注销观察者 if (mAdapter != null && mDataSetObserver != null) { mAdapter.unregisterDataSetObserver(mDataSetObserver); } ... super.setAdapter(adapter); if (mAdapter != null) { ... // 创建观察者,并注册到被观察者对象 mDataSetObserver = new AdapterDataSetObserver(); mAdapter.registerDataSetObserver(mDataSetObserver); mRecycler.setViewTypeCount(mAdapter.getViewTypeCount()); ... } ... }
观察上面代码,我们知道其实观察者在这里也就是AdapterDataSetObserver
这个类的实例。不妨再次在网站上查找,这个类定义在AbsListView.java
中,为一个内部类:
class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver { @Override public void onChanged() { super.onChanged(); if (mFastScroll != null) { mFastScroll.onSectionsChanged(); } } @Override public void onInvalidated() { super.onInvalidated(); if (mFastScroll != null) { mFastScroll.onSectionsChanged(); } } }
可以看到出现了前面在DataSetObservable
被观察者类中的遍历调用onChanged
方法,继续追踪很有意思,mFastScroll.onSectionsChanged()
方法如下:
// FastScroller.java
private Adapter mListAdapter;
public void onSectionsChanged() {
mListAdapter = null;
}
所以说其实主要的处理方法其实在其父类中,这里继续追踪父类AdapterView<ListAdapter>.AdapterDataSetObserver
中的onChanged()
:
也就是观察者调用的onChanged
方法即为其触发方法。在这个方法中,会请求重新绘制ListView
界面。
到这里是否就完了?明天继续!
References
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。