1、定义
观察者模式(Observer Pattern):观察者模式又被称为发布订阅模式,它定义了对象间一种一对多的依赖关系,使得每当一个对象改变状态,所有依赖于它的对象都会得到通知并被自动更新。
2、形式
观察者模式通用类图如下所示:
其中,
Subject
为被观察者,一般是抽象类或实现类,其定义了观察者必须实现的职责,它必须能够动态的增加、取消观察者(管理观察者并通知观察者)。Observer
为观察者,观察者接收到消息后,会进行update操作,对消息进行处理。ConcreteSubject
为具体的被观察者,定义被观察者的业务逻辑,同时定义对哪些事件进行通知。ConcreteObserver
为具体的观察者,定义消息处理逻辑。
对应代码如下:
Subject
public abstract class Subject {
private Vector<Observer> obsVector = new Vector<Observer>();
public void add(Observer observer){
this.obsVector.add(observer);
}
public void delete(Observer observer){
this.obsVector.remove(observer);
}
public void notifyObservers(){
for(Observer o: this.obsVector){
o.update();
}
}
}
Observer
public interface Observer {
public void update();
}
ConcreteSubject
public class ConcreteSubject extends Subject{
public void doSomething(){
/*
* 业务处理逻辑
* */
super.notifyObservers();
}
}
ConcreteObserver
public class ConcreteObserver implements Observer{
public void update() {
System.out.println("接收到消息,进行处理...");
}
}
Client
public class Client {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
Observer obs = new ConcreteObserver();
subject.add(obs);
/*消息被obs观察者接受到*/
subject.doSomething();
}
}
3、优缺点
优点
- 观察者和被观察者之间是抽象耦合,提高了可扩展性
- 建立了一套触发机制,可以将不同的职责串联形成复杂的逻辑关系
缺点:
- 可能会影响开发效率和运行效率,在存在多个观察者的情况下,一旦某个观察者卡壳可能会影响整体的执行效率(java消息通知默认是顺序执行),在这种情况下,会考虑采用异步的方式。
4、使用场景
- 关联行为场景,关联行为需要是可拆分的,要与组合关系区别开
- 事件多级触发场景
- 跨系统的消息交换场景,例如消息队列的处理机制
使用观察者模式还需要注意下面两个可能存在问题:
广播链的问题
如果一个对象即是观察者又是被观察者,那么就可能会形成一条观察链,但这样会增加代码的复杂性,导致系统的可维护性变差。因此一般控制系统中最多出现一个既是观察者又是被观察者的对象。
异步处理的问题
被观察者发生动作之后观察者要做出回应,如果观察者比较对导致处理时间比较长就可以采用异步处理,但需要考虑线程安全和队列的问题。
5、扩展
1)java中的观察者模式
JDK中提供了一个java.util.Observer
接口和Observable
实现类。可以通过实现Observer
接口来定义一个观察者,该接口中定义了一个update
方法,该方法中包含了两个参数供我们处理,如下所示:
public interface Observer {
/**
* This method is called whenever the observed object is changed. An
* application calls an <tt>Observable</tt> object's
* <code>notifyObservers</code> method to have all the object's
* observers notified of the change.
*
* @param o the observable object.
* @param arg an argument passed to the <code>notifyObservers</code>
* method.
*/
void update(Observable o, Object arg);
}
类似的,被观察者可以通过继承Observable
类来实现,该类提供了众多方法供我们调用,这些方法包含增加观察者、删除观察者、通知观察者、获取观察者总数以及关于状态变更的相应方法,具体如下:
将之前的模板代码修改如下:
ConcreteSubject
/*被观察者*/
public class ConcreteSubject extends Observable {
public void haveAction(){
System.out.println("吃饭了...");
System.out.println("观察者数量为:" + this.countObservers());
/*状态发生变更,通知观察者(如果不显式表明状态发生变更,该行为将不会通知给观察者)*/
super.setChanged();
super.notifyObservers();
}
}
ConcreteObserver
/*观察者*/
public class ConcreteObserver implements Observer {
public void update(Observable o, Object arg) {
System.out.println("观察到目标活动");
}
}
Client
public class Client {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
ConcreteObserver obs = new ConcreteObserver();
subject.addObserver(obs);
/*消息被obs观察者接受到*/
subject.haveAction();
}
}
执行client类后,从结果可以看出被观察者的活动可以被观察者所知晓。
2)应用时所要考虑的问题
在实际应用中,该模式往往会进行一下几方面的扩展。
- 观察者和被观察者之间的消息沟通
update的两个参数在实际中一般一个为被观察者,一个是数据传输对象,数据由被观察者产生,由观察者消费。
- 观察者的响应方式
在多个观察者但只有少量被观察者的条件下,为了保证观察者能够快速响应,往往会采用多线程技术和缓存技术。
- 被观察者尽量自己做主
为了防止加重观察者的处理逻辑,在设计时可以灵活考虑被观察者的自主性,让被观察者决定是否要将消息传输给观察者。
6、小结
观察者模式是一个被广泛使用的设计模式,主要应用于事物之间的通知关联,应当熟练掌握。