【设计模式】行为型模式—— 观察者模式(Observer)

观察者模式定义

定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新


观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。


举个例子:完农药开黑的时候。5人一队,推塔游戏。当其中防御塔或者水晶有一方遭受其他攻击的时候。会发出一个遭受攻击的指令通知所有队友。进而队友前往支援。

总结如下:水晶遭受攻击(观察目标) –> 通知队友(观察者) –> 队友响应


模式动机

一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展,这就是观察者模式的模式动机

模式优点

  • 观察者模式可以实现表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色。
  • 观察者模式在观察目标和观察者之间建立一个抽象的耦合
  • 观察者模式支持广播通信
  • 观察者模式符合“开闭原则”的要求

模式缺点

  • 如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间
  • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
  • 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化

具体应用

  • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
  • 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
  • 一个对象必须通知其他对象,而并不知道这些对象是谁。
  • 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。

实际场景

如某电子商务网站可以在执行发送操作后给用户多个发送商品打折信息,某团队战斗游戏中某队友牺牲将给所有成员提示等等,凡是涉及到一对一或者一对多的对象交互场景都可以使用观察者模式

模式结构

这里写图片描述

时序图

这里写图片描述

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 抽象观察目标
*/
abstract class Subject {
//定义一个观察者集合用于存储所有观察者对象
protected List<Observer> observers = Lists.newArrayList();
//注册方法,用于向观察者集合中增加一个观察者
public void attach(Observer observer) {
observers.add(observer);
}
//注销方法,用于在观察者集合中删除一个观察者
public void detach(Observer observer) {
observers.remove(observer);
}
//声明抽象通知方法
public abstract void notify();
}

1
2
3
4
5
6
7
class ConcreteSubject extends Subject {
//实现通知方法
public void notify() {
//遍历观察者集合,调用每一个观察者的响应方法
observers.foreach(observer -> observer.update());
}
}

1
2
3
4
interface Observer {
//声明响应方法
public void update();
}

1
2
3
4
5
6
class ConcreteObserver implements Observer {
//实现响应方法
public void update() {
//具体响应代码
}
}

在有些更加复杂的情况下,具体观察者类ConcreteObserver的update()方法在执行时需要使用到具体目标类ConcreteSubject中的状态(属性)

如果ConcreteObserver的update()方法不需要使用到ConcreteSubject中的状态属性,则可以对观察者模式的标准结构进行简化,在具体观察者ConcreteObserver和具体目标ConcreteSubject之间无须维持对象引用

如果在具体层具有关联关系,系统的扩展性将受到一定的影响,增加新的具体目标类有时候需要修改原有观察者的代码,在一定程度上违反了“开闭原则”,但是如果原有观察者类无须关联新增的具体目标,则系统扩展性不受影响。

扩展

jdk对观察者模式支持

1
java.util.Observable

这里写图片描述

  1. changed 改变状态标志 相当于 state
  2. clearChanged() 将状态变为false
  3. hasChanged() 检测状态是否改变
  4. countObservers() 用于返回队列中观察者的数量。

这里写图片描述

当观察目标的状态发生变化时,该方法将会被调用,在Observer的子类中将实现update()方法,即具体观察者可以根据需要具有不同的更新行为。当调用观察目标类Observable的notifyObservers()方法时,将执行观察者类中的update()方法

观察者模式和Java事件处理

在JDK 1.1及以后的各个版本中,事件处理模型采用基于观察者模式的委派事件模型(DEM),即一个Java组件所引发的事件并不由引发事件的对象自己来负责处理,而是委派给独立的事件处理对象负责。

在DEM模型中,目标角色(如界面组件)负责发布事件,而观察者角色(事件处理者)可以向目标订阅它所感兴趣的事件。当一个具体目标产生一个事件时,它将通知所有订阅者。

事件的发布者称为事件源(Event Source),而订阅者称为事件监听器(Event Listener)。在这个过程中还可以通过事件对象(Event Object)来传递与事件相关的信息

事件源对象、事件监听对象(事件处理对象)和事件对象构成了Java事件处理模型的三要素

在Java事件处理中,通常使用的是一对一的观察者模式,而不是一对多的观察者模式

###MVC模式

它包含三个角色:模型(Model),视图(View)和控制器(Controller)。观察者模式可以用来实现MVC模式,观察者模式中的观察目标就是MVC模式中的模型(Model),而观察者就是MVC中的视图(View),控制器(Controller)充当两者之间的中介者(Mediator)。当模型层的数据发生改变时,视图层将自动改变其显示内容