定义:
Define a one-to-many dependency between objects so that when one object changes state,all its dependents are notified and updated automatically.(定义对象间一种一对多的依赖关系,使得每 当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。)
观察者模式通用类图
观察者设计模式的核心思想在于建立对象之间的依赖关系,并实现事件驱动的通知机制。被观察者维护一个观察者列表,当自身状态改变时,遍历该列表,主动调用每个观察者的更新方法,从而实现状态变化的自动传播。同时,观察者可以自由地注册到被观察者或从被观察者中注销,两者之间无需了解对方的具体实现细节,降低了对象之间的耦合度 。
角色:
观察者模式包含以下几个核心角色:
1、抽象主题(Subject)
抽象主题是被观察对象的抽象类或接口,它定义了添加观察者、删除观察者以及通知观察者的抽象方法。同时,它还维护一个观察者对象的列表,用于存储所有注册的观察者。抽象主题是连接被观察者和观察者的桥梁,为具体被观察者提供统一的操作接口。
2、具体主题(ConcreteSubject)
具体主题是抽象主题的具体实现类,它负责实现抽象主题中定义的方法。当具体主题的状态发生改变时,它会调用抽象主题中通知观察者的方法,将状态变化告知所有注册的观察者。具体主题是实际被观察的对象,其状态变化会触发观察者的更新。
3、抽象观察者(Observer)
抽象观察者是观察者的抽象类或接口,它定义了一个更新方法,用于接收被观察者的通知,并在接收到通知后执行相应的操作。抽象观察者为具体观察者提供了统一的接口,使得具体观察者可以以一致的方式与被观察者进行交互。
4、具体观察者(ConcreteObserver)
具体观察者是抽象观察者的具体实现类,它实现了抽象观察者中定义的更新方法。在更新方法中,具体观察者根据接收到的被观察者的状态变化信息,执行具体的更新操作,如更新自身的显示、执行特定的业务逻辑等。具体观察者是实际接收通知并进行响应的对象。
代码示例:
以天气监测系统为例,气象站作为被观察者,用户作为观察者。当气象站监测到天气数据(温度、湿度、风速等)发生变化时,及时通知订阅天气信息的用户。
// 抽象观察者
public interface Observer {
void update(float temperature, float humidity, float windSpeed);
}
// 具体观察者,用户
public class User implements Observer {
private String name;
public User(String name) {
this.name = name;
}
@Override
public void update(float temperature, float humidity, float windSpeed) {
System.out.println(name + " 收到天气更新:温度 " + temperature + "℃,湿度 " + humidity + "%,风速 " + windSpeed + "m/s");
}
}
// 抽象主题
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
// 具体主题,气象站
public class WeatherStation implements Subject {
private float temperature;
private float humidity;
private float windSpeed;
private java.util.ArrayList<Observer> observers;
public WeatherStation() {
observers = new java.util.ArrayList<>();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
int i = observers.indexOf(observer);
if (i >= 0) {
observers.remove(i);
}
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(temperature, humidity, windSpeed);
}
}
public void setWeatherData(float temperature, float humidity, float windSpeed) {
this.temperature = temperature;
this.humidity = humidity;
this.windSpeed = windSpeed;
notifyObservers();
}
}
// 客户端代码
public class ObserverPatternClient {
public static void main(String[] args) {
WeatherStation weatherStation = new WeatherStation();
User user1 = new User("Alice");
User user2 = new User("Bob");
weatherStation.registerObserver(user1);
weatherStation.registerObserver(user2);
weatherStation.setWeatherData(25, 60, 3);
weatherStation.removeObserver(user1);
weatherStation.setWeatherData(28, 55, 4);
}
}
优点 :
1、实现松耦合:被观察者和观察者之间通过抽象接口进行交互,彼此无需了解对方的具体实现细节,降低了对象之间的耦合度。当被观察者或观察者的实现发生变化时,不会影响到对方,提高了系统的可维护性和扩展性。
2、易于扩展:可以方便地增加新的观察者或被观察者,而无需修改现有代码。新的观察者只需实现抽象观察者接口,然后注册到被观察者中即可接收通知;新的被观察者只需实现抽象主题接口,就能与现有的观察者进行交互。
3、支持事件驱动编程:观察者模式符合事件驱动的编程思想,能够实现对象状态变化的自动传播,使得系统能够及时响应各种事件,提高了系统的实时性和交互性。
缺点:
1、内存泄漏风险:如果观察者注册后没有正确注销,可能会导致观察者对象无法被垃圾回收,从而造成内存泄漏。特别是在长时间运行的系统中,需要特别注意观察者的生命周期管理。
2、通知顺序不确定:在被观察者通知观察者时,由于观察者列表的遍历顺序不确定,可能导致观察者接收到通知的顺序与预期不符。在某些对通知顺序有严格要求的场景中,需要额外处理来确保通知顺序的正确性。
3、性能问题:当观察者数量较多时,被观察者通知所有观察者的操作可能会带来一定的性能开销。尤其是在状态变化频繁的情况下,可能会影响系统的整体性能,需要考虑优化通知机制,如采用异步通知等方式。
使用场景:
(一)事件驱动的系统
当系统需要基于事件进行响应时,观察者模式非常适用。例如,图形用户界面(GUI)中的按钮点击事件、鼠标移动事件等,当用户触发这些事件(被观察者状态改变)时,相应的事件处理程序(观察者)会被调用,执行特定的操作 。
(二)消息订阅与发布系统
在消息订阅与发布系统中,消息发布者作为被观察者,消息订阅者作为观察者。订阅者可以根据自己的需求订阅感兴趣的消息主题,当发布者发布新消息时,自动通知所有订阅该主题的订阅者。这种模式在实时通信、新闻推送、邮件通知等场景中广泛应用。
(三)状态监控与预警系统
对于需要实时监控对象状态并在状态发生变化时进行预警的系统,观察者模式是一个很好的选择。例如,服务器监控系统中,当服务器的 CPU 使用率、内存占用率等指标超出阈值(被观察者状态改变)时,及时通知系统管理员(观察者),以便采取相应的措施。
观察者设计模式通过建立对象之间的一对多依赖关系,实现了状态变化的自动传播和对象间的动态联动。它在事件驱动系统、消息订阅与发布系统、状态监控与预警系统等众多领域有着广泛的应用。该模式具有松耦合、易于扩展等显著优点,但也存在内存泄漏风险、通知顺序不确定和性能问题等缺点。在实际开发中,我们应根据具体需求合理运用观察者设计模式,充分发挥其优势,同时注意规避其潜在的问题,从而编写出更加灵活、高效、可维护的代码。希望本文能帮助你深入理解观察者设计模式,并在今后的项目中熟练运用它解决实际问题。