設計模式-觀察者模式(Observer)

講故事(user story)

假設咱們是一個優惠券提供平臺,故事就發生在顧客在咱們平臺採購完成支付成功後。git

支付完成後平臺要進行的一些操做:app

  1. 短信通知客戶已經生成訂單異步

  2. 增長顧客的積分async

  3. 開始按訂單需求制券ide

​ 。。。(可能會有許多操做)3d

接下來就是將故事以代碼的形式展示出來。。。code

需求分析

咱們將上述故事轉化爲代碼中的對象分別是: 支付成功 PaySuccessSubject、短信通知 MessageObserver、積分增長 BonusObserver、制券 CouponObserverserver

當支付成功PaySuccessSubject後,要通知到MessageObserverBonusObserverCouponObserver這三個對象,爲了實現上面這個需求,將採用觀察者模式(發佈-訂閱)對象

敲黑板.劃重點

觀察者模式又叫發佈-訂閱(Publish/Subscribe)模式,定義了一種一對多的依賴關係,讓多個觀察者對象同時 監聽某一個主題對象。當主題對象在狀態發生變化時,通知全部觀察者對象,使他們可以本身更新本身。blog

觀察者模式結構圖

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();
 }

MessageObserverBonusObserverCouponObserver具體的觀察者類,實現更新接口,以便自己的狀態與主題的狀態相協調。

/// <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();
        }

結果顯示
result2

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();
        }

結果顯示:

result

委託加持觀察者模式

現實開發中,不少觀察者對象共同繼承或者實現同一個抽象觀察者,不合適;而且全部觀察者對象的操做方法統一叫一個 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

相關文章
相關標籤/搜索