观察者模式(事件)
本课属于 Coddy C# 学习旅程的“面向对象编程”单元。第 54 课,共 70 课。
观察者模式建立了一对多关系:一个对象(主题)在状态发生变化时,会通知多个依赖对象(观察者)。在 C# 中,事件提供了一种内置方式,可以优雅地实现这一模式。
这种模式涉及一个发布者,它会引发事件,以及对事件作出响应的订阅者。下面是一个股票价格监控器的实际示例:
public class Stock
{
private decimal _price;
public event Action<decimal> PriceChanged;
public decimal Price
{
get => _price;
set
{
_price = value;
PriceChanged?.Invoke(_price); // 通知所有观察者
}
}
}
public class PriceAlert
{
public void OnPriceChanged(decimal newPrice)
{
Console.WriteLine($"Alert: Price is now {newPrice}");
}
}
public class PriceLogger
{
public void OnPriceChanged(decimal newPrice)
{
Console.WriteLine($"Logged: {newPrice}");
}
}订阅者使用 += 将自己的方法附加到事件上,也可以使用 -= 将其移除:
var stock = new Stock();
var alert = new PriceAlert();
var logger = new PriceLogger();
stock.PriceChanged += alert.OnPriceChanged;
stock.PriceChanged += logger.OnPriceChanged;
stock.Price = 150.50m;
// 输出:
// 提醒:价格现在是 150.50
// 已记录:150.50
stock.PriceChanged -= logger.OnPriceChanged; // 取消订阅
stock.Price = 155.00m;
// 输出:提醒:价格现在是 155.00观察者模式有助于实现松耦合。Stock 类不需要了解任何关于观察者的信息。新的观察者可以订阅,而无需修改发布者,这让系统更易于扩展和维护。
挑战
简单让我们使用事件和观察者模式来构建一个温度监控系统。你将创建一个传感器,每当温度发生变化时,它都会通知多个观察者。这非常适合气候控制系统等场景,因为其中的不同组件都需要响应温度更新。
你将把代码分成三个文件:
TemperatureSensor.cs:在Monitoring命名空间中创建一个TemperatureSensor类。这是你的发布者,也就是其他对象要观察的主体。传感器应包含:- 一个用于存储当前温度的私有字段
- 一个名为
TemperatureChanged的事件,它使用Action<double>通知订阅者新的温度 - 一个
Temperature属性,每当温度被设为新值时都会触发该事件
Observers.cs:在同一命名空间中创建两个观察者类,用于订阅温度变化:DisplayUnit:包含一个OnTemperatureChanged(double temp)方法,用于输出Display: {temp}CAlarmSystem:包含一个OnTemperatureChanged(double temp)方法,用于输出Alarm Check: {temp}C
Program.cs:创建一个传感器和两个观察者,将它们连接起来。让两个观察者都订阅传感器的事件,然后更新温度。之后,取消订阅报警系统,再次更新温度,以显示只有显示单元会收到通知。
你将收到两个输入:
- 第一个温度读数(例如,
22.5) - 第二个温度读数(例如,
28.0)
你的程序应当:
- 创建传感器和两个观察者
- 让两个观察者都订阅
TemperatureChanged事件 - 将传感器的温度设为第一个输入值(两个观察者都应收到通知)
- 取消
AlarmSystem对事件的订阅 - 将传感器的温度设为第二个输入值(只有显示单元应收到通知)
例如,如果输入为 22.5 和 28.0,输出应为:
Display: 22.5C
Alarm Check: 22.5C
Display: 28C这展示了观察者模式的核心优势:传感器不知道也不关心谁在监听它。观察者可以动态订阅和取消订阅,而发布者只需通知当前正在监听的对象!
亲手试试
using System;
using Monitoring;
class Program
{
public static void Main(string[] args)
{
// 读取输入的温度
double temp1 = Convert.ToDouble(Console.ReadLine());
double temp2 = Convert.ToDouble(Console.ReadLine());
// TODO: 创建一个 TemperatureSensor 实例
// TODO: 创建 DisplayUnit 和 AlarmSystem 实例
// TODO: 让两个观察者都订阅 TemperatureChanged 事件
// TODO: 将传感器的温度设置为 temp1(两个观察者都会收到通知)
// TODO: 让 AlarmSystem 取消订阅该事件
// TODO: 将传感器的温度设置为 temp2(只有显示单元会收到通知)
}
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: C# 在线编译器