设计模式之观察者模式(Observer Pattern)
一.什么是观察者模式?
把现实世界中的报纸与订阅者的关系抽象出来就是观察者模式,一种报纸对应多个订阅者,订阅者可以随时解除订阅,未订阅的读者也可以随时开始订阅。一旦有新报纸发布,所有的订阅者都会收到新内容。
在观察者模式中,报纸叫做主题Subject,订阅者叫做观察者Observer,一个Subject可以被多个Observer关注,Observer可以随时解除关注,新的Observer也可以随时关注Subject。Subject内容发生改变时,会通知所有的Observer。
二.举个例子
很多网络游戏中都有答题活动,所有参与答题活动的玩家都会同时收到题目信息(延迟忽略不计),未参与活动的玩家可以中途加入,正在答题的玩家也可以随时退出。
在此例中,游戏服务器就是“一”,玩家是“多”,题目信息就是在它们之间传递的消息。
怎样才能设计出满足以上要求的类?不妨试试观察者模式。
首先,定义Subject抽象类:
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
|
package ObserverPattern; import java.util.ArrayList; /** * @author ayqy * 定义Subject抽象类 * */ public abstract class Subject { ArrayList<Observer> observers = new ArrayList<Observer>(); //观察者列表 /** * 注册主题 * @param o 申请注册该主题的Observer */ public void registSubject(Observer o) { observers.add(o); } /** * 删除主题 * @param o */ public void reomveSubject(Observer o) { int index = observers.indexOf(o); observers.remove(index); } /** * 通知所有观察者 * @param arg 该Subject想要传递给Observers的数据 */ public void notifyObservers(Object arg) { for (Observer o : observers) o.update( this , arg); } } |
注意,这里用了抽象类而没有用接口,为什么?
因为Subject的行为是fixed的,并不需要由子类来扩展。
-------
类似的,我们定义Observer抽象类:
1
2
3
4
5
6
7
8
9
10
11
12
|
package ObserverPattern; /** * @author ayqy * 定义Observer抽象类 * */ public abstract class Observer { Subject subject = null ; //定义该Observer所关注的Subject public abstract void update(Subject subject, Object arg); //定义Observer的更新接口 } |
-------
下面开始实现我们的自定义Subject——GameServer:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package ObserverPattern; /** * @author ayqy * 定义GameServer类,继承自Subject基类,负责发布题目 * */ public class GameServer extends Subject{ Question ques; //定义题目 public Question getQues() { return ques; } public void setQues(Question q) { this .ques = q; super .notifyObservers(ques); //调用父类方法通知所有Observer } } |
P.S.GameServer类的成员变量Question是对题目信息的简单封装,Question类包含题号no与题目内容content两部分定义,以及一个toString方法,返回题目描述信息
-------
再实现我们的自定义Observer——Player:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package ObserverPattern; /** * @author ayqy * 定义PlayerA,继承自Observer基类,负责接收新题目 * */ public class PlayerA extends Observer{ public PlayerA(Subject sub) { subject = sub; } @Override public void update(Subject subject, Object arg) { Question q = (Question)arg; System.out.println( "PlayerA received " + q.toString()); } } |
P.S.为了使类层次更加清晰,此处并没有定义Player基类
很容易复制粘贴得到PlayerB与PlayerC,不再赘述
至此,我们的模拟答题活动准备工作已经结束了,下面我们需要定义一个测试类来展示观察者模式的魅力。
三.效果示例
定义如下Test类:
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
|
package ObserverPattern; /** * @author ayqy * 实现一个测试类,模拟网络游戏答题活动(游戏服务器按时更新题目信息并通知所有参与答题的玩家) * */ public class Test { public static void main(String[] args) { System.out.println( "答题活动即将开始。。" ); //创建服务器 GameServer gs = new GameServer(); //创建玩家ABC Observer playerA = new PlayerA(gs); Observer playerB = new PlayerB(gs); Observer playerC = new PlayerC(gs); //为AB注册Subject,C对答题不感兴趣,拒绝注册 gs.registSubject(playerA); gs.registSubject(playerB); System.out.println( "玩家AB成功参与答题活动。。" ); System.out.println( "答题活动正式开始。。" ); gs.setQues( new Question( 1 , "第一题" )); gs.setQues( new Question( 2 , "第二题" )); System.out.println( "玩家A不想玩了,退出答题活动。。" ); gs.reomveSubject(playerA); gs.setQues( new Question( 3 , "第三题" )); System.out.println( "玩家C想中途加入活动" ); gs.registSubject(playerC); gs.setQues( new Question( 4 , "第四题" )); System.out.println( "答题活动结束。。" ); } } |
结果示例:
四.总结
从上面的例子可以看出观察者模式的特点:
1.利用观察者模式可以轻易地建立对象之间“一对多”的依赖关系
2.利用观察者模式的机制可以很容易的实现这种依赖关系的动态维护
<声明>作者水平有限 错误在所难免 欢迎指正</声明>
<邮箱>835412398@qq.com 交流方可进步</邮箱>