观察者模式
定义
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会受到通知并自动更新。
类图
例子
假设由这样一个例子,大家都比较关注房价,所以关注了一个网站,利用rss订阅。当有新的动态更新时,所有订阅的用户就会收到最新的消息。
定义主题及其实现House Site
package com.gitlearning.hanldegit.patterns.observe.first;
import java.util.ArrayList;
import java.util.List;
/**
* 房子站点
*/
public class HouseSite implements Subject{
private List<Subscriber> observers;
public HouseSite() {
observers = new ArrayList<>();
}
@Override
public void registerObserver(Subscriber subscriber) {
observers.add(subscriber);
}
@Override
public void removeObserver(Subscriber subscriber) {
observers.remove(subscriber);
}
@Override
public void publishMessage(String message) {
observers.forEach(observer -> observer.updateMessage(message));
}
}
interface Subject {
void registerObserver(Subscriber subscriber);
void removeObserver(Subscriber subscriber);
void publishMessage(String message);
}
定义订阅者及其实现:
package com.gitlearning.hanldegit.patterns.observe.first;
public interface Subscriber {
void updateMessage(String message);
}
class Person implements Subscriber{
String name;
public Person(String name) {
this.name = name;
}
@Override
public void updateMessage(String message) {
System.err.println(name + "收到了最新消息: " + message);
}
}
测试程序如下:
package com.gitlearning.hanldegit.patterns.observe.first;
import org.junit.jupiter.api.Test;
public class TestObserver {
@Test
void test() {
Subject houseSite = new HouseSite();
Person zhangsan = new Person("张三");
Person lisi = new Person("李四");
houseSite.registerObserver(zhangsan);
houseSite.registerObserver(lisi);
houseSite.publishMessage("今日涨价房源100套,降价房源888套。");
houseSite.removeObserver(lisi);
houseSite.publishMessage("今日有事,网站暂停更新");
}
}
输出消息为:
张三收到了最新消息: 今日涨价房源100套,降价房源888套。
李四收到了最新消息: 今日涨价房源100套,降价房源888套。
张三收到了最新消息: 今日有事,网站暂停更新
其实JAVA本身就提供了对观察者模式的支持,主题类Observable
package com.gitlearning.hanldegit.patterns.observe.userInternal;
import java.util.Observable;
public class HouseSite extends Observable {
void publishEvent(String message) {
setChanged();
notifyObservers(message);
}
}
观察者Observer:
package com.gitlearning.hanldegit.patterns.observe.userInternal;
import java.util.Observable;
import java.util.Observer;
public class Person implements Observer {
String name;
public Person(String name) {
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
System.err.println(name + "收到了最新消息: " + arg);
}
}
测试代码:
package com.gitlearning.hanldegit.patterns.observe.userInternal;
import org.junit.jupiter.api.Test;
import java.util.Observer;
public class TestObserverWithInternal {
@Test
void test() {
HouseSite site = new HouseSite();
Observer zhangsan = new Person("张三");
Observer lisi = new Person("李四");
site.addObserver(zhangsan);
site.addObserver(lisi);
site.publishEvent("今日最新消息, 首套房利率上浮25%");
site.deleteObserver(zhangsan);
site.publishEvent("离岸人民币汇率破7!");
}
}
结果:
李四收到了最新消息: 今日最新消息, 首套房利率上浮25%
张三收到了最新消息: 今日最新消息, 首套房利率上浮25%
李四收到了最新消息: 离岸人民币汇率破7!
这里需要注意一点,需要在Observable里设置changed状态,要不然无法调用update方法。
使用
Spring的ApplicationEvent继承自jdk中的EventObject,ApplicationListener继承自EventListener。
发布事件的时候触发监听器的onApplicationEvent方法,那触发的方法就在于ApplicationContext中。例如AbstractApplication的refresh()方法里的registerListeners(),通过ApplicationEventMulticaster广播出去,触发监听器的onApplicationEvent方法。
顺便讲一下,ApplicationEventPublisher有publishEvent方法,其实也是通过调用MultiCaster来实现的。
其他
- 观察者模式定义了对象之间一对多的关系
- 主题(也就是可观察者)用一个共同的接口来更新观察者。
- 使用观察者模式,可以采用推或者拉的模式