观察者模式(Observer Pattern)
观察者模式是一种对象行为模式。它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。在观察者模式中,主体是通知的发布者,它发出通知时并不需要知道谁是它的观察者,可以有任意数目的观察者订阅并接收通知。观察者模式不仅被广泛应用于软件界面元素之间的交互,在业务对象之间的交互、权限管理等方面也有广泛的应用。
观察者模式(Observer)完美的将观察者和被观察的对象分离开。举个例子,用户界面可以作为一个观察者,业务数据是被观察者,用户界面观察业务数据的变化,发现数据变化后,就显示在界面上。面向对象设计的一个原则是:系统中的每个类将重点放在某一个功能上,而不是其他方面。一个对象只做一件事情,并且将他做好。观察者模式在模块之间划定了清晰的界限,提高了应用程序的可维护性和重用性。
观察者设计模式定义了对象间的一种一对多的组合关系,以便一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动刷新。
核心组件
- Subject(主题):主题保存了一组观察者,提供用于增加或删除观察者的接口。
- Observer(观察者):为那些在主题状态改变时需要获得通知的对象定义一个更新接口。
- ConcreteSubject(具体主题):存储具体观察者关心的数据,发送通知给观察者,通常包含对具体数据的增删改操作。
- ConcreteObserver(具体观察者):实现观察者接口,以便在得到通知时更新自身。
适用场景
- 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面时:
- 观察者模式允许你独立地扩展或复用核心功能。
- 当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象需要改变时:
- 观察者模式可以让你在不修改这些类的前提下增加新的观察者。
- 当一个对象必须通知其他对象,但你希望避免做成这些对象之间的紧密耦合时:
- 观察者模式提供了一种松耦合的设计解决方案。
实现实例
以新闻应用为例,其中新闻发布系统(主题)需要通知所有订阅者(观察者)新闻更新。
主题接口(Subject Interface)
定义了附加和删除观察者的方法,以及通知观察者的方法。
public interface Subject {
void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers();
}
具体主题(Concrete Subject)
存储状态,并在状态改变时通知观察者。
public class NewsAgency implements Subject {
private List<Observer> observers = new ArrayList<>();
private String news;
public void registerObserver(Observer o) {
observers.add(o);
}
public void removeObserver(Observer o) {
observers.remove(o);
}
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(news);
}
}
public void setNews(String news) {
this.news = news;
notifyObservers();
}
}
观察者接口(Observer Interface)
定义更新接口,用于获取状态改变通知。
public interface Observer {
void update(String news);
}
具体观察者(Concrete Observer)
实现观察者接口,根据通知更新自己。
public class NewsChannel implements Observer {
private String news;
@Override
public void update(String news) {
this.news = news;
System.out.println("News Updated: " + news);
}
}
客户端代码(Client Code)
演示如何使用观察者模式。
public class Client {
public static void main(String[] args) {
NewsAgency newsAgency = new NewsAgency();
NewsChannel channel = new NewsChannel();
newsAgency.registerObserver(channel);
newsAgency.setNews("New update on AI technology!");
}
}
优缺点
优点
- 支持广播通信:
- 观察者模式提供了一种订阅机制,可以实现消息的广播。
- 应用松耦合:
- 主题与观察者之间使用抽象耦合,可以轻松地添加新的观察者。
缺点
- 可能引发无序的更新:
- 如果观察者的更新方法引发循环依赖,可能导致系统行为难以预测。
- 当观察者和主题之间的依赖关系复杂时,可能需要额外的维护成本。
类图
+----------------+ +------------------+
| Subject |-------->| Observer |
+----------------+ +------------------+
| + registerObserver() | + update() |
| + removeObserver() | |
| + notifyObservers() +------------------+
+----------------+ |
| | |
+----------------+ |
|
+-------------------+--------+----------------+
| | | |
+---------------+ +-----------------+ +----------------+ +--------------+
|ConcreteSubject| |ConcreteObserver1| |ConcreteObserver2| | ... |
+---------------+ +-----------------+ +----------------+ +--------------+
| + setNews() | | + update() | | + update() | | + update() |
+---------------+ +-----------------+ +----------------+ +--------------+
总结
观察者模式提供了一种优雅的方式来实现对象间的通信,特别适用于状态变化需要通知多个对象的场景。通过将主题与观察者解耦,增加了系统的灵活性和可扩展性,同时也需注意避免循环依赖和更新顺序问题。