一.模式定义
- 观察者模式定义了对象之间一对多依赖关系,当目标对象(被观察者)的状态发生改变时,它的所有依赖者(观察者)都会收到通知。一个观察目标可以对应多个观察者,而这些观察者之间没有相互联系,所以能够根据需要增加和删除观察者,使得系统更易于扩展,符合开闭原则;
- 并且观察者模式让目标对象和观察者松耦合,虽然彼此不清楚对方的细节,但依然可以交互,目标对象只知道一个具体的观察者列表,但并不认识任何一个具体的观察者,它只知道他们都有一个共同的接口。
- 模式理解:被观察者(目标对象)通过关联(集合方式)方式储存观察者,即一个观察者可以被多个观察者观察,观察者可以了解到目标对象的状态变化,但是这种变化不会影响目标自身的架构。
模式动机
建立一种对象与对象之间的关联关系,一个对象发生改变时将自动通知其他对象,其他对象做出相应反应。
发生改变的对象称为观察目标,而被通知的对象成为观察者,一个观察目标可以对应多个观察者。
二.模式结构
Subject:抽象主题(被观察者/目标对象),每一个主题可以有多个观察者,并将所有观察者对象的引用保存在一个集合里,被观察者提供一个接口,可以增加和删除观察者角色
ConcreteSubject:具体主题(具体目标),将有关状态存入具体观察者对象,在主题发生改变时,给所有的观察者发出通知
Observer:抽象观察者,为所有的具体观察者定义一个接口,该接口的作用是在收到主题的通知时能够及时的更新自己
ConcreteObserver:具体观察者,实现抽象观察者角色定义的接口,以便使本身的状态与主题状态相协调。如果需要,具体观察者角色可以保存一个指向具体主题角色的引用。
模式实例
- 假设猫是老鼠和狗的观察目标,老鼠和狗是观察者,猫叫老鼠跑,狗也跟着叫,使用观察者模式描述该过程。
抽象目标类 MySubject
MySubject是抽象目标类,在其中定义了一个ArrayList类型的集合observers,用于存储观察者对象,并定义了注册方法attach()和注销方法detach() ,同时声明了抽象的通知方法 cry()。需要注意的是attach()方法和detach()方法都必须针对抽象观察者进行编程,任何抽象观察者的子类对象都可以注册或注销。
package observer.test1;
import java.util.ArrayList;
public abstract class MySubject {
protected ArrayList observers =new ArrayList();
//注册方法
public void attach(MyObserver observer){
observers.add(observer);
}
//注销方法
public void detach(MyObserver observer){
observers.remove(observer);
}
public abstract void cry(); //抽象通知方法
}
抽象观察者类 MyObserver
抽象观察者MyObserver定义为一个接口,在其中声明了抽象响应方法response()
。
package observer.test1;
public interface MyObserver {
void response(); //抽象响应方法
}
具体目标类 Cat(猫类)
Cat是目标类MySubject 的子类,它实现了抽象方法 cry() ,在cry()中遍历了观察者集合,调用每一个观察者对象的response()响应方法。
package observer.test1;
public class Cat extends MySubject {
@Override
public void cry() {
System.out.println("猫叫!");
System.out.println("-----------------------------------------");
//通过for遍历集合并且调用方法
for (Object obs:observers){
((MyObserver)obs).response();
}
}
}
具体观察者类 Mouse(老鼠类)
Mouse是具体观察者类,它实现了在抽象观察者中定义的响应方法response()
。
package observer.test1;
public class Mouse implements MyObserver {
@Override
public void response() {
System.out.println("老鼠努力逃跑!");
}
}
具体观察者类 Dog(狗类)
Dog也是具体观察者类,它实现了在抽象观察者中定义的响应方法response()
。
package observer.test1;
public class Dog implements MyObserver {
@Override
public void response() {
System.out.println("狗跟着叫!");
}
}
三.应用场景
- 当有一个目标对象改变的同时需要其他对象也要一起改变。
- 类似一条触发链
- 关联行为场景
- 事件多级触发场景
- 跨系统的消息变换场景,如消息队列的处理机制。
注意事项
- 避免循环引用
- 如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
- 当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象有待改变的时候,应该考虑使用观察者模式。
- 将一个系统分割成一系列相互协作的类有一个很不好的副作用,就是需要维护相关对象间的一致性。为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便,而观察者模式所做的工作就是在解除耦合。