设计模式-观察者模式(Observer)
讲故事(user story)
假设我们是一个优惠券提供平台,故事就发生在顾客在我们平台采购完成支付成功后。
支付完成后平台要进行的一些操作:
-
短信通知客户已经生成订单
-
增加顾客的积分
-
开始按订单需求制券
。。。(可能会有许多操作)
接下来就是将故事以代码的形式展现出来。。。
需求分析
我们将上述故事转化为代码中的对象分别是: 支付成功 PaySuccessSubject、短信通知 MessageObserver、积分增加 BonusObserver、制券 CouponObserver。
当支付成功PaySuccessSubject后,要通知到MessageObserver,BonusObserver,CouponObserver这三个对象,为了实现上面这个需求,将采用观察者模式(发布-订阅)
敲黑板.划重点
观察者模式又叫发布-订阅(Publish/Subscribe)模式,定义了一种一对多的依赖关系,让多个观察者对象同时 监听某一个主题对象。当主题对象在状态发生变化时,通知所有观察者对象,使他们能够自己更新自己。
Show Code
Subject类,把所有观察者对象的引用保存在一个集合了,每个通知者都可以有任何数量的观察者。抽象通知者提供 可以增加和删除观察者对象的接口。
/// <summary> /// 抽象通知者 /// </summary> public abstract class Subject { /// <summary> /// 观察者集合 /// </summary> protected List<IObserver> observers = new List<IObserver>(); public string State { get; set; } /// <summary> /// 添加观察者 /// </summary> /// <param name="observer">观察者</param> public void Attach(IObserver observer) { observers.Add(observer); } /// <summary> /// 删除观察者 /// </summary> /// <param name="observer">观察者</param> public void Detach(IObserver observer) { observers.Remove(observer); } /// <summary> /// 通知 /// </summary> /// <returns></returns> public void Notify() { foreach (var observer in observers) { observer.Update(); } } }
PaySuccessSubject类,具体的通知者,给所有登记过的观察者发出通知。
/// <summary> /// 支持成功通知者 /// </summary> public class PaySuccessSubject : Subject { }
Observer类,抽象观察者,为所有的具体的观察者定义一个接口,一般用抽象类或接口实现。通常包含一个Update()更新方法。
/// <summary> /// 抽象观察 /// </summary> public abstract class Observer { public abstract void Update(); }
MessageObserver、BonusObserver、CouponObserver具体的观察者类,实现更新接口,以便本身的状态与主题的状态相协调。
/// <summary> /// 短信观察者 /// </summary> public class MessageObserver : Observer { public Subject Subject { get; set; } public MessageObserver(Subject subject) { Subject = subject; } public override void Update() { Console.WriteLine($"{Subject.State}:短信通知了..."); } } /// <summary> /// 积分观察者 /// </summary> public class BonusObserver : Observer { public Subject Subject { get; set; } public BonusObserver(Subject subject) { Subject = subject; } public override void Update() { Console.WriteLine($"{Subject.State}:积分增加了..."); } } /// <summary> /// 券观察者 /// </summary> public class CouponObserver : Observer { public Subject Subject { get; set; } public CouponObserver(Subject subject) { Subject = subject; } public override void Update() { Console.WriteLine($"{Subject.State}:开始制券了..."); } }
客户端代码
private static void Main(string[] args) { var subject = new PaySuccessSubject(); var observer1 = new CouponObserver(subject); var observer2 = new MessageObserver(subject); var observer3 = new BonusObserver(subject); //添加订阅 subject.Attach(observer1); subject.Attach(observer2); subject.Attach(observer3); //发布通知 subject.State = "星巴克10十元券采购成功"; subject.Notify(); Console.WriteLine("\n\nHappy Ending~"); Console.ReadLine(); }
结果显示
Code Upgrade
code review后发现,在通知给观察者时,是顺序执行,如果其中一个观察者卡顿或者错误,会导致其他观察者卡克,所以我们应该采用异步方式。
下面我们将模拟 制券过程耗时增加,但不影响通知其他观察者。直接上代码:
/// <summary>
/// 抽象观察
/// </summary>
public abstract class Observer
{
public abstract Task UpdateAsync();
}
/// <summary>
/// 短信观察者
/// </summary>
public class MessageObserver : Observer
{
public Subject Subject { get; set; }
public MessageObserver(Subject subject)
{
Subject = subject;
}
public override Task UpdateAsync()
{
Console.WriteLine($"{Subject.State}:短信通知了...");
return Task.CompletedTask;
}
}
/// <summary>
/// 积分观察者
/// </summary>
public class BonusObserver : Observer
{
public Subject Subject { get; set; }
public BonusObserver(Subject subject)
{
Subject = subject;
}
public override Task UpdateAsync()
{
Console.WriteLine($"{Subject.State}:积分增加了...");
return Task.CompletedTask;
}
}
/// <summary>
/// 券观察者
/// </summary>
public class CouponObserver : Observer
{
public Subject Subject { get; set; }
public CouponObserver(Subject subject)
{
Subject = subject;
}
public override async Task UpdateAsync()
{
Console.WriteLine($"{Subject.State}:开始制券...");
//模拟制券耗时
await Task.Delay(3000);
Console.WriteLine($"{Subject.State}:制券完成...");
}
}
/// <summary>
/// 抽象通知者
/// </summary>
public abstract class Subject
{
/// <summary>
/// 观察者集合
/// </summary>
protected List<Observer> observers = new List<Observer>();
public string State { get; set; }
/// <summary>
/// 添加观察者
/// </summary>
/// <param name="observer">观察者</param>
public void Attach(Observer observer)
{
observers.Add(observer);
}
/// <summary>
/// 删除观察者
/// </summary>
/// <param name="observer">观察者</param>
public void Detach(Observer observer)
{
observers.Remove(observer);
}
/// <summary>
/// 通知
/// </summary>
/// <returns></returns>
public Task Notify()
{
foreach (var observer in observers)
{
observer.UpdateAsync();
}
return Task.CompletedTask;
}
}
客户端端代码:
private static async Task Main(string[] args)
{
var subject = new PaySuccessSubject();
var observer1 = new CouponObserver(subject);
var observer2 = new MessageObserver(subject);
var observer3 = new BonusObserver(subject);
//添加订阅
subject.Attach(observer1);
subject.Attach(observer2);
subject.Attach(observer3);
//发布通知
subject.State = "星巴克10十元券采购成功";
await subject.Notify();
Console.WriteLine("\n\nHappy Ending~");
Console.ReadLine();
}
结果显示:
委托加持观察者模式
现实开发中,很多观察者对象共同继承或者实现同一个抽象观察者,不合适;并且所有观察者对象的操作方法统一叫一个 Update(),达不到望文生义的效果,所以我们对观察者模式再次进行升级,使用委托来替换掉抽象观察者,
直接上代码:
/// <summary>
/// 短信观察者
/// </summary>
public class MessageObserver
{
public ISubject Subject { get; set; }
public MessageObserver(ISubject subject)
{
Subject = subject;
}
/// <summary>
/// 发送短信
/// </summary>
/// <returns></returns>
public Task SendMessageAsync()
{
Console.WriteLine($"{Subject.State}:短信通知了...");
return Task.CompletedTask;
}
}
/// <summary>
/// 积分观察者
/// </summary>
public class BonusObserver
{
public ISubject Subject { get; set; }
public BonusObserver(ISubject subject)
{
Subject = subject;
}
/// <summary>
/// 添加积分
/// </summary>
/// <returns></returns>
public Task AddBonusAsync()
{
Console.WriteLine($"{Subject.State}:积分增加了...");
return Task.CompletedTask;
}
}
/// <summary>
/// 券观察者
/// </summary>
public class CouponObserver
{
public ISubject Subject { get; set; }
public CouponObserver(ISubject subject)
{
Subject = subject;
}
/// <summary>
/// 制券
/// </summary>
/// <returns></returns>
public async Task MakeCouponAsync()
{
Console.WriteLine($"{Subject.State}:开始制券...");
//模拟制券耗时
await Task.Delay(3000);
Console.WriteLine($"{Subject.State}:制券完成...");
}
}
/// <summary>
/// 抽象通知者
/// </summary>
public interface ISubject
{
/// <summary>
/// 通知
/// </summary>
/// <returns></returns>
public Task Notify();
public string State { get; set; }
}
/// <summary>
/// 支持成功通知者
/// </summary>
public class PaySuccessSubject : ISubject
{
public Func<Task> Update;
public string State { get; set; }
public Task Notify()
{
Update();
return Task.CompletedTask;
}
}
}
客户端调用:
internal class Program
{
private static async Task Main(string[] args)
{
var subject = new ObserverDelegate.PaySuccessSubject();
var observer1 = new ObserverDelegate.CouponObserver(subject);
var observer2 = new ObserverDelegate.MessageObserver(subject);
var observer3 = new ObserverDelegate.BonusObserver(subject);
//添加订阅
subject.Update += observer1.MakeCouponAsync;
subject.Update += observer2.SendMessageAsync;
subject.Update += observer3.AddBonusAsync;
//发布通知
subject.State = "星巴克10十元券采购成功";
await subject.Notify();
Console.WriteLine("\n\nHappy Ending~");
Console.ReadLine();
}
}
}
展示结果和上面一样。
源码地址:https://gitee.com/sayook/DesignMode/tree/master/Observer