一、概述
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。
定义了对象之间的一对多依赖,让多个观察者对象同时监听某一个主题对象,当主题对象发生变化时,它的所有依赖者(观察者)都会收到通知并更新。
Observer模式提供给关联对象一种同步通信的手段,使某个对象与依赖它的其他对象之间保持状态同步。
1.1、适用场景
一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
关联行为场景,建立一套触发机制
典型应用
1、侦听事件驱动程序设计中的外部事件
2、侦听/监视某个对象的状态变化
3、发布者/订阅者(publisher/subscriber)模型中,当一个外部事件(新的产品,消息的出现等等)被触发时,通知邮件列表中的订阅者
1.2、优缺点
优点:
- 观察者和被观察者之间建立一个抽象的耦合
- 观察者模式支持广播通信,建立一套触发机制。
缺点:
- 观察者之间有过多的细节依赖、提高时间消耗及程序复杂度
- 使用要得当,要避免循环调用
1.3、类图角色及其职责
1、Subject(被观察者)
被观察的对象。当需要被观察的状态发生变化时,需要通知队列中所有观察者对象。Subject需要维持(添加,删除,通知)一个观察者对象的队列列表。
2、ConcreteSubject
被观察者的具体实现。包含一些基本的属性状态及其他操作。
3、Observer(观察者)
接口或抽象类。当Subject的状态发生变化时,Observer对象将通过一个callback函数得到通知。
4、ConcreteObserver
观察者的具体实现。得到通知后将完成一些具体的业务逻辑处理。
1.4、演进过程
使用:被观察者想要起作用,就必须继承java.util包下的Observable类
构造方法摘要 | |
---|---|
Observable() 构造一个带有零个观察者的 Observable。 |
方法摘要 | |
---|---|
void |
addObserver(Observer o) 如果观察者与集合中已有的观察者不同,则向对象的观察者集中添加此观察者。 |
protected void |
clearChanged() 指示对象不再改变,或者它已对其所有的观察者通知了最近的改变,所以 hasChanged 方法将返回 false。 |
int |
countObservers() 返回 Observable 对象的观察者数目。 |
void |
deleteObserver(Observer o) 从对象的观察者集合中删除某个观察者。 |
void |
deleteObservers() 清除观察者列表,使此对象不再有任何观察者。 |
boolean |
hasChanged() 测试对象是否改变。 |
void |
notifyObservers() 如果 hasChanged 方法指示对象已改变,则通知其所有观察者,并调用 clearChanged 方法来指示此对象不再改变。 |
void |
notifyObservers(Object arg) 如果 hasChanged 方法指示对象已改变,则通知其所有观察者,并调用 clearChanged 方法来指示此对象不再改变。 |
protected void |
setChanged() 标记此 Observable 对象为已改变的对象;现在 hasChanged 方法将返回 true。 |
示例: 监听成员变量变化
public class PersonOrg { private String name; private String sex; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
步骤一、监听成员变量name,sex,age的变化,在数值变化是,执行我们的操作,所以Person就是被观察者,所以被观察者必须继承Observable,而Observable中有这三个方法:
1、notifyObservers()
: 如果 hasChanged
方法指示对象已改变,则通知其所有观察者,并调用 clearChanged
方法来指示此对象不再改变。
这个方法是通知观察者被观察者是否改变的,只要hasChanged()方法指示的对象改变,就会调用观察者中的方法。
2、hasChanged()
: 测试对象是否改变。
3、setChanged()
:标记此 Observable 对象为已改变的对象;现在 hasChanged 方法将返回 true。
所以,如果想观察成员变量是否改变,就要在set方法中,执行setChanged()与notifyObservers()
所以,被观察者应该改为:
public class Person extends Observable { private String name; private String sex; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; this.setChanged(); this.notifyObservers(); } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; this.setChanged(); this.notifyObservers(); } public int getAge() { return age; } public void setAge(int age) { this.age = age; this.setChanged(); this.notifyObservers(); } }
步骤二、有了被观察者,就要有观察者,观察者必须实现java.util包下的Observer接口,并重写update(Observable o, Object arg)方法,当被观察者改变时,就会执行update()方法
public class MyObserver implements Observer { @Override public void update(Observable o, Object arg) { System.out.println("对象已改变"); } }
现在,就可以执行看一看了。不过在执行set()方法之前一定要使用addObserver(Observer o)
这个方法注册观察者,不然不会生效。
使用 测试
@Test public void update() { Person person = new Person(); //注册观察者 person.addObserver(new MyObserver()); person.setName("小明"); person.setSex("男"); person.setAge(18); }
输出:
对象已改变 对象已改变 对象已改变
当然可以注册多个观察者
//注册观察者 person.addObserver(new MyObserver()); person.addObserver(new MyObserver());
更多使用:三个方法deleteObserver(Observer o)
,deleteObservers()
,countObservers()
@Test public void update2() { Person person = new Person(); //注册观察者 MyObserver myObserver = new MyObserver(); person.addObserver(myObserver); person.addObserver(new MyObserver()); //获得当前对象已注册的观察者数目 person.countObservers(); //删除指定的一个观察者 person.deleteObserver(myObserver); //删除该对象全部观察者 person.deleteObservers(); person.setName("小明"); person.setSex("男"); person.setAge(18); }
二、扩展
2.1、 java.awt.Event
2.2、org.springframework.web.context.request.RequestContextListener、ServletRequestListener、EventListener
监听器是观察者模式的实现一种
2.3、org.springframework.beans.factory.parsing.ReaderEventListener
2.4、google Guava之EventBus
增加pom引入
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>26.0-jre</version> </dependency>
EventBus是Guava的事件处理机制,是设计模式中的观察者模式(生产/消费者编程模型)的优雅实现,在应用中可以处理一些异步任务。对于事件监听和发布订阅模式,EventBus是一个非常优雅和简单解决方案,我们不用创建复杂的类和接口层次结构。
EventBus实际上是一个消息队列,Event Source发送一个消息到EventBus,然后再由EventBus将消息推送到所监听的Listener。
如上述示例使用guava改写
1、创建Listener
可以通过@Subscribe
注解将任意的类的方法变为一个Listener。
public class PersonGuavaListener { @Subscribe public void doAction(final String event) { System.out.println("对象发生变化:" + event); } }
2、创建EventBus并发送消息
public class PersonGuava { EventBus eventBus = new EventBus(); private String name; private String sex; private int age; public PersonGuava() { //注册Listener eventBus.register(new PersonGuavaListener()); } public String getName() { return name; } public void setName(String name) { this.name = name; //向订阅者发送消息 eventBus.post("Simple Event:name:"+name); } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; //向订阅者发送消息 eventBus.post("Simple Event:sex:"+sex); } public int getAge() { return age; } public void setAge(int age) { this.age = age; //向订阅者发送消息 eventBus.post("Simple Event:age:"+age); } }
测试
@Test public void test() { PersonGuava person = new PersonGuava(); person.setName("李宏旭"); person.setAge(30); person.setSex("男"); }
输出
对象发生变化:Simple Event:name:李宏旭 对象发生变化:Simple Event:age:30 对象发生变化:Simple Event:sex:男
由于EventBus是将消息队列放入到内存中的,listener消费这个消息队列,故系统重启之后,保存或者堆积在队列中的消息丢失。
fd
来源:https://www.cnblogs.com/bjlhx/p/11545163.html