观察者模式

栏目: 后端 · 发布时间: 6年前

内容简介:​ 主题+订阅者(观察者)=观察者模式​ 主题“接口”(自定义或者Observable类):remove(Object o);add(Object o);notify();​ 订阅者”接口“(自定义或者Observer接口):update();

概述

组成

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

​ 主题“接口”(自定义或者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成员变量的方法,为什么要引入这个布尔变量呢?原因参考 Why do I need to call setChanged before I notify the observers?

观察者模式

主题推送类型

​ 主题推送在 自定义 的的时候就是通过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接口的对象列表,所以我们可以随时增加观察者。事实上,在运行时我们可以用新的观察者取代旧的观察者,主题不会受到影响。同样,也可以在任何时候删除观察者。

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

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


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Learning Processing

Learning Processing

Daniel Shiffman / Morgan Kaufmann / 2008-08-15 / USD 49.95

Book Description Teaches graphic artists the fundamentals of computer programming within a visual playground! Product Description This book introduces programming concepts in the context of c......一起来看看 《Learning Processing》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

html转js在线工具
html转js在线工具

html转js在线工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换