假設咱們是一個優惠券提供平臺,故事就發生在顧客在咱們平臺採購完成支付成功後。git
支付完成後平臺要進行的一些操做:app
短信通知客戶已經生成訂單異步
增長顧客的積分async
開始按訂單需求制券ide
。。。(可能會有許多操做)3d
接下來就是將故事以代碼的形式展示出來。。。code
咱們將上述故事轉化爲代碼中的對象分別是: 支付成功 PaySuccessSubject、短信通知 MessageObserver、積分增長 BonusObserver、制券 CouponObserver。server
當支付成功PaySuccessSubject後,要通知到MessageObserver,BonusObserver,CouponObserver這三個對象,爲了實現上面這個需求,將採用觀察者模式(發佈-訂閱)對象
觀察者模式又叫發佈-訂閱(Publish/Subscribe)模式,定義了一種一對多的依賴關係,讓多個觀察者對象同時 監聽某一個主題對象。當主題對象在狀態發生變化時,通知全部觀察者對象,使他們可以本身更新本身。blog
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 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