观察者模式

定义

观察者模式定义了对象之间的一对多依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

关键字

  • Observable
    • 即被观察者,也可以被叫做主题(Subject)。通常有注册方法(register),取消注册方法(remove)和通知方法(notify)
  • Observer
    • 即观察者,可以接收到主题的更新。当对某个主题感兴趣的时候需要注册自己,在不需要接收更新时进行注销操作。

例子与应用

  1. 用户从报社订阅报纸,报社和用户之间是一对多依赖,用户可以在报社订阅(register)报纸,报社可以把最新的报纸发给用户(notify),用户自动收到更新。在用户不需要的时候还可以取消注册(remove)
  2. Android中的EventBus,Rxjava的实现都是基于观察者模式的思想。再比如回调函数:Android中对Button的点击监听等等。
  3. 观察者模式可以用来解耦

观察者模式代码实现

现在我们用代码来实现上面订阅报纸的例子:

  • NewProvider作为对于报社的抽象,每隔两秒钟向用户发送报纸。
  • User作为用户的抽象,可以收到报纸。
  • NewsModel作为对报纸本身的抽象。
1
2
3
4
5
6
7
8
9
10
/**
* 被观察者接口定义
*/
public interface MyObserverable {
void register(MyObserver myObserver);

void remove(MyObserver myObserver);

void send(NewsModel model);
}
1
2
3
4
5
6
/**
* 观察者接口定义
*/
public interface MyObserver {
void receive(NewsModel model);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
* 对于报社的抽象,实现了被观察者接口,每隔2s发送一次报纸
*/
public class NewsProvider implements MyObserverable {
private static final long DELAY = 2 * 1000;
private List<MyObserver> mObservers;//我们用一个List来维护所有的观察者对象

public NewsProvider() {
mObservers = new ArrayList<>();
generateNews();
}

/**
* 模拟产生新闻,每个2s发送一次
*/
private void generateNews() {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
int titleCount = 1;
int contentCount = 1;

@Override
public void run() {
send(new NewsModel("title:" + titleCount++, "content:" + contentCount++));
}
}, DELAY, 1000);
}

@Override
public void register(MyObserver myObserver) {
if (myObserver == null)
return;
synchronized (this) {
if (!mObservers.contains(myObserver))
mObservers.add(myObserver);
}
}

@Override
public synchronized void remove(MyObserver myObserver) {
mObservers.remove(myObserver);
}

@Override
public synchronized void send(NewsModel model) {
for (MyObserver observer : mObservers) {
observer.receive(model);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 对于用户的抽象
*/
public class User implements MyObserver {
private String mName;

public User(String name) {
mName = name;
}

@Override
public void receive(NewsModel model) {
System.out.println(mName + " receive news:" + model.getTitle() + " " + model.getContent());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class NewsModel {
private String title;
private String content;

public NewsModel(String title, String content) {
this.title = title;
this.content = content;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
NewsProvider provider = new NewsProvider();
User user;
for (int i = 0; i < 10; i++) {
user = new User("user:"+i);
provider.register(user);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
user:0 receive news:title:1  content:1
user:1 receive news:title:1 content:1
user:2 receive news:title:1 content:1
user:3 receive news:title:1 content:1
user:4 receive news:title:1 content:1
user:5 receive news:title:1 content:1
user:6 receive news:title:1 content:1
user:7 receive news:title:1 content:1
user:8 receive news:title:1 content:1
user:9 receive news:title:1 content:1
user:0 receive news:title:2 content:2
user:1 receive news:title:2 content:2
user:2 receive news:title:2 content:2
user:3 receive news:title:2 content:2
user:4 receive news:title:2 content:2
user:5 receive news:title:2 content:2
user:6 receive news:title:2 content:2
user:7 receive news:title:2 content:2
user:8 receive news:title:2 content:2
user:9 receive news:title:2 content:2
...

JDK观察者模式API

在JDK的util包内Java为我们提供了一套观察者模式的实现,在使用的时候我们只需要继承Observable和Observer类即可。此外在JDK的实现中还增加了一个布尔类型的changed域,通过设置这个变量来确定是否通知观察者。

Demo1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class NewsProvider extends Observable {
private static final long DELAY = 2 * 1000;
private List<MyObserver> mObservers;//我们用一个List来维护所有的观察者对象

public NewsProvider() {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
private int titleCount = 1;
private int contentCount = 1;
@Override
public void run() {
setChanged();//调用setChagned方法,将changed域设置为true,这样才能通知到观察者们
notifyObservers(new NewsModel("title:" + titleCount++, "content:" + contentCount++));
}
}, DELAY, 1000);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public class User implements Observer {
private String mName;

public User(String name) {
mName = name;
}

@Override
public void update(Observable observable, Object data) {
NewsModel model = (NewsModel) data;
System.out.println(mName + " receive news:" + model.getTitle() + " " + model.getContent());
}
}
1
2
3
4
5
6
7
public class Test {
public static void main(String[] args) {
NewsProvider provider = new NewsProvider();
User user = new User("张三");
provider.addObserver(user);
}
}

Demo2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Publish extends Observable {
private String data = "";

public String getData() {
return data;
}

public void setData(String data) {
if (!this.data.equals(data)){
this.data = data;
setChanged();//改变通知者的状态
}
notifyObservers();//调用父类Observable方法,通知所有观察者
}
}
1
2
3
4
5
6
7
8
9
10
11
public class Subscribe implements Observer {

public Subscribe(Observable o) {
o.addObserver(this);//将该观察者放入待通知观察者里
}

@Override
public void update(Observable o, Object arg) {
System.out.println("收到通知:" + ((Publish) o).getData());
}
}
1
2
3
4
5
6
7
public class Test {
public static void main(String[] args) {
Publish publish = new Publish();
Subscribe subscribe = new Subscribe(publish);
publish.setData("开始");
}
}

回调函数与观察者模式

你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。

在这个例子里:

  1. 你的电话号码就叫回调函数;
  2. 你把电话留给店员就叫登记回调函数;
  3. 店里后来有货了叫做触发了回调关联的事件;
  4. 店员给你打电话叫做调用回调函数;
  5. 你到店里去取货叫做响应回调事件。

在Android中我们有一个常用的回调:对于View点击事件的监听。

通常在我们使用的时候是这样的:

1
2
3
4
5
6
xxxView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// do something
}
});

这样我们就注册好了一个回调函数。

我们可以在View的源码里发现这个接口:

1
2
3
4
5
6
7
8
9
10
11
/**
* Interface definition for a callback to be invoked when a view is clicked.
*/
public interface OnClickListener {
/**
* Called when a view has been clicked.
*
* @param v The view that was clicked.
*/
void onClick(View v);
}

当你setClickListener的时候在View的源码中可以看到对本地OnClickListener的初始化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* Register a callback to be invoked when this view is clicked. If this view is not
* clickable, it becomes clickable.
*
* @param l The callback that will run
*
* @see #setClickable(boolean)
*/
public void setOnClickListener(@Nullable OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
getListenerInfo().mOnClickListener = l;
}

当你的点击到一个View后Android系统经过一系列的调用最后到了View的performClick方法中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* Call this view's OnClickListener, if it is defined. Performs all normal
* actions associated with clicking: reporting accessibility event, playing
* a sound, etc.
*
* @return True there was an assigned OnClickListener that was called, false
* otherwise is returned.
*/
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}

sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}

就在这里,触发了你的onClick方法,然后执行方法体。

这里我们的被观察者就是View,他的注册方法(register)就是setOnClickListener(),通知方法就是performClick;而OnClickListener就是观察者。只不过这里的只能注册一个观察对象而已。

0%