本文目录
观察者模式属于行为型模式。
一、观察者模式介绍
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。
观察者一般可以看做是第三者,举例说明:
(1)比如在学校上自习的时候,大家肯定都有过交头接耳、各种玩耍的经历,这时总会有一个“放风”的小伙伴,当老师即将出现时及时“通知”大家老师来了。
(2)比如,拍卖会的时候,大家相互叫价,拍卖师会观察最高标价,然后通知给其它竞价者竞价,这就是一个观察者模式。
(3)对于观察者模式而言,肯定有观察者和被观察者之分。比如在一个目录下建立一个文件,这时系统会通知目录管理器增加目录,并通知磁盘减少空间,在这里,文件就是观察者,目录管理器和磁盘就是被观察者。
观察者模式(Observer),又叫发布-订阅模式(Publish/Subscribe),定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。UML结构图如下:
截图
其中,Subject类是主题,它把所有对观察者对象的引用文件存在了一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供了一个接口,可以增加和删除观察者对象;Observer类是抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己;ConcreteSubject类是具体主题,将有关状态存入具体观察者对象,在具体主题内部状态改变时,给所有登记过的观察者发出通知;ConcreteObserver是具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协同。
步骤 1. 抽象观察者Observer
观察者一般是一个接口,每一个实现该接口的实现类都是具体观察者。
package com.iot.practice.designpattern.observer;
/**
* <p>Observer 此接口用于:</p>
* <p>@author:hujm</p>
* <p>@date:2021年02月25日 14:47</p>
* <p>@remark:</p>
*/
public interface Observer {
/**
* 更新
*/
public void update();
}
步骤 2:主题Subject
首先定义一个观察者数组,并实现增、删及通知操作。它的职责很简单,就是定义谁能观察,谁不能观察,用Vector是线程同步的,比较安全,也可以使用ArrayList,是线程异步的,但不安全。
package com.iot.practice.designpattern.observer;
import java.util.Vector;
/**
* <p>Subject 此类用于:</p>
* <p>@author:hujm</p>
* <p>@date:2021年02月25日 14:45</p>
* <p>@remark:主题Subject</p>
*/
public class Subject {
/**
* 观察者数组集合
*/
private Vector<Observer> vector = new Vector<>();
/**
* 增加一个观察者
*
* @param observer 观察者
*/
public void addObserver(Observer observer) {
this.vector.add(observer);
}
/**
* 删除一个观察者
*
* @param observer 观察者
*/
public void deleteObserver(Observer observer) {
this.vector.remove(observer);
}
/**
* 通知所有观察者
*/
public void notifyObserver() {
for (Observer observer : this.vector) {
observer.update();
}
}
}
步骤 3:具体主题
继承Subject类,在这里实现具体业务,在具体项目中,该类会有很多变种。
package com.iot.practice.designpattern.observer;
/**
* <p>ConcreteSubject 此类用于:</p>
* <p>@author:hujm</p>
* <p>@date:2021年02月25日 14:56</p>
* <p>@remark:继承Subject类,在这里实现具体业务,在具体项目中,该类会有很多变种</p>
*/
public class ConcreteSubject extends Subject {
/**
* 具体业务逻辑
*/
public void doSomething() {
super.notifyObserver();
}
}
步骤 4:具体观察者
实现Observer接口。
package com.iot.practice.designpattern.observer;
/**
* <p>ConcreteObserver 此类用于:</p>
* <p>@author:hujm</p>
* <p>@date:2021年02月25日 14:59</p>
* <p>@remark:具体观察者,实现Observer接口。</p>
*/
public class ConcreteObserver implements Observer {
@Override
public void update() {
System.out.println("收到通知,正在进行处理...");
}
}
步骤 5:Client客户端
首先创建一个被观察者,然后定义一个观察者,将该被观察者添加到该观察者的观察者数组中,进行测试。
package com.iot.practice.designpattern.observer;
/**
* <p>Client 此类用于:</p>
* <p>@author:hujm</p>
* <p>@date:2021年02月25日 15:01</p>
* <p>@remark:Client客户端,首先创建一个被观察者,然后定义一个观察者,将该被观察者添加到该观察者的观察者数组中,进行测试。</p>
*/
public class Client {
public static void main(String[] args) {
// 创建一个主题
ConcreteSubject subject = new ConcreteSubject();
// 定义一个观察者
Observer observer = new ConcreteObserver();
// 将一个观察者添加到主题中
subject.addObserver(observer);
// 通知
subject.doSomething();
}
}
运行结果如下图所示:
二、观察者模式的应用
意图:是定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
如何解决:使用面向对象技术,可以将这种依赖关系弱化。
关键代码:在抽象类里有一个 ArrayList 存放观察者们。
2.1 应用实例
(1)比如在学校上自习的时候,大家肯定都有过交头接耳、各种玩耍的经历,这时总会有一个“放风”的小伙伴,当老师即将出现时及时“通知”大家老师来了。
(2)比如,拍卖会的时候,大家相互叫价,拍卖师会观察最高标价,然后通知给其它竞价者竞价,这就是一个观察者模式。
(3)对于观察者模式而言,肯定有观察者和被观察者之分。比如在一个目录下建立一个文件,这时系统会通知目录管理器增加目录,并通知磁盘减少空间,在这里,文件就是观察者,目录管理器和磁盘就是被观察者。
2.2 优缺点
优点:
(1)观察者和被观察者是抽象耦合的。
(2)建立一套触发机制。
缺点:
(1)如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
(2)如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
(3)观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
2.3 使用场景
(1)一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
(2)一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
(3)一个对象必须通知其他对象,而并不知道这些对象是谁。
(4)需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
2.4 注意事项
(1)JAVA 中已经有了对观察者模式的支持类。
(2)避免循环引用。
(3)如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
三、源码分析
完结!