观察者模式
观察者模式是一种行为设计模式, 允许你定义一种订阅机制, 可在对象事件发生时通知多个 “观察” 该对象的其他对象。
多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
概括:多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。
模式结构
- 抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
- 具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
- 抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
- 具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
类图
优缺点
优点 :
- 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。
- 目标与观察者之间建立了一套触发机制。
- 开闭原则。 你无需修改发布者代码就能引入新的订阅者类 (如果是发布者接口则可轻松引入发布者类)。
缺点 :
- 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
- 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
- 订阅者的通知顺序是随机的。
应用场景
- 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象
- 多层级嵌套使用,形成一种链式触发机制,使得事件具备跨域(跨越两种观察者类型)通知
- 当应用中的一些对象必须观察其他对象时, 可使用该模式。 但仅能在有限时间内或特定情况下使用。
- 实现类似广播机制的功能,不需要知道具体收听者,只需分发广播,系统中感兴趣的对象会自动接收该广播
与其他模式的关系
责任链模式、 命令模式、 中介者模式和观察者模式用于处理请求发送者和接收者之间的不同连接方式:
责任链按照顺序将请求动态传递给一系列的潜在接收者, 直至其中一名接收者对请求进行处理。
命令在发送者和请求者之间建立单向连接。
中介者清除了发送者和请求者之间的直接连接, 强制它们通过一个中介对象进行间接沟通。
观察者允许接收者动态地订阅或取消接收请求。
中介者和观察者之间的区别往往很难记住。 在大部分情况下, 你可以使用其中一种模式, 而有时可以同时使用。 让我们来看看如何做到这一点。
中介者的主要目标是消除一系列系统组件之间的相互依赖。 这些组件将依赖于同一个中介者对象。 观察者的目标是在对象之间建立动态的单向连接, 使得部分对象可作为其他对象的附属发挥作用。
示例代码
public class ConcreteObserver implements Observer{
private final String name;
public ConcreteObserver(String name){
this.name = name;
}
@Override
public void update(Observer o, Object data) {
System.out.println("具体的观察者"+name);
System.out.println("更新数据"+data);
}
}
public class ConcreteSubject implements Subject{
private final ArrayList<Observer> observers = new ArrayList<>();
private Object data;
public ConcreteSubject(Object data) {
this.data = data;
}
public void setData(Object data) {
this.data = data;
}
@Override
public void register(Observer observer) {
observers.add(observer);
}
@Override
public void remove(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(observer,data);
}
}
}
public interface Observer {
void update(Observer o, Object data);
}
public class ObserverPattern {
public static void main(String[] args) {
Observer concreteObserver = new ConcreteObserver("新浪");
Observer concreteObserver1 = new ConcreteObserver("百度");
ConcreteSubject subject = new ConcreteSubject("温度:38");
subject.register(concreteObserver);
subject.register(concreteObserver1);
subject.setData("温度变成了:20");
subject.notifyObservers();
}
}