观察者模式
定义
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会受到通知并自动更新。
类图
例子
假设由这样一个例子,大家都比较关注房价,所以关注了一个网站,利用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来实现的。
其他
- 观察者模式定义了对象之间一对多的关系
- 主题(也就是可观察者)用一个共同的接口来更新观察者。
- 使用观察者模式,可以采用推或者拉的模式