观察者模式(Observer Pattern)是一种行为型设计模式,用于定义对象间的一对多依赖关系,使得当一个对象的状态发生变化时,其所有依赖者(观察者)都会自动收到通知并更新。这种模式广泛应用于事件处理、消息订阅系统等领域。
一、观察者模式的核心概念
观察者模式涉及两个主要角色:
-
主题(Subject):也称为"被观察者",它是一个对象,它的状态发生变化时,通知所有已注册的观察者。主题通常提供注册、注销和通知观察者的功能。
-
观察者(Observer):也称为"订阅者",它是依赖于主题的对象,观察者会在主题发生变化时接收通知并做出响应。
通过观察者模式,主题与观察者之间解耦,主题无需知道观察者的具体实现,观察者只需要实现通知接口即可。
二、观察者模式的类图
观察者模式的典型类图如下:
+---------------+ +----------------+
| Subject | | Observer |
|---------------| |----------------|
| +Attach() | | +Update() |
| +Detach() |<---->| |
| +Notify() | +----------------+
+---------------+
|
+------------+
| Concrete |
| Subject |
+------------+
|
+--------------------+
| Concrete Observer |
+--------------------+
-
Subject(主题/被观察者):具有状态的对象,维护观察者列表,并提供方法用于注册、注销观察者。
-
Observer(观察者) :对主题的状态变化做出响应的接口,通常会有一个
Update()
方法。 -
ConcreteSubject(具体主题) :实现了
Subject
类,保存主题的状态,并在状态变化时通知观察者。 -
ConcreteObserver(具体观察者) :实现了
Observer
接口,处理主题状态变化时的具体行为。
三、观察者模式的应用场景
-
GUI事件处理:在图形用户界面(GUI)中,观察者模式广泛用于事件处理。例如,当按钮被点击时,系统通知所有注册的监听器来响应这个事件。
-
发布-订阅系统:例如,新闻应用中的用户订阅和接收新闻更新,或股票价格更新系统,用户可以订阅某个股票的变化。
-
日志记录系统:当应用程序发生某些变化时,系统可以通知所有注册的观察者(如日志记录器)来进行相应操作。
四、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!";
}
}
五、代码解析
-
IObserver接口 :定义了
Update
方法,供观察者类实现。当主题发生变化时,Update
方法会被调用。 -
ISubject接口 :定义了
Attach
(注册观察者)、Detach
(注销观察者)和Notify
(通知观察者)方法。Notify
方法会通知所有已注册的观察者。 -
NewsAgency(新闻机构)类 :实现了
ISubject
接口,维护了一个观察者列表(_observers
)。当新闻更新时,调用Notify
方法通知所有订阅者。 -
Subscriber(订阅者)类 :实现了
IObserver
接口,包含一个Update
方法,每当主题状态发生变化时,Update
方法就会被调用,接收最新的新闻。 -
Main 方法 :在
Main
方法中,创建了一个新闻机构并注册了几个订阅者。当新闻更新时,所有订阅者都会接收到更新的内容。如果某个订阅者被注销,它就不会再收到未来的新闻更新。
六、观察者模式的优缺点
优点:
-
解耦:主题(被观察者)和观察者之间解耦,主题不需要知道观察者的具体实现,观察者也不需要知道主题的细节。它们仅通过接口交互。
-
动态响应:观察者可以在运行时动态注册和注销,可以灵活地增加或减少订阅者。
-
一对多依赖关系:主题对象一旦发生变化,所有依赖的观察者都会被自动通知,并更新状态。
缺点:
-
通知开销:如果观察者很多,主题在通知时可能会产生较大的性能开销。
-
不完全控制:由于观察者可以随时注册或注销,可能会存在一些不必要的更新和通知,尤其是当观察者很少或者在不需要的情况下仍被通知时。
-
循环依赖:如果观察者中有回调导致主题状态变化,可能会产生循环依赖,导致死循环。
七、总结
观察者模式是一种非常有用的设计模式,适用于实现一对多依赖的场景,尤其在事件驱动系统中应用广泛。在C#中,观察者模式能够简洁而灵活地实现对象间的通信,解耦了主题和观察者之间的关系,具有良好的扩展性和可维护性。