Skip to content

观察者模式(Observer Pattern)

观察者模式(Observer Pattern)是一种行为设计模式,它定义了对象之间的一对多依赖关系,使得当一个对象的状态发生变化时,所有依赖于它的对象都会得到通知并自动更新。观察者模式又称为发布-订阅(Publish-Subscribe)模式。

关键点

  1. 主题(Subject):也称为被观察者或者可观察对象,维护一系列观察者对象,提供添加、删除和通知观察者的方法。
  2. 观察者(Observer):也称为订阅者,定义了接收和处理通知的接口。
  3. 具体主题(ConcreteSubject):实现了主题接口,负责维护观察者列表,并在状态变化时通知观察者。
  4. 具体观察者(ConcreteObserver):实现了观察者接口,实现接收和处理通知的具体行为。

示例

假设我们有一个简单的气象站应用,它能够实时获取温度、湿度和气压数据,并将这些数据显示在多个布告板上。我们可以使用观察者模式来实现气象数据和布告板之间的解耦。

1. 定义主题接口和观察者接口

csharp
// 主题接口
public interface ISubject
{
    void RegisterObserver(IObserver observer);
    void RemoveObserver(IObserver observer);
    void NotifyObservers();
}

// 观察者接口
public interface IObserver
{
    void Update(float temperature, float humidity, float pressure);
}

2. 实现具体主题和具体观察者类

csharp
using System;
using System.Collections.Generic;

// 具体主题类 - 气象数据
public class WeatherData : ISubject
{
    private List<IObserver> observers;
    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherData()
    {
        observers = new List<IObserver>();
    }

    public void RegisterObserver(IObserver observer)
    {
        observers.Add(observer);
    }

    public void RemoveObserver(IObserver observer)
    {
        observers.Remove(observer);
    }

    public void NotifyObservers()
    {
        foreach (var observer in 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();
    }
}

// 具体观察者类 - 布告板
public class Display : IObserver
{
    private float temperature;
    private float humidity;
    private float pressure;
    private WeatherData weatherData;

    public Display(WeatherData weatherData)
    {
        this.weatherData = weatherData;
        weatherData.RegisterObserver(this);
    }

    public void Update(float temperature, float humidity, float pressure)
    {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        DisplayData();
    }

    public void DisplayData()
    {
        Console.WriteLine($"Current conditions: {temperature}F degrees and {humidity}% humidity");
    }
}

3. 客户端代码

csharp
class Program
{
    static void Main(string[] args)
    {
        WeatherData weatherData = new WeatherData();

        // 创建布告板并注册到气象数据主题
        Display display = new Display(weatherData);

        // 模拟气象数据变化
        weatherData.SetMeasurements(80, 65, 30.4f);
        weatherData.SetMeasurements(82, 70, 29.2f);
    }
}

解释

  1. 主题接口(ISubject):定义了主题对象需要实现的方法,包括注册、移除和通知观察者。
  2. 观察者接口(IObserver):定义了观察者对象需要实现的更新方法 Update
  3. 具体主题类(WeatherData):实现了主题接口,维护观察者列表,并在数据变化时通知所有观察者。
  4. 具体观察者类(Display):实现了观察者接口,接收并处理主题通知,实现了更新方法以更新布告板上的显示数据。

优点

  • 松耦合:主题对象和观察者对象之间解耦,可以独立地改变和复用。
  • 增强了模块的重用性:可以方便地增加和删除观察者,扩展性好。
  • 支持广播通信:主题对象在状态改变时可以通知多个观察者。

缺点

  • 观察者过多时可能会导致性能问题:如果观察者数量庞大或者观察者的更新操作较为复杂,可能会影响性能。

适用场景

  • 对象间存在一对多的依赖关系:当一个对象的状态发生变化时,需要通知多个对象。
  • 抽象模型有两个方面:一个依赖于另一个。

示例的现实应用

观察者模式在许多现实应用中都有应用:

  • 事件处理系统:例如 GUI 应用程序中的事件处理机制。
  • 消息中间件:例如 RabbitMQ、Kafka 等消息队列中的发布-订阅模型。
  • 股票市场报价系统:例如股票价格变动时,通知所有相关的投资者和分析师。

通过使用观察者模式,可以实现对象之间的松耦合,提高系统的灵活性和可维护性。