《设计模式》观察者模式
定义:
- 观察者模式又称发布-订阅模式,它定义了一种一对多的依赖关系,一个主题对象可以被多个观察者对象同时观察(监听)。当主题对象的状态发生变化时,使得所有的观察者对象都能够收到通知并自动更新。
- 观察者模式的核心就是将观察者和被观察者进行解耦,类似发布-订阅的方式关联两者,使得被观察者的状态更新能够通知到对它感兴趣的观察者们。
观察者模式的组成角色:
- 主题(Subject):又称为抽象通知者,一般由一个接口或者抽象类实现。它拥有一个包含所有观察者对象引用的集合,定义了增加和删除任意观察者对象的方法以及通知所有观察者对象的方法。
- 具体主题(ConcreteSubject):又称为具体通知者,当状态(数据)发生改变时,给所有的观察者发送通知。
- 抽象观察者(Observer):一般由一个抽象类或者一个接口实现,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,通常包含
update()
方法。 - 具体观察者(ConcreteObserver):实现抽象观察者的更新接口,同时在具体观察者中维护一个指向具体主题对象的引用,存储具体观察者的有关状态,这些状态需要和具体主题对象的状态保持一致。
观察者模式的 UML 类图:
举一个很简单的应用场景案例:模拟一个新闻公众号,用户可以随意订阅和取消订阅公众号,当新闻公众号有新闻发布的时候就通知所有订阅了这个公众号的用户。任何时候,所有的订阅用户都是收到相同的新闻更新内容。
抽象主题 Subject:
public interface Subject {
/**
* 增加观察者对象
* @param observer
*/
void attach(Observer observer);
/**
* 删除观察者对象
* @param observer
*/
void detach(Observer observer);
/**
* 通知所有观察者对象
*/
void notifyObserver();
}
具体主题 ConcreteSubject:
public class ConcreteSubject implements Subject{
private List<Observer> observers = new ArrayList<>();
private String subjectState;
public String getSubjectState() {
return subjectState;
}
public void setSubjectState(String subjectState) {
this.subjectState = subjectState;
}
@Override
public void attach(Observer observer) {
observers.add(observer);
}
@Override
public void detach(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObserver() {
for (Observer observer : observers) {
observer.update();
}
}
}
抽象观察者 Observer:
public interface Observer {
/**
* 更新观察者的状态
*/
void update();
}
具体观察者 ConcreteObserver:
public class ConcreteObserver implements Observer{
private String name;
private String observerState;
private ConcreteSubject subject;
public ConcreteObserver(String name, ConcreteSubject subject) {
this.name = name;
this.subject = subject;
}
@Override
public void update() {
observerState = subject.getSubjectState();
System.out.println(String.format("观察者%s收到的消息是:%s", name, observerState));
}
}
客户端 Client:
public class Client {
public static void main(String[] args) {
ConcreteSubject concreteSubject = new ConcreteSubject();
concreteSubject.attach(new ConcreteObserver("node1", concreteSubject));
concreteSubject.attach(new ConcreteObserver("node2", concreteSubject));
concreteSubject.attach(new ConcreteObserver("node3", concreteSubject));
concreteSubject.setSubjectState("Hello, observers!");
concreteSubject.notifyObserver();
}
}
输出结果:
观察者node1收到的消息是:Hello, observers!
观察者node2收到的消息是:Hello, observers!
观察者node3收到的消息是:Hello, observers!
其实,在 JDK 源码中提供了快速使用观察者模式的骨架,在 java.util
包下的 Observer
接口(相当于抽象通知者)和 Observable
类(相当于观察者对象)。
Observer 接口
:
/**
* 抽象通知者
*/
public interface Observer {
void update(Observable o, Object arg);
}
Observer 类
:
public class Observable {
private boolean changed = false;
private Vector<Observer> obs;
public Observable() {
obs = new Vector<>();
}
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
}
public void notifyObservers() {
notifyObservers(null);
}
public void notifyObservers(Object arg) {
Object[] arrLocal;
synchronized (this) {
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
public synchronized void deleteObservers() {
obs.removeAllElements();
}
protected synchronized void setChanged() {
changed = true;
}
protected synchronized void clearChanged() {
changed = false;
}
public synchronized boolean hasChanged() {
return changed;
}
public synchronized int countObservers() {
return obs.size();
}
}
Observer 接口和 Observable 类 UML 类图如下所示:
观察者模式的优点:
- 在观察目标和观察者之间建立一个抽象的耦合,观察目标只需要维持一个抽象观察者的集合,无须了解其具体观察者,从而使得各自的变化都不会影响另一边的变化,符合依赖倒置原则。
- 支持广播通信。观察目标会向所有已注册的观察者对象发送通知,简化了一对多系统设计的难度。
- 满足开闭原则的要求,增加新的具体观察者无须修改原有系统代码。在具体观察者与观察目标之间不存在关联关系的情况下,增加新的观察目标也很方便。
观察者模式的缺点:
- 如果一个观察目标对象有很多直接和间接观察者,将所有的观察者都通知到会花费很多时间。
- 如果在观察者和观察目标之间存在循环依赖,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
- 没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
观察者模式的应用场景:
- 一个抽象模型有两个方面,其中一方面依赖于另一方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。
- 一个对象的改变将导致一个或多个其他对象也发生改变,而并不知道具体有多少对象将发生改变,也不知道这些对象是谁。
- 需要在系统中创建一个触发链,A 对象的行为将影响 B 对象,B 对象的行为将影响 C 对象……可以使用观察者模式创建一种链式触发机制。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/156997.html