概念
观察者模式又称为 发布-订阅模式,定义了对象之间一对多依赖关系,当目标对象(被观察者)的状态发生改变时,它的所有依赖者(观察者)都会收到通知。一个观察目标可以对应多个观察者,而这些观察者之间没有相互联系,所以能够根据需要增加和删除观察者,使得系统更易于扩展,符合开闭原则;并且观察者模式让目标对象和观察者松耦合,虽然彼此不清楚对方的细节,但依然可以交互,目标对象只知道一个具体的观察者列表,但并不认识任何一个具体的观察者,它只知道他们都有一个共同的接口。
但观察者模式的缺点在于如果存在很多个被观察者的话,那么将需要花费一定时间通知所有的观察者,如果观察者与被观察者之间存在循环依赖的话,那么可能导致系统崩溃,并且观察者模式没有相应的机制让观察者知道被观察对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
简单理解
当对象间存在一对多关系时,比如,当一个对象(被观察者)被修改时,则会自动通知依赖它的对象(观察者)。则使用观察者模式(Observer Pattern),观察者模式属于行为型模式。
分析
UML结构图
角色分析
观察者模式角色:被观察者、观察者两种;
Subject:抽象类或接口(被观察者)
ConcreteSubject:具体主题(被观察者),状态改变时会调用所有观察者的update方法,每一个主题可以有多个观察者,并将所有观察者对象的引用保存在一个集合里,被观察者提供一个接口,可以增加和删除观察者角色
Observer:抽象类或接口(观察者)
ConcreteObserver:具体观察者,实现抽象观察者角色定义的update接口。如果需要,具体观察者角色可以保存一个指向具体主题角色的引用。
代码实现
java的jdk里提供的有对观察者模式的支持,可以直接进行使用
比如说,我们现在有一个红绿灯,有两个人正在等,一个人要左转,一个人要直行,每次红绿灯的颜色改变时,等红灯的人都会做出相应的动作(心里动作也算动作),现在用观察者模式来实现。
被观察者:java.util.Observable
观察者:java.util.Observa
/**
* 被观察者 - 红绿灯
* @date 2023-05-22
*/
@Data
public class TrafficLights extends Observable {
/**
* 红绿灯颜色 0:左转绿灯 1:黄色 2:直行绿灯
*/
private Integer colour;
/**
* 修改红绿灯颜色
* @param colour
*/
public void setColour(int colour){
this.colour = colour;
super.setChanged();
super.notifyObservers(this.colour);
}
}
/**
* 观察者1 - 左转
* @date 2023-05-22
*/
public class TurnLeftPeople implements Observer {
public TurnLeftPeople(Observable o) {
o.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
if ((Integer)arg == 1) {
System.out.println("PopleOne开始左转啦");
} else {
System.out.println("PopleOne继续等待");
}
}
}
/**
* 观察者2 - 直行
* @date 2023-05-22
*/
public class StraightTravelPeople implements Observer {
public StraightTravelPeople(Observable o) {
o.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
if ((Integer)arg == 2) {
System.out.println("PopleTwo开始直行啦");
} else {
System.out.println("PopleTwo继续等待");
}
}
}
public class ObserverTest {
public static void main(String[] args) {
//创建被观察者
TrafficLights trafficLights = new TrafficLights();
//创建观察者并添加到trafficLights的容器里
new StraightTravelPeople(trafficLights);
new TurnLeftPeople(trafficLights);
//修改红绿灯颜色
trafficLights.setColour(1);
trafficLights.setColour(2);
}
}
结果
TurnLeftPeople开始左转啦
StraightTravelPeople继续等待
TurnLeftPeople继续等待
StraightTravelPeople开始直行啦
总结+思考
搞懂了观察者模式的角色定义、调用逻辑,我们在使用的时候就很容易扩展了。
关于使用什么样的方式来把观察者添加到被观察者的容器里面去,应该有很多方式,我个人觉得应该在被观察者类里面去主动收集所有属于它的观察者对象,这样才算是解耦吧。
观察者模式与发布/订阅模式区别
发布/订阅模式:订阅者把自己想订阅的事件注册到调度中心,当该事件触发时候,发布者发布该事件到调度中心(顺带上下文),由调度中心统一调度订阅者注册到调度中心的处理代码
看图
区别
虽然两种模式都存在订阅者和发布者(具体观察者可认为是订阅者、具体目标可认为是发布者),但是观察者模式是由具体目标调度的,而发布/订阅模式是统一由调度中心调的,所以观察者模式的订阅者与发布者之间是存在依赖的,而发布/订阅模式则不会。两种模式都可以用于松散耦合,改进代码管理和潜在的复用。