观察者模式的学习和应用

停了有些日子没有管我的博客,这段时间经历了期末考试服务外包的比赛。今天刚刚结束,刚从武汉回到家里休息了一天,今天早上还约了人来把家里的光纤修好了。现在可以好好整理一下在比赛时候的一些小小的总结。这次的比赛收获的更多是一种应用和熟练,把自己之前学过的一些知识得以运用(才发现是如此的生疏。和队友的构想,讨论,舍弃太自我的想法,长时间的配合和解决一个一个问题,熬过一个一个夜,慢慢整理出的作品。也算是费尽一番心血。

这篇博客就选取其中的一点心得作为记录,作为对观察者模式的学习和应用总结。

何为观察者模式

观察者模式定义了对象间的一种一对多依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。

它将观察者和被观察者的对象分离开。提高了应用程序的可维护性和重用性。

实现观察者模式有很多形式,一种是“注册—通知—撤销注册”的形式。

观察者Observer:所有潜在的观察者必须实现观察者接口,这个接口只有update方法,当主题改变时,它被调用。
具体观察者ConcreteObserver: 具体观察者可以是任何实现了Observer接口的类。观察者必须注册具体主题,一边接收更新。
可观察者Subject: 主题接口,即可观察者Observable,对象使用此接口注册为观察者,或者把自己从观察者中删除,每个主题可以有多个观察者。
具体可观察者ConcreteSubject: 一个具体主题实现了主题接口,除了注册和撤销之外,具体主题还实现了notifyObservers()方法,这个方法用来在主题状态改变时更新所有观察者。具体主题也可能有设置和获取状态的方法。
类图
常见的例子:

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
public interface Observer
{
public void update(float temprature);
}
public class ConcreteObserver implements Observer
{
private float temperature;
private final Subject subject;
public ConcreteObserver(final Subject subject)
{
this.subject = subject;
this.subject.registerObserver(this);
}
public float getTemperature()
{
return temperature;
}
public void setTemperature(final float temperature)
{
this.temperature = temperature;
}
@Override
public void update(final float temperature)
{
this.temperature = temperature;
}
}
public interface Subject
{
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}
public class ConcreteSubject implements Subject
{
private final List<Observer> observers;
private float temperature;
public float getTemperature()
{
return temperature;
}
private void temperatureChanged()
{
this.notifyObservers();
}
public void setTemperature(final float temperature)
{
this.temperature = temperature;
this.temperatureChanged();
}
public ConcreteSubject()
{
observers = new ArrayList<Observer>();
}
@Override
public void registerObserver(final Observer o)
{
observers.add(o);
}
@Override
public void removeObserver(final Observer o)
{
if (observers.indexOf(o) >= 0)
{
observers.remove(o);
}
}
@Override
public void notifyObservers()
{
for (final Observer o : observers)
{
o.update(temperature);
}
}
}
public class Client
{
public static void main(final String[] args)
{
final ConcreteSubject sb = new ConcreteSubject();
sb.setTemperature((float) 20.00);
final Observer o = new ConcreteObserver(sb);
sb.setTemperature((float) 21.00);
}
}

观察者模式的应用场景:

1、 对一个对象状态的更新,需要其他对象同步更新,而且其他对象的数量动态可变。

2、 对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节。

观察者模式的优点:

1、 Subject和Observer之间是松偶合的,分别可以各自独立改变。

2、 Subject在发送广播通知的时候,无须指定具体的Observer,Observer可以自己决定是否要订阅Subject的通知。

3、 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合。

观察者模式的缺陷:

1、 松偶合导致代码关系不明显,有时可能难以理解。(废话)

2、 如果一个Subject被大量Observer订阅的话,在广播通知的时候可能会有效率问题。(毕竟只是简单的遍历)

为何使用观察者模式


在进行校园广播项目的时候,我遇到了一个问题,那就是我自定义实现了一个Dialog,这个Dialog用来展示举报界面并且携带信息返回。我要在Dialogshow之后拿到返回的值,但是由于时间上的异步性,我没有办法准确的控制好时间,等待信息返回之后再进行别的操作,于是乎这个时间差我只能够通过一个简化的观察者模式来解决。

我的思路就是利用自定义Dialog类中的接口定义拿的这个步骤,通过这个接口传递信息。在目标类实现这个接口中拿的操作,通过监听使得可观察者在信息改变时获取到改变。

废话少说,具体代码在下面,通过一个简化过的观察者模式(伪)的例子来了解我的方法。

一个简化的例子


自定义Dialog

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
public class ReportDialog {
Context act = null;
private int result = 7;
private ReportResult reportResult;
public ReportDialog(Context act) {
this.act = act;
}
public void show(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(act);
builder.setTitle("选择一项举报该电台:");
builder.setIcon(R.drawable.report_icon);
final String[] items = new String[]{"含有暴力色情等内容。", "言论有不当、虚假内容。", "含有引起不适的内容。"};
builder.setSingleChoiceItems(items,3, new DialogInterface.OnClickListener() {/*设置单选条件的点击事件*/
@Override
public void onClick(DialogInterface dialog, int which) {
result = which;
}
});
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(act, "感谢您的举报~", Toast.LENGTH_SHORT).show();
reportResult.getResult(result);
}
});
builder.setNegativeButton("CANCEL", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(act, "举报取消啦~", Toast.LENGTH_SHORT).show();
result = 7;
reportResult.getResult(result);
}
});
builder.setCancelable(false);
builder.show();
}
public interface ReportResult{
public void getResult(int result);
}
public void getReportResultListener(ReportResult reportResult){
this.reportResult=reportResult;
}
}

具体的实现类部分:
(一部分是使用retrofit2和后台进行交互)

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
private class Report implements ReportDialog.ReportResult {
Context context;
public Report(Context context, View v) {
this.context = context;
ReportDialog reportDialog = new ReportDialog(context);
reportDialog.show(v);
reportDialog.getReportResultListener(this);
}
@Override
public void getResult(int result) {
if (result != 7) {
Date now = new Date();
long nowtime = now.getTime();
ReportApi reportApi = new ReportApi();
ReportService reportService = reportApi.getService();
ReportBean reportBean = new ReportBean();
reportBean.setChannelID(channelId);
reportBean.setEvent(result);
reportBean.setTime(nowtime);
reportBean.setUserID(channelHostId);
Gson gson = new Gson();
final RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), gson.toJson(reportBean));
Call<isTrueBean> call_beginBroadcast = reportService.getState(requestBody);
call_beginBroadcast.enqueue(new Callback<isTrueBean>() {
@Override
public void onResponse(Call<isTrueBean> call, Response<isTrueBean> response) {
if (response.body() != null) {
int result = response.body().getSign();
if (result == 1) {
Log.i(TAG, "举报成功");
} else {
Log.i(TAG, "举报失败");
}
} else {
Log.i(TAG, "举报返回空");
}
}
@Override
public void onFailure(Call<isTrueBean> call, Throwable t) {
Log.i(TAG, "举报返回失败");
}
});
}
}
}

这样应该很明显的看我是用什么方法解决了问题的。可能跟经典的的观察者模式有些出入,但是达到了实际需要的效果。

大胖倪的慢灵魂 wechat
感觉有用打赏一个呗~