Java设计模式 - 观察者模式
定义
观察者模式属于对象行为型模式。
在对象之间定义一对多的依赖,这样一来当一个对象改变状态,依赖它的对象都会收到通知并自动更新。
优点
1、 主题和观察者之间抽象耦合。无论什么对象主要实现了特定的接口(如本文中的Observable),就可以成为观察者对象。
2、 支持广播通信。就像一个播音员不需要知道谁在收听她的播音,只负责播音,而听不听就是听众的事了。这相当于给了你在任何时候都可以增加或者删除观察者的自由。
适用范围
当对象之间是一种一对多的关系时。好比有一份文件要同时分发给多个人。
模式结构
各个角色的作用:
Subject(主题):目标知道它的观察者。可以有任意多个观察者观察同一个目标;提供注册和删除观察者对象的接口。
Observer(观察者):为那些在目标发生改变时需获得通知的对象定义一个更新接口。
ConcreteSubject(具体的主题):当它的状态发生改变时,向它的各个观察者发出通知。
ConcreteObserver(具体观察者):实现Oberserver的更新接口以使自身状态与目标状态一致。
UML图
两种方式:
1、(推)主题对象主动推送;
2、(拉)主题对象通知观察者,观察者自己去获取需要的信息;
这两种方式会在下面介绍。
例子
写一个简单的程序来说明问题:假如现在有一个广播站在播音,有许多收音机在收听。
面向接口编程能够利用多态,从而降低对象之间的耦合度。
1 package com.tony.observer;
2
3 /**
4 * 具体的主题类必须实现这个接口
5 *
6 */
7 public interface Subject {
8
9 //注册观察者
10 public void register(Observable observer);
11 //移除观察者
12 public void remove(Observable observer);
13 //推送消息
14 public void update(String message);
15
16 }
1 package com.tony.observer;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 /**
7 * 相当于ConcreteSubject,具体的主题类
8 *
9 */
10 public class Radiostation implements Subject{
11
12 private List<Observable> observers;
13
14 public Radiostation(){
15 observers = new ArrayList<Observable>();
16 }
17
18 public void register(Observable observer){
19 observers.add(observer);
20 System.out.println("have "+observers.size()+" observer are listening...");
21 }
22
23 public void remove(Observable observer){
24 observers.remove(observer);
25 System.out.println("a observer has gone...left "+observers.size()+" observer");
26 }
27
28 public void update(String message){
29 for(Observable observer:observers){
30 observer.update(message);
31 }
32 }
33 }
1 package com.tony.observer;
2
3 /**
4 * 相当于Observer,所有的观察者必须实现这个接口
5 *
6 */
7
8 public interface Observable {
9
10 //更新数据
11 public void update(String message);
12 //成为主题对象的观察者,开始监听
13 public void register();
14 //不再监听主题对象
15 public void remove();
16
17
18 }
1 package com.tony.observer;
2
3 /**
4 * 相当于ConcreteObserver,观察者对象
5 *
6 */
7 public class Radio implements Observable {
8
9 private Subject subject;
10
11 public Radio(Subject subject){
12 this.subject = subject;
13 }
14
15 @Override
16 public void update(String message) {
17 display(message);
18 }
19
20 @Override
21 public void register() {
22 subject.register(this);
23 }
24
25 @Override
26 public void remove() {
27 subject.remove(this);
28 }
29
30 public void display(String message){
31 System.out.println("get message from radiostation:"+message);
32 }
33
34
35 }
1 package com.tony.observer;
2 /**
3 * 测试观察者模式
4 *
5 */
6 public class Test {
7 public static void main(String[] args) {
8
9 Subject radiostation = new Radiostation();
10
11 Radio o1 = new Radio(radiostation);
12 Radio o2 = new Radio(radiostation);
13
14 //注册
15 o1.register();
16 o2.register();
17
18 //更新消息
19 radiostation.update("hello world");
20 radiostation.update("over");
21
22 //退出监听
23 o1.remove();
24 o2.remove();
25 }
26 }
运行结果:
这种实现方法属于“推”:主题主动将数据推送给观察者。其实还有另外一种叫做“拉”的方式:主题不主动将数据推送给观察者,只是给它们一个更新提示,接收的权利在观察者手上!
与“拉”相比较,“推”有个很大的缺点:当推送的数据量很大的时候,会对程序运行性能产生影响!就像安卓手机某些后台应用:不经过你的同意自动将服务器中的一些数据下载的你的手机上。而“拉”这种方式就没有这样的情况,就好像你关注的公众号一样:给你推送消息时并没有把所有内容都推送过来,只是把一个标题发给你,想不想看在于你。
但是”推“这种方式比较符合设计模式的原则,所以一般使用的都是这种方式。
总结
观察者模式应用很广,不仅在你写代码的时候用到,你在生活当中也时常能够遇到:比如走在校园里收听到的广播:广播站是主题,你就是观察者。当你进入到能够听见广播的范围时,你就成了一个观察者:播音主持说的话就是要更新的数据,通过广播站这个主题将数据发送给你们,你们负责收听。
必须能够熟练使用。
其他模式:设计模式专栏
参考文献
《Head First 设计模式》
《设计模式》