zoukankan      html  css  js  c++  java
  • 设计模式-观察者模式(Observer)

    简介:

    观察者模式,也称为订阅-发布模式,定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖他的对象都得到通知并被自动更新。

    主要由以下几个部分组成:

    a.Subject目标对象。它具有以下特征:

        一个目标可以被多个观察者订阅

        提供订阅和取消订阅的方法

        当目标对象状态发生变化时,通知所有订阅者。

    把Subject独立出来是因为他提供了观察者模式中常见的三个特征,每个观察者模式都是这样,可以抽象出来。具体的单独逻辑可放在ConcreteSubject具体的目标实现对象中。

    b.Observer定义观察者的接口。提供方法(一般为接口),当目标对象发生变化通知过来,做对应的响应操作。可以在该方法中调用Subject目标对象,以获取目标对象的数据。

    c.ConcreteSubject具体的目标实现对象。可以维护具体对象的一些个性化属性,如目标状态等。

    d.ConcreteObserver观察者的具体实现对象。提供处理目标对象变化的具体响应操作。

    具体实例:

    报社发行报纸,广大读者可以订报,也可以取消订报。报社维护订阅者的名单,一旦有新的报纸印刷出来,及时发布给广大读者。

    Subject目标对象:提供观察者模式的基本功能

    上面的例子中,广大读者都在观察同一个报社对象,这个报社对象就是被观察的目标。一般建议在目标接口名称后面加上Subject

    /// <summary>
     /// Subject目标对象
     /// </summary>
     public class NewsPaperSubject
     {
         /// <summary>
         /// 维护有效订阅者的列表。
         /// </summary>
         protected List<ReaderObserver> observerList = new List<ReaderObserver>();
    
         /// <summary>
         /// 提供公共接口,供注册订阅者
         /// </summary>
         /// <param name="aObserver"></param>
         public void Attach(ReaderObserver aObserver)
         {
             observerList.Add(aObserver);
         }
    
         /// <summary>
         /// 提供公共接口,供取消注册订阅者
         /// </summary>
         /// <param name="aObserver"></param>
         public void Detach(ReaderObserver aObserver)
         {
             observerList.Remove(aObserver);
         }
    
         /// <summary>
         /// 目标对象发生变化时,通知所有订阅的观察者
         /// </summary>
         protected void NotifyAllObservers()
         {
             foreach (ReaderObserver observer in observerList)
             {
                 observer.update(this);
             }
         }
     }

    上面的例子中,仅实现了报社的基本功能,并没有定义报社出版报纸等功能,是为了这个更通用。具体的逻辑放在ConcreteSubject中。

    ConcreteSubject具体的目标实现对象:真正的模板对象,日报

    日报,在原来基类上新增属性:报纸的出版日期。修改了出版日期,相当于重新出版了新报纸,此时需要通知所有读者。

     
    /// <summary>
    /// ConcreteSubject具体的目标实现对象 -日报
    /// </summary>
    public class DaliyNewsPaper : NewsPaperSubject
    {
        public string Date
        {
            get { return _date; }
            set
            {
                _date = value;
                //改了出版日期,相当于重新出版了新报纸,此时需要通知所有读者。
                NotifyAllObservers();
            }
        }
        private string _date;
    }
    Observer定义观察者的接口:观察者的接口或者抽象类。
    一般建议在观察者接口后面跟Observer
    接口更新方法,建议命名以Update。
    观察者订阅好报纸以后,在家里等着收报纸即可,没有其他的功能需要实现。这里先抽象出观察者接口,然后再后续类中具体实现。
    /// <summary>
    /// Observer定义观察者的接口
    /// </summary>
    public abstract class ReaderObserver
    {
        /// <summary>
        /// 目标对象发生变化通知过来,响应操作接口方法。
        /// <param name="aSubject">推送过来的目标对象</param>
        /// </summary>
        public abstract void update(NewsPaperSubject aSubject);
    }
    ConcreteSubject具体的目标实现对象:真正的观察者,读者
    /// <summary>
    /// ConcreteObserver观察者的具体实现对象 -读者
    /// </summary>
    public class DailyNewsPaperReader : ReaderObserver
    {
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
        private string _name;
    
        public override void update(NewsPaperSubject aSubject)
        {
            Console.WriteLine("{0}已经收到{1}的报纸", _name, (aSubject as DaliyNewsPaper).Date);
        }
    }
    测试代码如下:
    public class ObserverTest
    {
        public static void Main(string[] args)
        {
            //创建目标对象:报纸
            DaliyNewsPaper newsPaper = new DaliyNewsPaper();
    
            //创建观察者对象:张三
            DailyNewsPaperReader observer1 = new DailyNewsPaperReader();
            observer1.Name = "张三";
            //张三订报纸
            newsPaper.Attach(observer1);
    
            //创建观察者对象:李四
            DailyNewsPaperReader observer2 = new DailyNewsPaperReader();
            observer2.Name = "李四";
            //李四订报纸
            newsPaper.Attach(observer2);
    
            //2013-10-24报纸出版,张三、李四收到报纸
            Console.WriteLine("2013-10-24报纸出版了!!!");
            newsPaper.Date = "2013-10-24";
            Console.WriteLine();
    
            //李四取消订报
            newsPaper.Detach(observer2);
    
            //2013-10-25报纸出版,张三收到报纸
            Console.WriteLine("2013-10-25报纸出版了!!!");
            newsPaper.Date = "2013-10-25";
    
            Console.ReadLine();
        }
    }
    补充分析:
    1.观察者模式一般都是目标对象跟观察者一对多的关系,即一个目标有多个观察者。
    对于观察者需要观察多个目标的情况下,可以修改观察者的UPDATE方法,根据传入参数决定是哪个目标的变化,或者干脆定义不同名字的UPDATE方法来对应不同的目标调用。
    2.观察者模式的变例:区别对待观察者
    比如说:某公司的请假制度为,请假半天以下的,通知下项目经理和项目组内成员即可,请假3天以下(含3天)的,还需要通知部门经理,请假3天以上的,还需要通知总经理。
    实例中,请假对象是一个目标对象,项目经理、项目组成员、部门经理、总经理都是观察者。但是不同的是,并不是每次请假都需要通知上述所有人,需要根据请假的天数来决定通知的范围。
    如果使用观察者模式?怎么处理?
    参考代码如下:
    protected void NotifyObservers()
           {
               foreach (ReaderObserver observer in observerList) 
               {
                   //不管请多少天,都需要通知项目经理和项目组成员
                   observer.getJob().equals("项目经理").Update(this);
                   observer.getJob().equals("项目组成员").Update(this);
    
                   if (this.Day > 0.5)
                       observer.getJob().equals("部门经理").Update(this);
    
                   if(this.Death >3)
                       observer.getJob().equals("总经理").Update(this);
               }
           }
    此时具体目标对象需要重写抽象目标对象的通知方法,在方法内部根据目标的属性来决定被通知的观察者。
     
     
  • 相关阅读:
    LeetCode
    (六)6.5 Neurons Networks Implements of Sparse Autoencoder
    (六)6.4 Neurons Networks Autoencoders and Sparsity
    (六) 6.3 Neurons Networks Gradient Checking
    opencv::调整图像亮度与对比度
    opencv::两张图片的线性融合
    opencv::源码编译
    日志::spdlog
    std::is_same
    json::rapidjson工具
  • 原文地址:https://www.cnblogs.com/liaozh/p/3386874.html
Copyright © 2011-2022 走看看