【设计模式】观察者模式 (发布-订阅模式,模型-视图模式,源-监听器模式,从属者模式)
观察者模式(Observer Pattern)详解
一、观察者模式简介
观察者模式(Observer Pattern) 是一种 行为型设计模式(对象行为型模式),它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象发生变化时,它的所有依赖者(观察者)都会收到通知并自动更新。
你可以把它想象成“订阅-发布”机制:当你订阅了一份杂志后,每当有新一期出版时,你就会收到通知。这里的“你”就是观察者,“杂志”则是被观察的主题。
交通信号灯 <- -> 观察目标
汽车(汽车驾驶员) <- -> 观察者
(一对多)
软件系统:一个对象的状态或行为的变化将导致其他对象的状态或行为也发生改变,它们之间将产生联动。
观察者模式:
- 定义了对象之间一种一对多的依赖关系,让一个对象的改变能够影响其他对象
- 发生改变的对象称为观察目标,被通知的对象称为观察者
- 一个观察目标可以对应多个观察者
别名
发布-订阅(Publish/Subscribe)模式
模型-视图(Model/View)模式
源-监听器(Source/Listener)模式
从属者(Dependents)模式
观察者模式包含以下4个角色:
Subject(目标)
ConcreteSubject(具体目标)
Observer(观察者)
ConcreteObserver(具体观察者)
二、解决的问题类型
观察者模式主要用于解决以下问题:
- 状态变化需要通知相关联的对象:例如,用户界面中的模型数据发生变化时,视图需要自动更新。需要在系统中创建一个触发链。
- 避免紧耦合:允许对象之间保持松散的联系,无需直接相互引用。一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两个方面封装在独立的对象中使它们可以各自独立地改变和复用。
- 实现广播通信:可以向所有注册的观察者发送消息,而不需要知道它们的具体身份。
三、使用场景
场景 | 示例 |
---|---|
GUI事件处理 | 如按钮点击触发窗口刷新 |
消息传递系统 | 如发布/订阅模式的消息队列 |
数据模型与视图同步 | MVC架构中,Model数据变化时通知View更新 |
实时数据监控 | 如股票价格变动时通知所有关注的客户端 |
四、核心概念
- Subject(主题/被观察者):维护了一个观察者列表,并提供方法供观察者注册或移除自身;在状态改变时通知所有观察者。
- Observer(观察者):接收来自主题的通知并作出响应。
- ConcreteSubject & ConcreteObserver:具体实现上述两个接口的实际类。
五、实际代码案例(Java)
1. 定义 Observer 接口
// 观察者接口
public interface Observer {void update(float temperature, float humidity, float pressure);
}
2. 定义 Subject 接口
import java.util.ArrayList;// 主题接口
public interface Subject {void registerObserver(Observer o); // 注册观察者void removeObserver(Observer o); // 移除观察者void notifyObservers(); // 通知所有观察者
}
3. 创建具体的 WeatherData 类作为被观察者
public class WeatherData implements Subject {private ArrayList<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 measurementsChanged() {notifyObservers();}public void setMeasurements(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;measurementsChanged();}
}
4. 创建具体的观察者类
public 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");}
}
5. 测试代码
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);}
}
输出结果:
Current conditions: 80.0F degrees and 65.0% humidity
Current conditions: 82.0F degrees and 70.0% humidity
Current conditions: 78.0F degrees and 90.0% humidity
典型代码
典型的抽象目标类代码
using System.Collection
abstract class Subject
{//定义一个观察者集合用于存储所有观察者对象
protected ArrayList observers = new ArrayList();
//声明抽象注册方法,用于向观察者集合中增加一个观察者public abstract void Attach(Observer observer);
//声明抽象注销方法,用于在观察者集合中删除一个观察者public abstract void Detach(Observer observer);//声明抽象通知方法public abstract void Notify();
}
典型的具体目标类代码
class ConcreteSubject : Subject
{public override void Attach(Observer observer){observers.Add(observer);}public override void Detach(Observer observer){observers.Remove(observer);}//实现通知方法public override void Notify(){//遍历观察者集合,调用每一个观察者的响应方法foreach(object obs in observers){((Observer)obs).Update();}}
}
典型的抽象观察者代码
interface Observer
{void Update();
}
典型的具体观察者代码
class ConcreteObserver : Observer
{//实现响应方法public void Update(){//具体更新代码}
}
典型的客户端代码片段
……
Subject subject = new ConcreteSubject();
Observer observer = new ConcreteObserver();
subject.Attach(observer);
subject.Notify();
……
其他案例
- 在某多人联机对战游戏中,多个玩家可以加入同一战队组成联盟,当战队中的某一成员受到敌人攻击时将给所有其他盟友发送通知,盟友收到通知后将做出响应。
现使用观察者模式设计并实现该过程,以实现战队成员之间的联动。
战队成员之间的联动过程:
联盟成员受到攻击 --> 发送通知给盟友 --> 盟友做出响应
- 猫、狗与老鼠
假设猫是老鼠和狗的观察目标,老鼠和狗是观察者,猫叫老鼠跑,狗也跟着叫,使用观察者模式描述该过程。
六、优缺点分析
优点 | 描述 |
---|---|
✅ 解耦 | 主题和观察者之间是松散耦合的,便于独立开发和修改。可以实现表示层和数据逻辑层的分离。 |
✅ 支持广播通信 | 可以轻松地将信息广播给多个观察者 |
✅ 易于扩展 | 新增观察者只需实现 Observer 接口即可,不影响现有代码。符合开闭原则,增加新的具体观察者无须修改原有系统代码,在具体观察者与观察目标之间不存在关联关系的情况下,增加新的观察目标也很方便 |
缺点 | 描述 |
---|---|
❌ 可能导致性能问题 | 如果观察者数量过多,通知过程可能会变得缓慢 |
❌ 潜在的内存泄漏风险 | 若未正确移除观察者,可能导致无法释放资源。如果存在循环依赖时可能导致系统崩溃 |
❌ 难以追踪依赖关系 | 复杂的观察者网络可能使程序难以调试和理解 |
其他 | 没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而只是知道观察目标发生了变化 |
七、与其他模式对比(补充)
模式名称 | 目标 |
---|---|
命令模式 | 封装请求为对象,支持请求排队、日志记录等操作 |
中介者模式 | 减少对象之间的直接交互,通过中介者进行协调 |
观察者模式 | 维持对象间的一对多依赖关系,实现状态变更的通知 |
八、最终小结
观察者模式是一种非常实用的设计模式,特别适合那些需要在不同组件之间维持松散耦合并且能够响应状态变化的应用场景。通过合理地运用观察者模式,我们可以在不破坏原有模块独立性的前提下,有效地实现组件间的协作与通信。
在开发图形用户界面(GUI)、实时数据监控系统、消息中间件等项目时,观察者模式能够帮助你更好地组织代码结构,提高系统的灵活性和可维护性。
📌 一句话总结:
观察者模式就像一个新闻通讯社,一旦有新的新闻发布,所有订阅了该新闻的人都会立即收到通知。
部分内容由AI大模型生成,注意识别!