文章目录
- 1、观察者模式设计初衷
- 1.1. 解耦对象之间的依赖关系
- 1.2. 允许动态的依赖关系
- 1.3. 自动通知和更新
- 1.4 设计初衷的详细说明
- 1. 对象之间的解耦
- 2. 动态依赖关系
- 3. 自动更新
- 2、实现细节
- 2.1. Subject 接口和实现
- 2.2. Observer 接口和实现
- 2.3. 主类
- 3、主要角色
- 4、关系示意图
- 5、Java 实现示例
- 使用 Java 内置的 Observable 和 Observer
- Observable 类
- Observer 接口
- 主类
- 6、自定义实现 Observer 和 Subject
- 6.1 Subject 接口
- 6.2 Observer 接口
- 主类
- 7、优缺点分析
- 7.1 优点
- 7.2 缺点
- 8、总结
1、观察者模式设计初衷
1.1. 解耦对象之间的依赖关系
在软件系统中,对象之间往往需要相互依赖。例如,视图对象依赖于数据模型对象,来显示最新的数据状态。直接的依赖关系会导致高耦合,增加维护的复杂性和难度。
设计初衷:
- 松耦合设计:观察者模式通过抽象层(Observer接口)来解耦具体的对象。被观察者(Subject)只知道有观察者存在,不需要了解观察者的具体实现。
- 易于扩展:通过接口解耦,可以方便地增加新的观察者而不影响现有的代码,实现开闭原则(对扩展开放,对修改关闭)。
示例:
一个天气预报系统中,WeatherData
类作为被观察者,而CurrentConditionsDisplay
、StatisticsDisplay
等作为观察者。WeatherData
不需要了解具体显示类的实现,只需知道它们实现了Observer
接口。
1.2. 允许动态的依赖关系
在运行时,系统的依赖关系可能需要动态变化。例如,用户可能会在运行时订阅或取消订阅某些数据。
设计初衷:
- 动态注册和注销观察者:观察者模式允许在运行时动态地添加和移除观察者,灵活应对变化的需求。
- 灵活性和动态性:被观察者可以在任何时候通知其观察者,无需事先确定所有的观察者。
示例:
在一个股票交易系统中,用户可以随时订阅或取消订阅股票行情。StockData
类作为被观察者,当用户订阅时,将其客户端注册为观察者,当用户取消订阅时,将其从观察者列表中移除。
1.3. 自动通知和更新
在一些实时更新的系统中,依赖对象需要在状态变化时自动更新。例如,当数据模型改变时,视图对象需要自动更新以反映最新的数据。
设计初衷:
- 自动通知:被观察者在其状态改变时,自动通知所有注册的观察者,避免手动更新带来的复杂性。
- 一致性:确保所有依赖于被观察者的对象在状态变化后保持一致性,避免数据不同步问题。
示例:
在一个气象站应用中,当气象数据变化时,WeatherData
会通知所有的显示组件(观察者),以便它们更新显示最新的气象数据。
1.4 设计初衷的详细说明
1. 对象之间的解耦
问题:
在软件系统中,不同的对象之间往往存在依赖关系。例如,一个数据模型对象(Model)和多个视图对象(View)。当数据模型发生变化时,视图对象需要随之更新。如果每个视图对象都直接依赖于数据模型对象,并且在数据模型中直接调用视图对象的方法,这将导致系统的高耦合,增加维护难度。
解决方案:
通过观察者模式,被观察者(Subject)和观察者(Observer)之间的关系是松耦合的。被观察者只知道有一组观察者实现了某个接口,而不知道具体是什么对象。观察者也只知道被观察者的状态发生了变化,而不知道变化的具体原因。这样就实现了对象之间的解耦。
示例:
在一个气象站应用中,气象数据(WeatherData)是被观察者,而各种显示元素(如当前条件显示、统计显示等)是观察者。气象数据只需要通知观察者数据发生了变化,而具体如何显示是观察者的事情。
2. 动态依赖关系
问题:
系统在运行时可能需要动态地添加或移除依赖关系。例如,在一个实时股票行情系统中,不同的客户端可能在不同的时间段订阅或取消订阅股票行情。
解决方案:
观察者模式允许在运行时动态地注册(添加)或注销(移除)观察者。这使得系统能够灵活地响应不同的需求和变化,而无需在设计时确定所有的依赖关系。
示例:
在一个聊天应用中,当用户加入一个群组时,用户的客户端会成为群组消息的观察者。当用户离开群组时,用户的客户端会被移除出群组消息的观察者列表。
3. 自动更新
问题:
当一个对象的状态变化时,依赖它的所有对象需要同步更新。例如,在一个数据驱动的应用中,当数据模型变化时,所有依赖于该模型的视图都需要更新。
解决方案:
通过观察者模式,当被观察者的状态发生变化时,它会通知所有注册的观察者进行更新。这样,所有依赖于被观察者的对象都能够自动更新,保持系统的一致性。
示例:
在一个股票交易应用中,当股票价格变化时,所有显示该股票价格的视图都会自动更新,以显示最新的价格。
2、实现细节
2.1. Subject 接口和实现
import java.util.ArrayList;
import java.util.List;public interface Subject {void registerObserver(Observer o);void removeObserver(Observer o);void notifyObservers();
}class WeatherData implements Subject {private List<Observer> observers;private float temperature;private float humidity;private float pressure;public WeatherData() {observers = new ArrayList<>();}@Overridepublic void registerObserver(Observer o) {observers.add(o);}@Overridepublic void removeObserver(Observer o) {observers.remove(o);}@Overridepublic void notifyObservers() {for (Observer observer : observers) {observer.update(temperature, humidity, pressure);}}public void setMeasurements(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;measurementsChanged();}public void measurementsChanged() {notifyObservers();}
}
2.2. Observer 接口和实现
public interface Observer {void update(float temperature, float humidity, float pressure);
}class CurrentConditionsDisplay implements Observer {private float temperature;private float humidity;private Subject weatherData;public CurrentConditionsDisplay(Subject weatherData) {this.weatherData = weatherData;weatherData.registerObserver(this);}@Overridepublic void update(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;display();}public void display() {System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");}
}
2.3. 主类
public class WeatherStation {public static void main(String[] args) {WeatherData weatherData = new WeatherData();CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);weatherData.setMeasurements(80, 65, 30.4f);weatherData.setMeasurements(82, 70, 29.2f);weatherData.setMeasurements(78, 90, 29.2f);}
}
3、主要角色
-
Subject(被观察者/主题):
- 保存观察者的列表。
- 向观察者发送通知。
- 提供注册和移除观察者的方法。
-
Observer(观察者):
- 接收通知并更新状态。
- 包含一个更新方法,用于被主题通知。
4、关系示意图
+----------+ +-----------+
| Subject |<------>| Observer |
+----------+ +-----------+
| attach() | | update() |
| detach() | +-----------+
| notify() |
+----------+
5、Java 实现示例
使用 Java 内置的 Observable 和 Observer
Java 提供了内置的 java.util.Observable
类和 java.util.Observer
接口。
Observable 类
import java.util.Observable;public class WeatherData extends Observable {private float temperature;private float humidity;private float pressure;public void setMeasurements(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;measurementsChanged();}public void measurementsChanged() {setChanged();notifyObservers();}public float getTemperature() {return temperature;}public float getHumidity() {return humidity;}public float getPressure() {return pressure;}
}
Observer 接口
import java.util.Observable;
import java.util.Observer;public class CurrentConditionsDisplay implements Observer {private float temperature;private float humidity;public CurrentConditionsDisplay(Observable observable) {observable.addObserver(this);}@Overridepublic void update(Observable obs, Object arg) {if (obs instanceof WeatherData) {WeatherData weatherData = (WeatherData) obs;this.temperature = weatherData.getTemperature();this.humidity = weatherData.getHumidity();display();}}public void display() {System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");}
}
主类
public class WeatherStation {public static void main(String[] args) {WeatherData weatherData = new WeatherData();CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);weatherData.setMeasurements(80, 65, 30.4f);weatherData.setMeasurements(82, 70, 29.2f);weatherData.setMeasurements(78, 90, 29.2f);}
}
6、自定义实现 Observer 和 Subject
在现代Java编程中,更推荐使用自定义接口和类实现观察者模式,因为 java.util.Observable
已被标记为过时。
6.1 Subject 接口
import java.util.ArrayList;
import java.util.List;public interface Subject {void registerObserver(Observer o);void removeObserver(Observer o);void notifyObservers();
}class WeatherData implements Subject {private List<Observer> observers;private float temperature;private float humidity;private float pressure;public WeatherData() {observers = new ArrayList<>();}@Overridepublic void registerObserver(Observer o) {observers.add(o);}@Overridepublic void removeObserver(Observer o) {int i = observers.indexOf(o);if (i >= 0) {observers.remove(i);}}@Overridepublic void notifyObservers() {for (Observer observer : observers) {observer.update(temperature, humidity, pressure);}}public void setMeasurements(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;measurementsChanged();}public void measurementsChanged() {notifyObservers();}
}
6.2 Observer 接口
public interface Observer {void update(float temp, float humidity, float pressure);
}class CurrentConditionsDisplay implements Observer {private float temperature;private float humidity;@Overridepublic void update(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;display();}public void display() {System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");}
}
主类
public class WeatherStation {public static void main(String[] args) {WeatherData weatherData = new WeatherData();CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay();weatherData.registerObserver(currentDisplay);weatherData.setMeasurements(80, 65, 30.4f);weatherData.setMeasurements(82, 70, 29.2f);weatherData.setMeasurements(78, 90, 29.2f);}
}
7、优缺点分析
7.1 优点
-
松耦合:
- 观察者和被观察者之间是松耦合的,便于维护和扩展。
-
动态注册和注销:
- 可以在运行时动态添加或移除观察者,灵活应对变化的需求。
-
自动通知:
- 被观察者状态变化时,会自动通知所有观察者,保持系统的一致性。
7.2 缺点
-
通知顺序不确定:
- 观察者接收通知的顺序不确定,可能导致一些时序问题。
-
内存泄漏:
- 如果被观察者没有正确地移除不再需要的观察者,可能导致内存泄漏。
-
性能问题:
- 如果观察者数量过多,通知过程可能比较耗时,影响性能。
8、总结
观察者模式的设计初衷主要在于解决对象之间的耦合问题,允许动态依赖关系,并自动通知依赖对象进行更新。这些特性使得观察者模式在需要松耦合、动态依赖和自动更新的场景中非常有用。然而,开发者在使用观察者模式时,也需要注意内存泄漏、性能开销等问题,采取适当的优化措施,如使用弱引用和异步通知,来提升系统的健壮性和效率。
通过合理使用观察者模式,可以使系统更加灵活、可维护性更高,并且能够有效地响应变化。这种模式在GUI应用、实时数据更新、发布-订阅系统等场景中得到了广泛应用,是软件设计中的重要模式之一。
欢迎点赞|关注|收藏|评论,您的肯定是我创作的动力 |