观察者模式:轻松实现消息订阅

文章摘要

观察者模式是一种松耦合设计模式,通过订阅/通知机制实现对象间通信。典型应用如报社-读者场景:报社(被观察者)出版新报纸时自动通知所有订阅者(观察者),读者可随时订阅或退订。C#提供了多种实现方式:1)基础接口实现(ISubject/IObserver);2)委托事件机制;3)标准EventHandler方式。该模式适用于需要动态通知的场景,如GUI事件、消息推送等,核心优势是降低对象间的直接依赖,提高系统可扩展性。代码示例展示了完整的订阅、通知和取消订阅流程,以及如何传递复杂事件参数。

一、观察者模式(Observer)生动形象解释

1. 生活中的例子

报纸订阅:
你订阅了一份报纸,每当有新报纸出版时,报社就会把报纸送到你家门口。你不需要天天打电话问“今天有新报纸吗?”,只要有新内容,报社会主动通知你。
如果你不想看了,取消订阅即可,报社以后也不会再送报纸给你。

对应到代码:

  • 报社:被观察者(Subject)
  • 你:观察者(Observer)
  • 订阅/退订:注册/移除观察者
  • 出版新报纸:状态变化,通知所有观察者
2. 适用场景
  • 一个对象状态变化,需要通知其他对象,并且不希望这些对象紧密耦合。
  • 典型如:事件系统、GUI事件、消息推送、数据绑定等。

二、C#代码实现

1. 定义观察者接口
public interface IObserver
{
    void Update(string message);
}
2. 定义被观察者接口
public interface ISubject
{
    void Attach(IObserver observer);    // 添加观察者
    void Detach(IObserver observer);    // 移除观察者
    void Notify(string message);        // 通知所有观察者
}
3. 实现具体的被观察者(报社)
using System.Collections.Generic;

public class Newspaper : ISubject
{
    private List<IObserver> observers = new List<IObserver>();

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

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

    public void Notify(string message)
    {
        foreach (var observer in observers)
        {
            observer.Update(message);
        }
    }

    // 新报纸出版
    public void Publish(string content)
    {
        Console.WriteLine("报社:新报纸出版啦!");
        Notify(content);
    }
}
4. 实现具体的观察者(订阅者)
public class Reader : IObserver
{
    public string Name { get; set; }

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

    public void Update(string message)
    {
        Console.WriteLine($"{Name} 收到报纸:{message}");
    }
}
5. 使用示例
class Program
{
    static void Main(string[] args)
    {
        Newspaper newspaper = new Newspaper();

        Reader alice = new Reader("Alice");
        Reader bob = new Reader("Bob");

        // 订阅
        newspaper.Attach(alice);
        newspaper.Attach(bob);

        // 发布新报纸
        newspaper.Publish("2024年6月新闻:观察者模式真有趣!");

        // Bob 退订
        newspaper.Detach(bob);

        // 再次发布
        newspaper.Publish("2024年7月新闻:C#代码实现观察者模式!");
    }
}
6. 输出结果
报社:新报纸出版啦!
Alice 收到报纸:2024年6月新闻:观察者模式真有趣!
Bob 收到报纸:2024年6月新闻:观察者模式真有趣!
报社:新报纸出版啦!
Alice 收到报纸:2024年7月新闻:C#代码实现观察者模式!

总结口诀

观察者模式像订报,
报社有新就通知,
订阅退订都灵活,
低耦合、扩展易!

下面我将分别介绍更复杂的事件参数委托实现和**.NET内置事件实现**,让你对观察者模式在C#中的实际应用有更深入的理解。


一、复杂事件参数

有时候,通知不仅仅是一个字符串,可能需要传递更多信息。可以自定义事件参数类(通常继承自EventArgs)。

public class NewspaperEventArgs : EventArgs
{
    public string Title { get; set; }
    public string Content { get; set; }
    public DateTime PublishDate { get; set; }

    public NewspaperEventArgs(string title, string content, DateTime date)
    {
        Title = title;
        Content = content;
        PublishDate = date;
    }
}

二、委托实现

C#中,委托(Delegate)是实现观察者模式的常用方式。你可以定义一个委托类型,然后让观察者注册回调。

// 定义委托
public delegate void NewspaperPublishedHandler(object sender, NewspaperEventArgs e);

public class Newspaper
{
    // 声明事件
    public event NewspaperPublishedHandler NewspaperPublished;

    public void Publish(string title, string content)
    {
        Console.WriteLine("报社:新报纸出版啦!");
        // 触发事件
        NewspaperPublished?.Invoke(this, new NewspaperEventArgs(title, content, DateTime.Now));
    }
}

观察者(订阅者)只需注册事件:

public class Reader
{
    public string Name { get; set; }
    public Reader(string name) { Name = name; }

    public void OnNewspaperPublished(object sender, NewspaperEventArgs e)
    {
        Console.WriteLine($"{Name} 收到报纸:《{e.Title}》, 内容:{e.Content}, 日期:{e.PublishDate}");
    }
}

使用示例:

class Program
{
    static void Main(string[] args)
    {
        Newspaper newspaper = new Newspaper();
        Reader alice = new Reader("Alice");
        Reader bob = new Reader("Bob");

        // 订阅事件
        newspaper.NewspaperPublished += alice.OnNewspaperPublished;
        newspaper.NewspaperPublished += bob.OnNewspaperPublished;

        newspaper.Publish("观察者模式", "C#事件机制详解");

        // Bob 退订
        newspaper.NewspaperPublished -= bob.OnNewspaperPublished;

        newspaper.Publish("委托与事件", "深入理解C#委托与事件");
    }
}

三、.NET内置事件实现(推荐写法)

.NET推荐事件声明方式是使用EventHandler<TEventArgs>,这样更规范。

public class Newspaper
{
    // 使用泛型EventHandler
    public event EventHandler<NewspaperEventArgs> NewspaperPublished;

    public void Publish(string title, string content)
    {
        Console.WriteLine("报社:新报纸出版啦!");
        NewspaperPublished?.Invoke(this, new NewspaperEventArgs(title, content, DateTime.Now));
    }
}

订阅和取消订阅方式与上面一致:

public class Reader
{
    public string Name { get; set; }
    public Reader(string name) { Name = name; }

    public void OnNewspaperPublished(object sender, NewspaperEventArgs e)
    {
        Console.WriteLine($"{Name} 收到报纸:《{e.Title}》, 内容:{e.Content}, 日期:{e.PublishDate}");
    }
}

四、总结

  • 自定义事件参数:让通知内容更丰富。
  • 委托实现:灵活注册/注销回调,解耦合。
  • .NET内置事件:推荐用event EventHandler<TEventArgs>,更规范,易于维护。

口诀

委托事件来通知,
参数丰富用EventArgs;
订阅退订都灵活,
.NET推荐EventHandler!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你一身傲骨怎能输

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值