• 设计模式-观察者模式(Observer)


    讲故事(user story)

    假设我们是一个优惠券提供平台,故事就发生在顾客在我们平台采购完成支付成功后。

    支付完成后平台要进行的一些操作:

    1. 短信通知客户已经生成订单

    2. 增加顾客的积分

    3. 开始按订单需求制券

    ​ 。。。(可能会有许多操作)

    接下来就是将故事以代码的形式展现出来。。。

    需求分析

    我们将上述故事转化为代码中的对象分别是: 支付成功 PaySuccessSubject、短信通知 MessageObserver、积分增加 BonusObserver、制券 CouponObserver

    当支付成功PaySuccessSubject后,要通知到MessageObserverBonusObserverCouponObserver这三个对象,为了实现上面这个需求,将采用观察者模式(发布-订阅)

    敲黑板.划重点

    观察者模式又叫发布-订阅(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();
     }
    

    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("
    
    Happy 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("
    
    Happy 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("
    
    Happy Ending~");
                Console.ReadLine();
            }
        }
    }
    

    展示结果和上面一样。

    源码地址:https://gitee.com/sayook/DesignMode/tree/master/Observer

  • 相关阅读:
    C语言开发CGI程序的简单例子
    js收集错误信息,错误上报
    php安装pear、pecl
    (转)进程与线程的一个简单解释
    php curl 中的gzip压缩性能测试
    (转载):() { :|:& }; : # <-- 打开终端,输入这个,回车.你看到了什么??
    (转)open和fopen的区别:
    C语言中typedef
    secureCRT使用VIM 像LINUX中那样对语法高亮
    iframe与主框架跨域相互访问方法
  • 原文地址:https://www.cnblogs.com/sayook/p/12759319.html
Copyright © 2020-2023  润新知