博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
观察者模式
阅读量:6851 次
发布时间:2019-06-26

本文共 2649 字,大约阅读时间需要 8 分钟。

概述

组成

​ 主题+订阅者(观察者)=观察者模式

​ 主题“接口”(自定义或者Observable类):remove(Object o);add(Object o);notify();
​ 订阅者”接口“(自定义或者Observer接口):update();

定义

图片描述

​ 观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它所有的依赖者都会收到通知并自动更新。但注意的是,这种一对多的关系并不是绝对的,一个观察者同样可以观察多个主题。

设计原则

为了交互对象之间的松耦合而努力。松耦合的设计之所以能让我们建立有弹性的OO系统,能够够应对变化,是因为对象之间的互相依赖降到了最低。

松耦合:两个对象松耦合,它们依然可以交互,但是不清楚彼此的细节。

观察者模式提供了一种对象设计,让主题和观察者之间松耦合:关于观察者的一切,主题只知道观察者实现了某个接口(也就是Observer接口)。主题不需要知道观察者的具体类型是哪种、做了些什么或其他任何细节。

实现

​ 实现观察者模式的方式不止一种,但其中以包含Subject和Observer接口的类设计最为常见。

​ 注意:在例子中还提供了主题是基类观察者是接口的实例,不推荐的主要原因是继承类只能继承一个这里继承了就无法继承其他的。

​ 观察者模式有主题推送观察者获取两种类型:

订阅/取消功能

​ 无论是对于主题推送类型还是观察者获取类型,都有一个这样的功能的实现:订阅者进行订阅和取消订阅为什么都是要由订阅者来做,这就是上面的松耦合的设计原则,主题不需要知道订阅者的状态,只需要知道它实现了订阅者接口。为了实现这个功能,首先需要在主题的接口或者基类中中定义相应的public的订阅和取消订阅的方法,比如api中Observable类所实现的:

` 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 synchronized void deleteObservers() {

​ obs.removeAllElements();
​ }`

​ 上面的这些方法已经能够让订阅者实现自行订阅的不同主题功能(当订阅者只订阅一个主题时,订阅可以放在构造函数中来实现),除此之外,如果想要订阅者能够取消订阅,还需要在订阅者对象中保留相应主题对象的引用(在api中Observer是一个接口),当要取消订阅的时候直接通过主题引用调用deleteObserver(this)方法即可。这样就能够实现订阅者自己来进行订阅主题和取消订阅主题。当一个订阅者订阅多个具体主题实现对象的时候,可以考虑使用容器来进行管理,在取消订阅的时候也可可以遍历容器找到相应的Class类型的主题对象或者找到相同的的主题对象,然后再地调用deleteObserver(this)即可。

​ api中订阅者和主题两者接口/基类中的成员,如下图所示,在主题基类中可以看到除了我们上面说的:通知、添加订阅者、删除订阅者这些方法之外,还有一系列关于changed成员变量的方法,为什么要引入这个布尔变量呢?原因参考

图片描述

主题推送类型

​ 主题推送在自定义的的时候就是通过notify()在内部遍历订阅者数组将全部参数统统发给所有订阅者,在这里因为不同订阅者所需的参数不同,所以必须将所有参数都传递过去。订阅者中设置了与要接受的某些参数对应类型的成员变量。(实例可参考)

​ 主题推送在基于api实现的时候就是当参数改变时通过调用notifyObservers(Object arg)将参数放在arg里。在这里因为不同订阅者所需的参数不同,所以必须将所有参数都传递过去。然后订阅者在重写的update(Observable o, Object arg)里去取就行,其他的和自定义基本相同。其中第一个Object o是将主题的引用传递过去了,这是为了让观察者能够知道是哪个主题的通知它的。因为一个观察者可以观察多个主题。

观察者获取类型

​ 观察者获取在自定义的时候就是在主题的具体实现里添加上所有数据的get方法(因为数据都是私有的),然后在订阅者在重写的当中update(Object o);其中o是将主题的引用传递过去,通过主题引用来调用get方法获取所想要的参数(不一定是全部了)。当然也可以不这样实现update,直接只是update(),但是需要在订阅者对象中设置有主题对象的引用,同样是根据引用调用get方法获取参数,但这样的做法缺点在于不利于应对一个订阅者订阅多个主题对象的情况。

​ 观察者获取在基于api实现的时候就是当参数改变时通过调用notifyObservers()从而会调用notifyObservers(null),然后调用订阅者重写的update(Observable o, null)方法里来进一步通过主题的引用来调用get方法获取所想要的参数(不一定是全部了)(实例可参考)。其中第一个Object o是将主题的引用传递过去了,这是为了让观察者能够知道是哪个主题的通知它的。因为一个观察者可以观察多个主题。

基于API实现的缺点

​ 基于api实现主题有个显著的缺点,因为Observable是一个类,而不是接口,如果继承了Observable类,那就无法继承其他类了。如果必须要继承其他类,那还是需要自行来实现主题接口和主题的具体实现类

优点

​ 任何时候我们都可以随时增加观察者,因为主题唯一依赖的东西是一个实现Observer接口的对象列表,所以我们可以随时增加观察者。事实上,在运行时我们可以用新的观察者取代旧的观察者,主题不会受到影响。同样,也可以在任何时候删除观察者。

​ 有新类型的观察者出现时,主题的代码不需要修改。假如我们有个新的具体类需要当观察者,我们不需要为了兼容新类型而修改主题的代码,所要做的是在新的类里实现此观察者接口,然后注册为观察者即可。

​ 改变主题或观察者其中一方,并不会影响另一方。因为两者是松耦合的,所以只要他们之间的接口仍被遵守,我们就可以自由地改变他们。

转载地址:http://ckgul.baihongyu.com/

你可能感兴趣的文章
PHP适合做大型网站吗?
查看>>
lua入门之二:c/c++ 调用lua及多个函数返回值的获取
查看>>
C使用FILE指针文件操作
查看>>
cobbler pxe-menu
查看>>
openssl 非对称加密 RSA 加密解密以及签名验证签名
查看>>
MyBatis Generator生成DAO——序列化
查看>>
算法笔记_175:历届试题 蚂蚁感冒(Java)
查看>>
pdb文件是什么
查看>>
.Net 跳转
查看>>
vim搭建笔记
查看>>
人要有畏惧之心
查看>>
SpringCloud学习笔记(4)——Zuul
查看>>
mysql主从备份及原理分析
查看>>
Docker 入门 --- 命令总结
查看>>
MySQL 批量写入数据报错:mysql_query:Lost connection to MySQL server during query
查看>>
【spring boot】spring boot中使用@RestController不起作用,不返回json,依旧去找访问接口的请求地址对应的页面...
查看>>
简约响应式布局样式
查看>>
iOS平台XML解析类库对比和安装说明
查看>>
各种纪念-好久没更新了
查看>>
渴望出差
查看>>