设计模式之观察者模式

一、概论

什么是观察者模式呢?其实也叫订阅者模式,如果多个用户订阅了这个主题,在这个主题发生改变的时候,每一个用户都能够接受到这个主题推送的通知,观察者模式主要分两种角色,一种是Subject(主题类),另一种是Observer(监视类),主题类为监听类提供了两个方法,一个是Register ,Observer通过这个方法来申明监听或者说订阅了这个主题,一个个方法是UnRegister  ,是用来移除监听对象,但Subject 类发生改变的时候,调用Notify方法,让后每个订阅者都能够收到通知,就像读者订阅了报纸,当报纸出新的版本事,就可以通知读者这个报纸出新版本了,各位读者就是订阅者,报纸就是主题类。

二、定义接口

根据.net的编码规范,我们将监视类的接口定义为IObserver,主题类的接口定义为IObservable,表示的是被监听的意思,

IObserver中定义了Update方法

   /// <summary>
    /// 定义观察者接口
    /// </summary>
    public interface IObserver
    {
        void Update();
    }

IObservable中定义了两个方法 Register 和UnRegister

    /// <summary>
    /// 定义被观察者接口
    /// </summary>
    public interface IObservable
    {
        /// <summary>
        /// 注册
        /// </summary>
        /// <param name="observer"></param>
        void Register(IObserver observer);

        /// <summary>
        /// 取消注册
        /// </summary>
        /// <param name="observer"></param>
        void UnRegister(IObserver observer);
       
    }

 

三、观察者模式实现

现在定义一个报纸的类,它继承与接口IObserver

     /// <summary>
    /// 被观察具体实现类(报纸)
    /// </summary>
    public class NewsPaper : IObservable
    {
        List<IObserver> objs = new List<IObserver>();
        
        public void Register(IObserver observer)
        {
            objs.Add(observer);
        }

        public void UnRegister(IObserver observer)
        {
            objs.Remove(observer);
        }

        public void Notify()
        {
            if (objs!=null&objs.Count>0)
            {
                foreach (var obj in objs)
                {
                    obj.Update();
                }
            }
            
        }
    }

在这个类中,我们定义了一个裝取监视着的一个容器,Register的方法就是将注册的监视着放进这个容器,UnRegister就是移除监视者,在调用Notify()方法时遍历容器中的所有的监视者,通知所有的监视者。

 

下面我们定义了连个读者的类

   public class Wang : IObserver
    {
        public void Update()
        {
            Console.WriteLine("你好Wang,你订阅的报纸已经发版");
        }
    }
    public class Li : IObserver
    {
        public void Update()
        {
            Console.WriteLine("你好Li,你订阅的报纸已经发版");
        }
    }

 

接下来我们运行下面的程序

    class Program
    {
        static void Main(string[] args)
        {
            Wang p1 = new Wang();
            Li p2 = new Li();
            NewsPaper log = new NewsPaper();
            log.Register(p1);
            log.Register(p2);
            log.Notify();
            log.UnRegister(p2);
            log.Notify();
            Console.ReadKey();
        }
    }

运行结果如下:

 

 这样我们就简单的实现了观察者模式

四、推模式

推模式是什么呢,推模式就是把订阅者需要的信息推送给订阅者,如何推?就是将需要的信息封装到一个类中通过Notify方法推送给Observer

我们先定义一个推送的类NewsEventArgs ,这个类是用来储存报纸的价格的。

    public class NewsEventArgs
    {
        private int price;

        public int Price
        {
            get { return price; }
        }
        public NewsEventArgs(int price)
        {
            this.price = price;
        }
    }

 

 NewsPaper类中的Notify方法要发生改变,代码如下

 

    /// <summary>
    /// 被观察具体实现类
    /// </summary>
    public class Log : IObservable
    {
        List<IObserver> objs = new List<IObserver>();
        public void Register(IObserver observer)
        {
            objs.Add(observer);
        }

        public void UnRegister(IObserver observer)
        {
            objs.Remove(observer);
        }

        public void Notify()
        {
            int price=100;
            if (objs != null & objs.Count > 0)
            {
                foreach (var obj in objs)
                {
                    var args = new NewsEventArgs(price);
                    obj.Update(args);
                    price++;
                }
            }

        }
    }

 

在订阅者的类中的Update方法也相应发生些改变

    /// <summary>
    /// 观察者具体实现
    /// </summary>
    public class People1 : IObserver
    {
        public void Update(NewsEventArgs args)
        {
            Console.WriteLine("你好,你订阅的报纸已经发版,价格是 " + args.Price);
        }
    }

 

运行结构如下

 

 五、拉模式

推模式就是主题者作为参数将自身返回给订阅者,所有订阅者获取到的信息是一样的,这个可以用来传递主题类的自身描述。

下面是修改后的newspaper类

    /// <summary>
    /// 被观察具体实现类
    /// </summary>
    public class NewsPaper : IObservable
    {
        public string Type { get { return "经济"; }  }

        public DateTime SubTime{ get { return DateTime.Now; } }

        List<IObserver> objs = new List<IObserver>();
        public void Register(IObserver observer)
        {
            objs.Add(observer);
        }

        public void UnRegister(IObserver observer)
        {
            objs.Remove(observer);
        }

        public void Notify()
        {
            if (objs != null & objs.Count > 0)
            {
                foreach (var obj in objs)
                {
                    obj.Update(this);
                }
            }

        }
    }

下面是修改后的读者的类

    /// <summary>
    /// 观察者具体实现
    /// </summary>
    public class Wang : IObserver
    {
        public void Update(IObservable sender)
        {
            NewsPaper news= (NewsPaper)sender;
            Console.WriteLine("你好 wang,你订阅的报纸已经发版,发布时间:" + news.SubTime + "类型是" + news.Type);
        }
    }

 

运行结果

 

 六、推模式和拉模式的区别

推模式和拉模式到底有什么区别,我认为是这样的,

推模式是订阅者需要什么我们就给什么,数据是针对某一个用户,具有隐私性,就像价格,虽然A,B都订阅了报纸,但他们的价格可能是不同的,这种情况就适合用推模式

拉模式是不管订阅者是什么,我们都将相同的东西返回给订阅者,这个可以是主题者自身的信息。

当然我们也可以两种模式一起使用,在.net中,事件委托就是同时用了两种模式

在实际运用中我们要根据实际情况来运用观察者模式

 

posted @ 2016-12-30 13:32  欢大少  阅读(479)  评论(3编辑  收藏  举报