定义了一种一对多的依赖关系,一个主题对象可被多个观察者对象同时监听。当这个主题对象状态变化时,会通知所有观察者对象并作出相应处理逻辑。又被称为发布订阅模式。
何时使用
一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
使用场景:
- 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
- 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
- 一个对象必须通知其他对象,而并不知道这些对象是谁。
- 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制
uml
角色
1. 抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
2. 具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
3. 抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
4. 具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
案例
手写相关实现
- observer抽象观察者,提供主题更新使的通知方法
public interface Observer {
/** 通知接口,主题改变时,通过此接口通知相关观察者*/
void update(String msg);
}
- subject主题,也就是被观察者,提供注册、移除观察者的方法
public interface Subject {
/** 为该主题注册观察者*/
void register(Observer o);
/** 移除关注该主题的观察者*/
void remove(Observer o);
/** 通知所有观察者 */
void notifyObserver();
}
- 具体主题类,持有观察者列表,提供观察者注册,移除的方法。一般还存在一个私有属性,当该属性变化时,触发通知消息
public class RealObject implements Subject {
private int state;
private List<Observer> observers = new ArrayList<>();
@Override
public void register(Observer o) {
observers.add(o);
}
@Override
public void remove(Observer o) {
observers.remove(o);
}
public void setState(int state) {
if (this.state != state) {
this.state = state;
notifyObserver();
}
}
@Override
public void notifyObserver() {
observers.stream().forEach(o -> o.update("state change : " + state));
}
}
- 实际的观察者,被观察对象发生变化后,通知该观察者进行相关处理,需要提供通知方法
public class ObserverOne implements Observer {
@Override
public void update(String msg) {
System.out.println(ObserverOne.class.getName() + " : " + msg);
}
}
public class ObserverTwo implements Observer {
@Override
public void update(String msg) {
System.out.println(ObserverTwo.class.getName() + " : " + msg);
}
}
- 测试类
public class TestObserver {
public static void main(String[] args) {
Subject subject = new RealObject();
ObserverOne one = new ObserverOne();
ObserverTwo two = new ObserverTwo();
subject.register(one);
subject.register(two);
// 此时两个观察者均能收到更新消息
((RealObject) subject).setState(5);
//将二号观察者去掉
subject.remove(two);
((RealObject) subject).setState(6);
//更新为相同的值,此时数据没变化,不再通知
((RealObject) subject).setState(6);
}
}
使用jdk提供的类
java.util包中已经为我们提供了实现观察者模式的类,Observer类和Observable(Subject)类,我们只需要基于此开发相应的具体实现即可.
- Observable类也就是被观察者类,维护了观察者的容器Vector,提供了如下若干方法供我们使用,
方法 | 含义 |
---|---|
addObserver(Observer o) | 注册观察者 |
clearChanged() | 修改变化标志位false |
countObservers() | 观察者个数 |
deleteObserver(Observer o) | 删除某个观察者 |
deleteObservers() | 清除所有观察者对象 |
notifyObservers() | 通知观察者 |
notifyObservers(Object arg) | 通知观察者 |
setChanged() | 设置变化标志位true |
- Observer接口,观察者类,仅提供了如下一个方法:
方法 | 含义 |
---|---|
void update(Observable o, Object arg) | 获取被观察者的状态变更消息 |
实现
- 具体的被观察者类
public class RealSubject extends Observable {
private int state;
public int getState() {
return state;
}
public void setState(int state) {
if (this.state != state){
this.state = state;
this.setChanged();
this.notifyObservers();
}
}
}
- 具体的观察者类
public class OneObserver implements Observer {
@Override
public void update(Observable o, Object arg) {
RealSubject realSubject = (RealSubject) o;
System.out.println(OneObserver.class.getName() + " : " + arg + ",state = " + realSubject.getState());
}
}
public class TwoObserver implements Observer {
@Override
public void update(Observable o, Object arg) {
RealSubject realSubject = (RealSubject) o;
System.out.println(TwoObserver.class.getName() + " : " + arg + ",state = " + realSubject.getState());
}
}
- 测试类
public class Client {
public static void main(String[] args) {
Observable subject = new RealSubject();
OneObserver one = new OneObserver();
TwoObserver two = new TwoObserver();
subject.addObserver(one);
subject.addObserver(two);
// 此时两个观察者均能收到更新消息
((RealSubject) subject).setState(5);
//将二号观察者去掉
subject.deleteObserver(two);
((RealSubject) subject).setState(6);
//更新为相同的值,此时数据没变化,不再通知
((RealSubject) subject).setState(6);
}
}
优点
1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
缺点
1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。