观察者模式
# 观察者模式
# 定义
观察者模式(Observer Pattern)也叫做发布订阅模式(Publish/subscribe),它是一个在项目中经常使用的模式。定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。通过这种方式来达到减少依赖关系,解耦合的作用。
我们先来解释一下观察者模式的几个角色名称:
● Subject被观察者 定义被观察者必须实现的职责,它必须能够动态地增加、取消观察者。它一般是抽象类或者是实现类,仅仅完成作为被观察者必须实现的职责:管理观察者并通知观察者。
● Observer观察者 观察者接收到消息后,即进行update(更新方法)操作,对接收到的信息进行处理。
● ConcreteSubject具体的被观察者 定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知。
● ConcreteObserver具体的观察者 每个观察在接收到消息后的处理反应是不同,各个观察者有自己的处理逻辑。
一般类图是这样的
在软件系统中经常会有这样的需求:如果一个对象的状态发生改变,某些与它相关的对象也要随之做出相应的变化。比如说邮件系统,你在收到一封邮件的时候经常桌面上会有通知,告诉你有邮件收到了。 观察者模式特点:那就是一个对象要时刻监听着另一个对象,只要它的状态一发生改变,自己随之要做出相应的行动。
# 简单代码实现
1、被观察者
被观察者的职责非常简单,就是定义谁能够观察,谁不能观察
/**
* 被观察者
* @version 1.0.0
* @date 2021/12/01 16:39
*/
public class Subject {
/**
* 定义一个观察者数组
*/
private List<Observer> observerList = new ArrayList<>();
/**
* 增加一个观察者
* @param o
*/
public void addObserver(Observer o){
this.observerList.add(o);
}
/**
* 删除一个观察者
* @param o
*/
public void delObserver(Observer o){
this.observerList.remove(o);
}
/**
* 通知所有观察者
*/
public void notifyObservers(){
for(Observer o :this.observerList){
o.update();
}
}
}
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
2、具体被观察者
/**
* 具体被观察者
* @version 1.0.0
* @date 2021/12/01 17:04
*/
public class ConcreteSubject extends Subject{
//具体的业务
public void doSomething(){
/*
* do something
*/
super.notifyObservers();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
3、观察者
/**
* 观察者
* @version 1.0.0
* @date 2021/12/01 16:41
*/
public interface Observer {
//更新方法
public void update();
}
2
3
4
5
6
7
8
9
10
11
4、具体观察者
/**
* 具体观察者
* @version 1.0.0
* @date 2021/12/01 16:46
*/
public class ConcreteObserver implements Observer{
/**
* 实现更新方法
*/
@Override
public void update() {
System.out.println("接收到信息,并进行处理!");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
5、测试类
public class Client {
public static void main(String[] args) {
//创建一个被观察者
ConcreteSubject subject = new ConcreteSubject();
//定义一个观察者
Observer obs= new ConcreteObserver();
//观察者观察被观察者
subject.addObserver(obs);
//观察者开始活动了
subject.doSomething();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 优点
1、观察者和被观察者之间是抽象耦合 不管是增加观察者还是被观察者都非常容易扩展,而且在Java中都已经实现的抽象层级的定义,在系统扩展方面更是得心应手。
2、建立一套触发机制
根据单一职责原则,每个类的职责是单一的,那么怎么把各个单一的职责串联成真实世界的复杂的逻辑关系呢?观察者模式是一种常用的触发机制,它形成一条触发链,依次对各个观察者的方法进行处理。但同时,这也算是观察者模式一个缺点,由于是链式触发,当观察者比较多的时候,性能问题是比较令人担忧的。
# 使用场景
- 关联行为场景。需要注意的是,关联行为是可拆分的,而不是“组合”关系。
- 事件多级触发场景。当一个对象在不知道对方具体是如何实现时需要通知其它对象。
- 跨系统的消息交换场景,如消息队列的处理机制。
- 当一个对象改变需要通知不确定数的对象时
# 实际应用
发送消息,消息是被观察者,用户是观察者
1、被观察者
public interface Subject {
// 添加订阅关系
void attach(Observer observer);
// 移除订阅关系
void detach(Observer observer);
// 通知订阅者
void notifyObservers(String message);
}
2
3
4
5
6
7
8
9
10
2、具体被观察者
public class ConcreteSubject implements Subject{
// 订阅者容器
private List<Observer> observers = new ArrayList<Observer>();
@Override
public void attach(Observer observer) {
// 添加订阅关系
observers.add(observer);
}
@Override
public void detach(Observer observer) {
// 移除订阅关系
observers.remove(observer);
}
@Override
public void notifyObservers(String message) {
// 通知订阅者
for (Observer observer : observers) {
observer.update(message);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
3、观察者
public interface Observer {
//更新方法
public void update(String message);
}
2
3
4
5
6
4、具体观察者
public class ConcreteObserver implements Observer{
@Override
public void update(String message) {
// 模拟处理业务逻辑
System.out.println("用户收到消息:" + message);
}
}
2
3
4
5
6
7
8
5、测试
public class Client {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
// 这里假设是增加新用户
subject.attach(new ConcreteObserver());
ConcreteObserver twoObserver = new ConcreteObserver();
subject.attach(twoObserver);
// 发送朋友圈动态
subject.notifyObservers("第一条消息");
subject.detach(twoObserver);
subject.notifyObservers("第二个消息");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
运行结果
用户收到消息:第一条消息 用户收到消息:第一条消息 用户收到消息:第二个消息
可以看到,ConcreteSubject 维护了一个订阅关系,在通过notifyObservers 方法通知订阅者之后,观察者都获取到消息从而处理自己的业务逻辑。如果有新的业务添加进来,我们也只需要创建一个新的订阅者,并且维护到observers 容器中即可,也符合我们的开闭原则。
上面代码依赖关系
# 总结
观察者模式是围绕了解耦的思想来写的,观察者模式作为行为型设计模式,主要也是为了不同的业务行为的代码解耦。
合理的使用设计模式可以使代码结构更加清晰,同时还能满足不同的小模块符合单一职责,以及开闭原则,提高代码的可扩展性,维护成本低的特点。