C#中观察者模式(Observer Pattern)深入解析

观察者模式(Observer Pattern)是一种行为型设计模式,用于定义对象间的一对多依赖关系,使得当一个对象的状态发生变化时,其所有依赖者(观察者)都会自动收到通知并更新。这种模式广泛应用于事件处理、消息订阅系统等领域。

一、观察者模式的核心概念

观察者模式涉及两个主要角色:

  1. 主题(Subject):也称为"被观察者",它是一个对象,它的状态发生变化时,通知所有已注册的观察者。主题通常提供注册、注销和通知观察者的功能。

  2. 观察者(Observer):也称为"订阅者",它是依赖于主题的对象,观察者会在主题发生变化时接收通知并做出响应。

通过观察者模式,主题与观察者之间解耦,主题无需知道观察者的具体实现,观察者只需要实现通知接口即可。

二、观察者模式的类图

观察者模式的典型类图如下:

复制代码
       +---------------+      +----------------+
       |   Subject     |      |    Observer    |
       |---------------|      |----------------|
       | +Attach()     |      | +Update()      |
       | +Detach()     |<---->|                |
       | +Notify()     |      +----------------+
       +---------------+        
            |
        +------------+ 
        | Concrete  |
        |  Subject  |
        +------------+ 
            |
        +--------------------+
        | Concrete Observer  |
        +--------------------+
  • Subject(主题/被观察者):具有状态的对象,维护观察者列表,并提供方法用于注册、注销观察者。

  • Observer(观察者) :对主题的状态变化做出响应的接口,通常会有一个 Update() 方法。

  • ConcreteSubject(具体主题) :实现了 Subject 类,保存主题的状态,并在状态变化时通知观察者。

  • ConcreteObserver(具体观察者) :实现了 Observer 接口,处理主题状态变化时的具体行为。

三、观察者模式的应用场景
  1. GUI事件处理:在图形用户界面(GUI)中,观察者模式广泛用于事件处理。例如,当按钮被点击时,系统通知所有注册的监听器来响应这个事件。

  2. 发布-订阅系统:例如,新闻应用中的用户订阅和接收新闻更新,或股票价格更新系统,用户可以订阅某个股票的变化。

  3. 日志记录系统:当应用程序发生某些变化时,系统可以通知所有注册的观察者(如日志记录器)来进行相应操作。

四、C#实现观察者模式

下面是一个简单的 C# 实现观察者模式的例子。在这个例子中,NewsAgency 类是一个主题,Subscriber 类是观察者。当新闻发布时,所有订阅者都会接收到更新的新闻。

复制代码
using System;
using System.Collections.Generic;

// 观察者接口
public interface IObserver
{
    void Update(string news);
}

// 主题接口
public interface ISubject
{
    void Attach(IObserver observer);   // 注册观察者
    void Detach(IObserver observer);   // 注销观察者
    void Notify();                     // 通知观察者
}

// 具体主题(被观察者)
public class NewsAgency : ISubject
{
    private List<IObserver> _observers = new List<IObserver>();  // 观察者列表
    private string _latestNews;

    public string LatestNews
    {
        get { return _latestNews; }
        set
        {
            _latestNews = value;
            Notify();  // 一旦新闻变化,通知所有观察者
        }
    }

    public void Attach(IObserver observer)
    {
        _observers.Add(observer);
    }

    public void Detach(IObserver observer)
    {
        _observers.Remove(observer);
    }

    public void Notify()
    {
        foreach (var observer in _observers)
        {
            observer.Update(_latestNews);  // 通知所有观察者
        }
    }
}

// 具体观察者
public class Subscriber : IObserver
{
    public string Name { get; }

    public Subscriber(string name)
    {
        Name = name;
    }

    public void Update(string news)
    {
        Console.WriteLine($"{Name} received news: {news}");
    }
}

class Program
{
    static void Main(string[] args)
    {
        // 创建新闻机构(主题)
        NewsAgency newsAgency = new NewsAgency();

        // 创建多个订阅者(观察者)
        Subscriber subscriber1 = new Subscriber("John");
        Subscriber subscriber2 = new Subscriber("Jane");
        Subscriber subscriber3 = new Subscriber("Alex");

        // 注册订阅者
        newsAgency.Attach(subscriber1);
        newsAgency.Attach(subscriber2);
        newsAgency.Attach(subscriber3);

        // 发布新闻
        Console.WriteLine("Publishing news...");
        newsAgency.LatestNews = "Breaking News: New technology launched!";

        // 注销订阅者
        newsAgency.Detach(subscriber2);

        // 发布新的新闻
        Console.WriteLine("\nPublishing another news...");
        newsAgency.LatestNews = "Breaking News: Major event happening!";
    }
}
五、代码解析
  1. IObserver接口 :定义了Update方法,供观察者类实现。当主题发生变化时,Update方法会被调用。

  2. ISubject接口 :定义了Attach(注册观察者)、Detach(注销观察者)和Notify(通知观察者)方法。Notify方法会通知所有已注册的观察者。

  3. NewsAgency(新闻机构)类 :实现了 ISubject 接口,维护了一个观察者列表(_observers)。当新闻更新时,调用 Notify 方法通知所有订阅者。

  4. Subscriber(订阅者)类 :实现了 IObserver 接口,包含一个 Update 方法,每当主题状态发生变化时,Update 方法就会被调用,接收最新的新闻。

  5. Main 方法 :在 Main 方法中,创建了一个新闻机构并注册了几个订阅者。当新闻更新时,所有订阅者都会接收到更新的内容。如果某个订阅者被注销,它就不会再收到未来的新闻更新。

六、观察者模式的优缺点
优点:
  1. 解耦:主题(被观察者)和观察者之间解耦,主题不需要知道观察者的具体实现,观察者也不需要知道主题的细节。它们仅通过接口交互。

  2. 动态响应:观察者可以在运行时动态注册和注销,可以灵活地增加或减少订阅者。

  3. 一对多依赖关系:主题对象一旦发生变化,所有依赖的观察者都会被自动通知,并更新状态。

缺点:
  1. 通知开销:如果观察者很多,主题在通知时可能会产生较大的性能开销。

  2. 不完全控制:由于观察者可以随时注册或注销,可能会存在一些不必要的更新和通知,尤其是当观察者很少或者在不需要的情况下仍被通知时。

  3. 循环依赖:如果观察者中有回调导致主题状态变化,可能会产生循环依赖,导致死循环。

七、总结

观察者模式是一种非常有用的设计模式,适用于实现一对多依赖的场景,尤其在事件驱动系统中应用广泛。在C#中,观察者模式能够简洁而灵活地实现对象间的通信,解耦了主题和观察者之间的关系,具有良好的扩展性和可维护性。

相关推荐
邪修king13 小时前
Re:Linux 系统篇(三十五):进程间通信Chapter2:从匿名管道到进程池:Linux 进程间通信实战(深度理解「先描述,再组织」)
java·linux·运维
2301_7779983413 小时前
ARP协议进阶:ARP缓存、RARP与ARP欺骗原理解析
linux·服务器·网络·网络协议·缓存
拆房老料13 小时前
【第四部分:Agentic 智能体设计模式】16. 一个 Prompt 搞不定复杂任务?试试 Prompt Chaining
java·前端·人工智能·设计模式·prompt
运维行者_14 小时前
应用性能监控怎么选?从调用链到用户体验的五个能力维度
运维·服务器·数据库·人工智能·mysql·性能优化·应用性能监控
张张123y14 小时前
后端第十四课:Agent 记忆系统(让 Agent 记住上下文和用户,跨会话不健忘)
java·大数据·人工智能·python·agent·记忆
曹牧19 小时前
C#:空合并运算符
java·前端·数据库
布吉岛的石头19 小时前
Java 程序员第 50 阶段2:预训练的目标函数:自回归语言建模本质
java·开发语言·人工智能·深度学习·回归·transformer
golang学习记19 小时前
IDEA 2026年的玩法已经完全不同
java·ide·intellij-idea
小羊没烦恼!19 小时前
铁道部新客票系统设计(二)
开发语言·前端·数据库·python·oracle·c#·php