观察者模式介绍
观察者模式:定义对象间的一对多依赖关系,当一个对象的状态发生改变是,所有依赖于他的对象都得到通知并被自动更新。
观察者模式的适用场景:
- 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这二者封装在独立的对象中,使他们可以相互独立的改变和和复用。
- 对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变。
- 一个对象必须通知其他对象,而它又不能假定其他对象是谁,换言之,你不希望这些对象是紧耦合的。
观察者模式的结构:
观察者模式角色说明:
- Subject(目标):目标知道它的观察者。可以有任意多个观察者观察同一个目标。同时提供观察者添加和删除的接口。
- Observer(观察者):为那些在目标发生变化时需要获取通知对象定义的一个接口。
- ConcreteObserver(具体观察者):实现Observer接口,以使自身状态与目标的状态保持一致。
观察者模式实例
首先来试下观察者接口及实现:
interface Observer {
void update(Object state);
}
class ObserverImpl1 implements Observer {
@Override
public void update(Object state) {
System.out.println("ObserverImpl1 get event" + state);
}
}
class ObserverImpl2 implements Observer {
@Override
public void update(Object state) {
System.out.println("ObserverImpl2 get event" + state);
}
}
subject类实现如下:
class Subject {
private List<Observer> observers = new ArrayList<>();
public void attach(Observer observer) {
observers.add(observer);
}
public void detach(Observer observer) {
observers.remove(observer);
}
public void notify(Object state) {
for (Observer observer : observers) {
observer.update(state);
}
}
}
测试类:
public static void main(String[] args) {
Observer observer = new ObserverImpl1();
Observer observer1 = new ObserverImpl2();
Subject subject = new Subject();
subject.attach(observer);
subject.attach(observer1);
//发布状态变化
subject.notify("state change xxx");
System.out.println("----------------------");
// 剔除一个观察者
subject.detach(observer1);
// 发布状态变化
subject.notify("state change yyy");
}
观察者模式的优缺点
优点:
- Subject和Observer之间是松耦合的,分别可以各自独立改变。
- Subject在发送广播通知的时候,无需指定具体的Observer,Observer可以自己决定是否要订阅Subject的通知。
缺点:
- 松耦合导致代码关系不明显,有时可能难以理解。
- 如果一个Subject被大量Observer订阅的话,在广播通知的时候可能会有效率问题。
观察者模式在源码中的应用
- JDK为我们提供了观察者模式,即Observer和Observable,如下:
public interface Observer {
void update(Observable o, Object arg);
}
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();
}
}
- spring中观察者模式的使用
熟悉spring的都应该了解,spring有一个多播器,我们在编码时继承一个ApplicationListener接口并注入spring容器,在spring发布事件时,会回调器onApplicationEvent。
class MyApplicationListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
System.out.println("get application event" + applicationEvent);
}
}
在spring容器初始时,会将扫描的所有ApplicationListener组件注册到多播器中,而当事件发布时则多播器会将时间发布给没ApplicationListener组件。spring默认多播器为SimpleApplicationEventMuitlcaster,来看一下其multicastEvent方法:
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
Executor executor = this.getTaskExecutor();
Iterator var5 = this.getApplicationListeners(event, type).iterator();
// 获取所有ApplicationListerner并通知
while(var5.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var5.next();
if (executor != null) {
executor.execute(() -> {
this.invokeListener(listener, event);
});
} else {
this.invokeListener(listener, event);
}
}
}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = this.getErrorHandler();
if (errorHandler != null) {
try {
this.doInvokeListener(listener, event);
} catch (Throwable var5) {
errorHandler.handleError(var5);
}
} else {
this.doInvokeListener(listener, event);
}
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
} catch (ClassCastException var6) {
String msg = var6.getMessage();
if (msg != null && !this.matchesClassCastMessage(msg, event.getClass())) {
throw var6;
}
Log logger = LogFactory.getLog(this.getClass());
if (logger.isTraceEnabled()) {
logger.trace("Non-matching event type for listener: " + listener, var6);
}
}
}