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#中,观察者模式能够简洁而灵活地实现对象间的通信,解耦了主题和观察者之间的关系,具有良好的扩展性和可维护性。

相关推荐
HackTwoHub2 小时前
AI大模型网关存在SQL注入、附 POC 复现、影响版本LiteLLM 1.81.16~1.83.7(CVE-2026-42208)
数据库·人工智能·sql·网络安全·系统安全·网络攻击模型·安全架构
wuminyu2 小时前
专家视角看Java字节码加载与存储指令机制
java·linux·c语言·jvm·c++
l1t2 小时前
DeepSeek总结的DuckLake构建基于 SQL 原生表格式的下一代数据湖仓
数据库·sql
KmSH8umpK2 小时前
Redis分布式锁从原生手写到Redisson高阶落地,附线上死锁复盘优化方案进阶第八篇
数据库·redis·分布式
网络工程小王3 小时前
【LangChain 大模型6大调用指南】调用大模型篇
linux·运维·服务器·人工智能·学习
TDengine (老段)3 小时前
从施工监测到运营预警,桥科院用 TDengine 提升桥梁数据管理能力
大数据·数据库·物联网·时序数据库·tdengine·涛思数据
wangbing11253 小时前
各linux版本的包管理命令
linux·运维·服务器
callJJ4 小时前
Spring Data Redis 两种编程模型详解:同步 vs 响应式
java·spring boot·redis·python·spring
S1998_1997111609•X4 小时前
论mysql国盾shell-sfa犯罪行为集团下的分项工程及反向注入原理尐深度纳米算法下的鐌檵鄐鉎行为
网络·数据库·网络协议·百度·开闭原则
比昨天多敲两行4 小时前
Linux基础开发工具(下)
linux·运维·服务器