观察者模式
百度百科上的定义:
观察者模式(有时又被称为模型(Model)-视图(View)模式、源-收听者(Listener)模式或从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。
应用场景
观察者模式多用于实现订阅功能的场景,例如消息事件的订阅,当我们订阅了某个消息事件时,当这个事件发布了新的消息,就会通知我们。
- 当一个抽象模型有两个方面,其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
- 当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象需要被改变。
- 当一个对象必须通知其他对象,而它又不能假定其他对象是谁。换言之,不希望这些对象是紧密耦合的。
实现逻辑
Java已经支持观察者模式
- java.util.Observable :抽象主题
- java.util.Observer :抽象观察者
具体代码
- 创建一个Java Entity,模拟需要发布/订阅的消息OfficeInfo
import lombok.Data;
import java.util.Objects;
@Data
public class OfficeInfo {
private int length;
private int width;
private int hight;
public OfficeInfo(int length, int width, int hight) {
this.length = length;
this.width = width;
this.hight = hight;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
OfficeInfo that = (OfficeInfo) o;
return length == that.length &&
width == that.width &&
hight == that.hight;
}
@Override
public int hashCode() {
return Objects.hash(length, width, hight);
}
}
- 创建消息生产类,也就是被观察类
import java.util.Observable;
public class Watched extends Observable {
private OfficeInfo officeInfo;
public OfficeInfo getOfficeInfo() {
return officeInfo;
}
public void setOfficeInfo(OfficeInfo officeInfo) {
if (this.officeInfo == null || !this.officeInfo.equals(officeInfo)) {
this.officeInfo = officeInfo;
setChanged();
}
notifyObservers();
}
}
Notice:
Watched继承Observable类,在其消息发生变化时,调用Observable的setChanged方法,记录消息发生了变化,并使用notifyObservers方法将该变化通知给所有订阅了该事件的观察者们。
- 创建两个消息消费类,也是观察者类
import lombok.extern.slf4j.Slf4j;
import java.util.Observable;
import java.util.Observer;
@Slf4j
public class Watcher1 implements Observer {
public Watcher1(Observable o) {
o.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
OfficeInfo info = ((Watched) o).getOfficeInfo();
log.info("Watcher1:状态发生改变:length:{},width:{},hight:{}", info.getLength(), info.getWidth(), info.getHight());
}
}
Notice:
Watcher1 在构造方法中,将自身this注册进被观察者的订阅名单中,并实现了Observer 的update接口,用于接收被观察者发布的消息。
import lombok.extern.slf4j.Slf4j;
import java.util.Observable;
import java.util.Observer;
@Slf4j
public class Watcher2 implements Observer {
public Watcher2(Observable o) {
o.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
OfficeInfo info = ((Watched) o).getOfficeInfo();
log.info("Watcher2:状态发生改变:length:{},width:{},hight:{}", info.getLength(), info.getWidth(), info.getHight());
}
}
- 测试
public class ObserverDemo {
public static void main(String[] args) {
//创建被观察者对象
Watched watched = new Watched();
//创建观察者对象,并将被观察者登记注册
new Watcher1(watched);
new Watcher2(watched);
//给观察者赋值
watched.setOfficeInfo(new OfficeInfo(1,2,3));
watched.setOfficeInfo(new OfficeInfo(4,5,6));
watched.setOfficeInfo(new OfficeInfo(7,8,9));
}
}
Notice:
初始化创建被观察类Watched
并新建两个观察者类Watcher1和Watcher2,将自己注册进Watched的订阅列表中;
在验证的时候,只需要将OfficeInfo初始化赋值后,即可验证两个观察者是否能收到消息
- 测试结果
14:46:17.709 [main] INFO observer.Watcher2 - Watcher2:状态发生改变:length:1,width:2,hight:3
14:46:17.722 [main] INFO observer.Watcher1 - Watcher1:状态发生改变:length:1,width:2,hight:3
14:46:17.723 [main] INFO observer.Watcher2 - Watcher2:状态发生改变:length:4,width:5,hight:6
14:46:17.723 [main] INFO observer.Watcher1 - Watcher1:状态发生改变:length:4,width:5,hight:6
14:46:17.723 [main] INFO observer.Watcher2 - Watcher2:状态发生改变:length:7,width:8,hight:9
14:46:17.723 [main] INFO observer.Watcher1 - Watcher1:状态发生改变:length:7,width:8,hight:9
通过测试验证得到的结果,是Watcher1和Watcher2都能同时发生状态变化,都能接收到主题类的消息。