在软件开发中,我们通常有实时监听一个对象的需求,当一个对象的状态改变时,我们希望软件能够立刻接受到通知,并作出相应的处理。例如Java的委派事件模型中,有事件源和事件的处理者,MVC模式中模型(Model)与视图的关系。这类场景都可以使用观察者模式来进行实现。
观察者模式
观察者模式(Observer)的定义:观察者模式又称为:发布-订阅模式、模型-视图模式,Observer即进行观察的人,就是指观察者。当观察对象的状态发生变化时,会自动通知观察者,所有目标对象都会得到通知并被更新。观察者模式适合用于根据对象状态进行相应处理的应用场景。
在Java中,java.util.Observable类和java.util.Observer接口提供了观察者模式的定义。但极不推荐使用这种方式实现观察者模式,实际项目中请根据自己的需求进行定制!
观察者模式的结构
- 抽象主题(Subject):即抽象的观察对象。抽象主题定义了增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
- 具体主题(ConcreteSubject):具体主题即具体的观察对象。它实现了抽象主题中的通知方法,当自身状态发生变化后,它会通知所有已经注册的观察者角色。
- 抽象观察者(Observer):观察者负责接收来自Subject状态变化时的通知,它包含了一个更新自己的抽象方法。
- 具体观察者(ConcreteObserver):实现抽象观察者中的方法,当自身更新的方法被调用后,会主动去获取要观察的对象的最新状态。
观察者模式的实际应用
观察者模式的应用场景较为灵活。它不仅支持封装在独立对象中调用,还支持多层级的嵌套调用,形成链式触发机制。并且可以使用观察者模式实现类似广播(Broadcast)机制的功能,分发消息,在系统中的各个部分进行接收(Android系统广播机制)。
- 事件总线(EventBus)
- MQ消息队列的设计(例如:RabbitMQ的内部实现,许多部分使用了观察者模式)
- 微信公众号的推送系统:当有新文章或新消息发布时,若客户端订阅了该信息,微信服务器将进行实时推送。
示例
在本文中,我们将使用观察者模式来实现一个消息订阅系统,服务器能够主动地向客户端推送消息,客户端负责订阅消息实时接收并显示。
首先,我们先实现一个抽象观察者,抽象观察者中提供一个receive()方法。
Observer.java
package com.yeliheng.observer;
/**
* 抽象观察者
*/
public interface Observer {
void receive(String message);
}
接着,我们来实现具体观察者。即我们的消息订阅者,也就是客户端。
MessageClient.java
package com.yeliheng.observer;
/**
* 具体观察者,即接收消息的客户端
*/
public class MessageClient implements Observer {
@Override
public void receive(String message) {
System.out.println("[客户端] 收到消息:" + message);
}
}
在具体观察者中,我们实现了抽象观察者中的receive()方法,并将接收到的消息进行输出。
现在,我们可以开始编写消息发布者的代码了。
消息发布者即我们的抽象主题和具体主题,它们是被观察的对象。观察者会对它们的状态进行监听,并在状态改变时作出相应的处理。
我们先来实现一个抽象主题。即消息发布者,这是一个抽象类。
IPublisher.java
package com.yeliheng.observer;
import java.util.ArrayList;
import java.util.List;
/**
* 消息发布者
*/
public abstract class IPublisher {
protected List<Observer> subscriberList = new ArrayList<>();
public void add(Observer subscriber) {
subscriberList.add(subscriber);
}
public void remove(Observer subscriber) {
subscriberList.remove(subscriber);
}
public abstract void notifyObserver(String message);
}
如上文所示,IPublisher类是一个抽象主题,它维护了一个观察者列表,并且提供对观察者的增加,删除方法。还包含了一个notifyObserver()方法。这个方法就是用于通知观察者状态的改变。
接着我们实现具体主题。
package com.yeliheng.observer;
public class MessagePublisher extends IPublisher{
@Override
public void notifyObserver(String message) {
System.out.println("[服务器] 已发送消息!");
for(Observer obj : subscriberList) {
obj.receive(message);
}
}
}
具体主题类继承了抽象主题类,并重写了notifyObserver()通知方法。在通知方法中遍历所有存在的观察者并让其主动接收服务器推送过来的消息。
最后,我们可以开始使用我们写好的观察者模式。
Main.java
package com.yeliheng.observer;
public class Main {
public static void main(String[] args) {
MessagePublisher publisher = new MessagePublisher();
MessageClient client = new MessageClient();
publisher.add(client);
publisher.notifyObserver("Hello,Publisher");
}
}
在main函数中,我们注册了一个发布者,并且将观察者MessageClient类添加进发布者维护的观察者列表中。
最后我们尝试发送Hello,Publisher消息通知观察者,看看观察者是否能够正常接收发布者发来的消息。
可以看到,消息被正常发送和接收。
观察者模式的优缺点
优点
- 降低了订阅者和发布者(目标和观察者)之间的耦合度,符合依赖倒置原则。
- 触发的机制使代码逻辑清晰,易于维护。
缺点
- 过度使用可能会造成观察者对象过多,如果采用同步的方式通知观察者,会造成系统开销的增加。
- 目标与观察者之间的依赖关系如果没有正确编写将可能导致循环引用。
总结
观察者模式是一种高效的行为型模式,它可以巧妙地实时监听对象的状态。在必要时进行触发,而不是进行循环地等待。合理地使用观察者模式能够让我们的程序耦合度降低,可拓展性增加。本文通过消息的发布和订阅讲解了观察者模式的应用。
本文示例的完整源代码参见:Github