观察者模式
定义对象一对多的依赖关系,当一个对象状态改变时,其它依赖它的对象状态也发生改变。
适用场景:
- 一个抽象模型有两个方面,一个方面依赖另一个方面。将二者封装在两个独立的对象中,使它们能够独立使用和复用
- 当一个对象状态改变,需要改变其它对象,可以是多个
- 当一个对象需要通知其它对象,而不确定其它对象是谁,有多少个
例子
- 银行取钱,账户余额发生改变,然后短信通知、邮件通知、app通知……
- 热水器烧水,水开了,警示灯亮、提示音响、温度显示……
代码实现
以银行取钱为例
/// <summary>
/// 观察者接口
/// </summary>
public interface IObserverAccount
{
void Update(string text);
}
账户对象
/// <summary>
/// 观察对象
/// </summary>
public class SubjcetAccount
{
private List<IObserverAccount> observers = new List<IObserverAccount>();
public string Name => "公子小白";
private double _money = 10000.00;
/// <summary>
/// 剩余金额
/// </summary>
public double Money
{
get => _money;
set => value = this._money;
}
/// <summary>
/// 取出金额
/// </summary>
public double DelMoney { get; set; }
/// <summary>
/// 通知
/// </summary>
public void SendMsg(string text)
{
if (observers != null)
{
foreach (var item in observers)
{
item.Update(text);
}
}
}
/// <summary>
/// 取钱
/// </summary>
/// <param name="money"></param>
public bool Withdraw(double money)
{
if (Money < money)
{
return false;
}
_money -= money;
return true;
}
/// <summary>
/// 订阅观察者
/// </summary>
/// <param name="account"></param>
public void AddOberver(IObserverAccount account)
{
if (!observers.Exists(c => c.Equals(account)))
{
observers.Add(account);
}
}
/// <summary>
/// 移除观察者
/// </summary>
/// <param name="account"></param>
public void RemoveOberver(IObserverAccount account)
{
if (observers.Exists(c => c.Equals(account)))
{
observers.Remove(account);
}
}
}
邮件通知
/// <summary>
/// 邮件通知-观察者
/// </summary>
public class Email : IObserverAccount
{
public void Update(string text) => Console.WriteLine($"邮件通知:{text}");
}
短信通知
/// <summary>
/// 手机通知-观察者
/// </summary>
public class Mobile : IObserverAccount
{
public void Update(string text) => Console.WriteLine($"短信通知:{text}");
}
客户端调用
var account = new SubjcetAccount();
account.AddOberver(new Email());
account.AddOberver(new Mobile());
account.DelMoney = 1000.00;
account.Withdraw(account.DelMoney);
account.SendMsg($"{account.Name},你的账户支出{account.DelMony},余额为{account.Money}");
调用结果如下:
到这里一个观察者模式基本实现,其思想就是==发布-订阅==模式,观察对象是个发布者,观察者就是订阅者,它自行决定是否订阅发布者的通知。C#中有着强大丰富的事件和委托,那么接下来使用委托、事件来实现观察者模式(发布-订阅)。
观察者模式-委托事件版
定义一个委托
public delegate void NotifyEventHandler(object sender,string text);
定义一个账户(发布者),定义一个委托事件
public class SubjectAccount
{
public event NotifyEventHandler NotifyEvent;
public string Name => "公子小白";
private double _money = 10000.00;
/// <summary>
/// 剩余金额
/// </summary>
public double Money
{
get => _money;
set => value = this._money;
}
/// <summary>
/// 取出金额
/// </summary>
public double DelMoney { get; set; }
/// <summary>
/// 取钱
/// </summary>
/// <param name="money"></param>
public bool Withdraw(double money)
{
if (Money < money)
{
return false;
}
_money -= money;
return true;
}
public void OnNotifyChange()
{
if (NotifyEvent != null)
{
NotifyEvent(this,$"{Name},取出金额 {DelMoney},账户余额 {Money}");
}
}
}
定义邮件通知(订阅者)
/// <summary>
/// 邮件通知-观察者
/// </summary>
public class EmailSubject
{
public void Update(object obj,string text)
{
if (obj is SubjectAccount)
{
Console.WriteLine($"邮件通知:{text}");
}
}
}
定义短信通知(订阅者)
/// <summary>
/// 手机通知-观察者
/// </summary>
public class MobileSubject
{
public void Update(object obj,string text)
{
if (obj is SubjectAccount)
{
Console.WriteLine($"短信通知:{text}");
}
}
}
客户端调用
var account = new SubjectAccount();
var email = new EmailSubject();
var mobile = new MobileSubject();
// 这里是不是很熟悉,就是winform开发中的事件添加方式
account.NotifyEvent += mobile.Update;
account.NotifyEvent += email.Update;
account.DelMoney = 1000;
account.Withdraw(account.DelMoney);
account.OnNotifyChange();
调用结果如下:
本文使用 mdnice 排版