前言
观察者模式(Observer Pattern)也被称为发布订阅模式(Publish-Subscribe Design Pattern)。它是一种行为设计模式,允许对象之间建立一种一对多的依赖关系,这样当一个对象状态改变时,它的所有依赖者(观察者)都会收到通知并自动更新。
一、角色和结构图
观察者模式的主要角色包括:
- 抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
- 具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法。当具体主题的内部状态发生改变时,会通知所有注册过的观察者对象。
- 抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
- 具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
二、应用场景
观察者模式是一种广泛应用的软件设计模式,它在多种场景中发挥着重要的作用。以下是观察者模式的一些典型应用场景:
- 图形用户界面(GUI)设计:在GUI中,观察者模式常用于处理组件之间的交互。例如,当按钮被点击时,它可以作为观察者通知窗口(被观察者)进行相应的事件处理。这种模式有助于实现组件之间的解耦,使得它们可以独立地进行开发和维护。
- 消息订阅与发布系统:观察者模式适用于构建消息发布/订阅系统。在这种系统中,消息发布者充当主题(被观察者),而订阅者则充当观察者。当发布者发布新消息时,所有订阅者都会收到通知并执行相应操作。这种模式常见于新闻订阅、实时数据监控、社交媒体平台等场景。
- Spring框架:Spring 中实现的观察者模式包含三部分:Event 事件(相当于消息)、Listener 监听者(相当于观察者)、Publisher 发送者(相当于被观察者)。
- 事件驱动系统:观察者模式在事件驱动系统中有着广泛的应用,如游戏引擎。当特定事件发生时,如游戏角色的位置变化,触发相应的回调函数并通知所有注册的观察者,从而更新图形显示。
- 实时日志记录系统:观察者模式也可以用于实时日志记录系统,其中日志记录器充当被观察者,而观察者可以是日志分析器、报警系统等。当日志发生变化时,观察者将收到通知并执行相应的操作,如生成报告、发送警报等。
此外,观察者模式还可以应用于团队协作、实时数据更新、硬件状态监控等多种场景。总的来说,当存在对象之间的一对多关系,并且需要实时通知和更新时,观察者模式是一个非常有效的解决方案。
三、观察者模式的简易实现
观察者模式可以通过简单的代码实现。以下是一个使用Java语言实现的简易示例:
首先,我们定义一个Observer
接口,它声明了一个update
方法,该方法将由被观察者(Subject)在状态改变时调用:
public interface Observer {
void update(String message);
}
接着,我们定义一个Subject
接口,它声明了注册、移除和通知观察者的方法:
import java.util.ArrayList;
import java.util.List;
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers(String message);
}
然后,我们实现一个具体的ConcreteSubject
类,它实现了Subject
接口,并维护了一个观察者列表:
public class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers(String message) {
for (Observer observer : observers) {
observer.update(message);
}
}
// 这里可以添加一些改变状态的方法,并在状态改变时调用notifyObservers
}
最后,我们实现一个具体的ConcreteObserver
类,它实现了Observer
接口:
public class ConcreteObserver implements Observer {
private String name;
public ConcreteObserver(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " received message: " + message);
}
}
现在,我们可以创建一个ConcreteSubject
实例,并注册一些ConcreteObserver
实例,然后通知它们:
public class ObserverPatternDemo {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
Observer observer1 = new ConcreteObserver("Observer 1");
Observer observer2 = new ConcreteObserver("Observer 2");
subject.registerObserver(observer1);
subject.registerObserver(observer2);
// 当ConcreteSubject的状态改变时,它会调用notifyObservers方法
subject.notifyObservers("State has changed!");
// 如果需要,可以移除某个观察者
subject.removeObserver(observer1);
// 再次通知,这次observer1将不会收到消息
subject.notifyObservers("Another state change.");
}
}
运行ObserverPatternDemo
类的main
方法,你将看到观察者如何接收到来自被观察者的通知,并输出相应的消息。
这个简单的实现展示了观察者模式的基本结构和工作原理。在实际应用中,你可以根据需要添加更多的功能,比如错误处理、线程安全等。
四、异步非阻塞观察者模式的简易实现
异步非阻塞除了能实现代码解耦之外,还能提高代码的执行效率;进程间的观察者模式解耦更加彻底,一般是基于消息队列来实现,用来实现不同进程间的被观察者和观察者之间的交互。 异步非阻塞观察者模式通常涉及到多线程或异步编程技术,使得被观察者(Subject)可以在不阻塞当前线程的情况下通知观察者(Observer)。下面是一个简易的Java实现,使用Java的ExecutorService
来异步地通知观察者:
首先,我们定义Observer
接口和Subject
接口与前面的示例类似:
public interface Observer {
void update(String message);
}
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObserversAsync(String message);
}
接下来,我们实现一个AsyncConcreteSubject
类,它实现了Subject
接口,并使用ExecutorService
来异步地通知观察者:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class AsyncConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
private final ExecutorService executorService = Executors.newCachedThreadPool();
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObserversAsync(final String message) {
executorService.submit(() -> {
for (Observer observer : observers) {
observer.update(message);
}
});
}
// 关闭ExecutorService,释放资源
public void shutdown() {
executorService.shutdown();
}
}
现在,我们实现一个ConcreteObserver
类,它实现了Observer
接口:
public class ConcreteObserver implements Observer {
private String name;
public ConcreteObserver(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " received message asynchronously: " + message);
}
}
最后,我们创建一个AsyncObserverPatternDemo
类来演示异步非阻塞观察者模式:
public class AsyncObserverPatternDemo {
public static void main(String[] args) throws InterruptedException {
AsyncConcreteSubject subject = new AsyncConcreteSubject();
Observer observer1 = new ConcreteObserver("Observer 1");
Observer observer2 = new ConcreteObserver("Observer 2");
subject.registerObserver(observer1);
subject.registerObserver(observer2);
// 异步地通知观察者
subject.notifyObserversAsync("State has changed asynchronously!");
// 等待一段时间,确保异步任务完成
Thread.sleep(1000);
subject.shutdown(); // 关闭ExecutorService
}
}
在这个例子中,当notifyObserversAsync
方法被调用时,它会在一个单独的线程中执行observer.update(message)
,因此不会阻塞主线程。Thread.sleep(1000)
只是为了演示目的,确保异步任务有足够的时间完成。在实际应用中,你可能需要更健壮的机制来处理异步任务的完成,比如使用Future
或CompletableFuture
。
请注意,使用ExecutorService
时,应该合理地管理线程池的生命周期,包括关闭它,以避免资源泄漏。在上面的示例中,我们提供了一个shutdown
方法来关闭ExecutorService
。在应用程序结束时,应该调用此方法。
此外,对于更复杂的场景,比如错误处理、线程池大小的管理、任务的超时处理等,你可能需要使用更高级的并发控制机制。