• 行为型模式-观察者模式的实现(C#)


    1. 定义

    Define a one-to-many dependency between objects so that when oneobject changes state, all its dependents are notified and updatedautomatically..

    — Design Patterns : Elements of Reusable Object-Oriented Software
    观察者模式(Observer Pattern),又称为发布/订阅模式,它是软件设计模式中的一种。观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

    在观察者模式中,一个目标物件(被观察者)管理所有依赖于他的观察者,并且在它本身的状态发生改变时主动发出通知,这通常通过呼叫各个观察者所提供的方法来实现。

    这种模式通常被用来实现事件处理系统。

    观察者模式有很多实现方式,从根本上说,该模式必须包含两个角色:观察者和被观察者。

    观察者和被观察者之间的互动关系不能是类之间的直接调用,那样就将观察者和被观察对象紧密耦合起来了,从而违反了面向对象设计原则。

    1.1 建模

    经典观察者模式类图如下,在这个模型中,抽象的被被观察者(主题ISubject)有注册(attach)、取消注册(detech)、通知(notify)方法。
    由于所有观察者均抽象成了IObserver,从而解除了主题与具体观察者之间的耦合。

    2.C# 观察者的实现模式

    在事件处理场景,很自然会想到利用event 关键字 和EventHandler来实现观察者模式,这是一种最简单的方法。
    同时C#也提供了一种低级抽象IObserver--IObservable(System命名空间,来自System.Runtime.dll) 来实现观察者。
    被观察者(主题)接口:

    namespace System
    {
        public interface IObservable<out T>
        {       
            IDisposable Subscribe(IObserver<T> observer);
        }
    }
    

    观察者接口:

    namespace System
    {
       
        public interface IObserver<in T>
        {
            void OnCompleted();
            void OnError(Exception error);
            void OnNext(T value);
        }
    }
    

    上述接口中,由被观察者将观察者注册到观察者列表,观察者分别有完成、失败、执行的动作。

    3. 案例

    本文将基于一个经典案例分别用event 和IObserver 实现观察者模式。
    场景描述:
    气象部门根据气象卫星获取温度信息,当温度超过某一阈值时,需要向各单位发出高温预警通知,以便其及时做好高温防护错误。

    在这个场景中,发布者是预警系统,观察者是各个单位。

    抽象的模型图如下:

    3.1 基于event 实现 观察者

    发布者(主题)的定义:

    public class Subject : IObservable<decimal>
    {
            /// <summary>
            /// 观察者处理委托
            /// </summary>
            public event EventHandler<decimal> Observers;
    
            /*
             * 高温黄色预警 >=35,<37 
             * 高温橙色预警 >=37,<40 
             * 高温红色预警 >=40    
             * **/
            public void SetTemperature(decimal temperature)
            {
    
                if (temperature >= 35)
                {
                    if (temperature >= 40)
                    {
                        _warningLevel = "红色";
                    }
                    else if (temperature >= 37)
                    {
                        _warningLevel = "橙色";
                    }
    
                    PublishWarning(temperature, _warningLevel);
                }
            }
            
            private void PublishWarning(decimal temperature, string warningLevel)
            {
                Console.WriteLine($"===========气象部门发布高温{_warningLevel} 预警,气温:{temperature} ");
                
                Observers?.Invoke(this, temperature);
            }
          
    }
    

    分别定义三个观察者

        /// <summary>
        /// 企业单位观察者
        /// </summary>
      public class EnterpriseObserver 
      {
            public void OnWarning(object sender,decimal eventArgs)
            {
                Console.WriteLine($"企业单位收到预警事件,气温:{eventArgs}");
            }
      }
    
        /// <summary>
        /// 政府部门观察者
        /// </summary>
      public class EnterpriseObserver 
      {
            public void OnWarning(object sender, decimal eventArgs)
            {
                Console.WriteLine($"政府部门收到预警事件,气温:{eventArgs}");
            }
      }
    
        /// <summary>
        /// 个人观察者
        /// </summary>
      public class EnterpriseObserver 
      {
            public void OnWarning(object sender, decimal eventArgs)
            {
                Console.WriteLine($"个人收到预警事件,气温:{eventArgs}");
            }
      }
    
    

    客户端调用,利用委托可以多播的特性增加多个观察者:

                var subject = new Subject();
    
                subject.Observers += new EnterpriseObserver().OnWarning;
                subject.Observers += new GovernmentObserver().OnWarning;
                subject.Observers += new PersonObserver().OnWarning;
                int t = 3;
                var random = new Random(500);
                while (t > 0)
                {
    
                    var temperature = random.NextDouble() * 50;
                    if (temperature > 35)
                    {
                        subject.SetTemperature((decimal)temperature);
    
                        t--;
                    }
                }
    
    • 调用结果

    3.2 基于IObserver--IObservable 实现观察者

    在上面的代码中进行改造
    发布者(主题)的定义:

       public class Unsubscriber<T> : IDisposable
        {
            private List<IObserver<T>> _observers;
            private IObserver<T> _observer;
            public Unsubscriber(List<IObserver<T>> observers,
                IObserver<T> observer)
            {
                _observers = observers;
                _observer = observer;
            }
            public void Dispose()
            {
                Console.WriteLine("Unsubscribed....");
                _observers.Remove(_observer);
            }
        }
    
        /// <summary>
        /// 发布者
        /// </summary>
        public class Subject : IObservable<decimal>
        {
            /// <summary>
            /// 观察者列表
            /// </summary>
            private List<IObserver<decimal>> observers;
    
            /// <summary>
            /// 观察者处理委托
            /// </summary>
            public event EventHandler<decimal> Observers;
    
            private decimal _temperature;
            private string _warningLevel;
    
    
            public Subject()
            {
                observers = new List<IObserver<decimal>>();
            }
    
            public IDisposable Subscribe(IObserver<decimal> observer)
            {
                if (!observers.Contains(observer))
                    observers.Add(observer);
                return new Unsubscriber<decimal>(observers, observer);
            }
    
    
            /*
             * 高温黄色预警 >=35,<37 C
             * 高温橙色预警 >=37,<40 C
             * 高温红色预警 >=40    C
             * **/
            public void SetTemperature(decimal temperature)
            {
    
                if (temperature >= 35)
                {
                    if (temperature >= 40)
                    {
                        _warningLevel = "红色";
                    }
                    else if (temperature >= 37)
                    {
                        _warningLevel = "橙色";
                    }
    
                    PublishWarning(temperature, _warningLevel);
                }
            }
    
            private void PublishWarning(decimal temperature, string warningLevel)
            {
                Console.WriteLine($"===========气象部门发布高温{_warningLevel} 预警,气温:{temperature} ");
                foreach (var observer in observers)
                {
                    observer.OnNext(temperature);
                    
                }
                Observers?.Invoke(this, temperature);
            }
        }
    

    观察者定义:

     /// <summary>
        /// 企业单位观察者
        /// </summary>
        public class EnterpriseObserver : IObserver<decimal>
        {
            public void OnCompleted()
            {
            }
    
            public void OnError(Exception error)
            {
            }
    
            public void OnNext(decimal value)
            {
                Console.WriteLine($"企业单位收到预警信息,气温:{value}");
            }
    
            public void OnWarning(object sender,decimal eventArgs)
            {
                Console.WriteLine($"企业单位收到预警事件,气温:{eventArgs}");
            }
        }
    
        /// <summary>
        /// 政府部门
        /// </summary>
        public class GovernmentObserver : IObserver<decimal>
        {
            public void OnCompleted()
            {
            }
    
            public void OnError(Exception error)
            {
            }
    
            public void OnNext(decimal value)
            {
                Console.WriteLine($"政府部门收到预警信息,气温:{value}");
            }
            public void OnWarning(object sender, decimal eventArgs)
            {
                Console.WriteLine($"政府部门收到预警事件,气温:{eventArgs}");
            }
        }
    
        /// <summary>
        /// 个人观察者
        /// </summary>
        public class PersonObserver : IObserver<decimal>
        {
            public void OnCompleted()
            {
            }
    
            public void OnError(Exception error)
            {
            }
    
            public void OnNext(decimal value)
            {
                Console.WriteLine($"个人收到预警信息,气温:{value}");
            }
    
            public void OnWarning(object sender, decimal eventArgs)
            {
                Console.WriteLine($"个人收到预警事件,气温:{eventArgs}");
            }
        }
    
    

    客户端调用:

    var subject = new Subject();
    
                var ob1 = new EnterpriseObserver();
                var ob2 = new GovernmentObserver();
                var ob3 = new PersonObserver();
    
                subject.Subscribe(ob1);
                subject.Subscribe(ob2);
                subject.Subscribe(ob3);
                int t = 3;
                var random = new Random(500);
                while (t > 0)
                {
                   
                    var temperature = random.NextDouble()*50;
                    if (temperature > 35)
                    {
                        subject.SetTemperature((decimal)temperature);
    
                        t--;
                    }
                }
    
    

    4. 小结

    • 用C#实现观察者模式时,基本不需要自己定义发布者和观察者的抽象,基础类库中已经提供了现成的。
    • 一般情况下,使用event 编程模型实现观察者模式更加精简,编码少。
    • 仅在一些对性能要求比较高的特殊场景,我们可以直接使用IObserver-IObservable接口实现观察者模式,以减少event实例化的开销,减轻GC压力。
  • 相关阅读:
    【转载】Perl异常处理方法总结
    (个人)读取A.CSV修改它的某列,写入B.CSV
    (转载)PERL 处理CSV
    (转载)CSV 文件处理 PERL
    string.split()
    反转字符串应该注意的问题
    jQuery取值相加
    ADO.NET的五个主要对象
    Response.Redirect和Server.Transfer
    string和stringBuilder的区别
  • 原文地址:https://www.cnblogs.com/aimigi/p/14416232.html
Copyright © 2020-2023  润新知