c#之观察者模式

观察者模式

前言:最近花心思学习了一下设计模式,总体感觉设计模式主要是给我们提供解决问题的一种思路,是自己的代码可重用性高,保证代码的可靠性,设计模式使代码编制真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。
首先我们通过一个故事来进行探讨:在曾经的一段时间里,我们的股市是非常火爆的,几乎接近了全名炒股的程度,张三(化名)经常会在上班的时候关注股市的信息,但总是会担心老板出现在自己的身后,这样总归是不太好的。在这个时候,张三想到了一个办法,在它的公司有一个前台的秘书,平常没事的时候经常找她聊聊天,关系也是挺不错的。这时候他就请前台的秘书(小童)请她帮忙,如果老板的时候让她提前打个招呼。这样就不会怕发现了。

大家通过上面的故事:这个事情就是一个典型的观察者模式。

定义:

观察者模式:定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生改变的时候,会通知所有的观察者对象,使他们能够自动的进行更新。

 我们先给出图形方便大家的理解,如下:

我们首先定义抽象通知类接口,通过我们的开放-封闭原则和依赖倒转原则,我们应该让程序都依赖于抽象,而不是相互依赖。我们选择了定义抽象通知类接口,代码如下:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 观察者模式
{
    /// <summary>
    /// 抽象通知者接口
    /// </summary>
    interface Subject
    {
        /// <summary>
        /// 增加监听
        /// </summary>
        /// <param name="observer"></param>
        void Attach(Observer observer);
        /// <summary>
        /// 删除监听
        /// </summary>
        /// <param name="observer"></param>
        void Detach(Observer observer);
        /// <summary>
        /// 发送通知
        /// </summary>
        void Notify();
        /// <summary>
        /// 前台的状态
        /// </summary>
        string SubjectState { get; set; }
    }
}

 


那么接下来该定义抽象通知者的实现类了,我们继承的接口必须要实现里面全部的方法。全部代码的实现如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 观察者模式
{
    /// <summary>
    /// 前台通知者类
    /// </summary>
    class FrontDesk : Subject
    {
        //同事列表
        private IList<Observer> observers = new List<Observer>();
        //行动
        private string action;

        /// <summary>
        /// 前台通过电话 所说的话或者是所做的事情
        /// </summary>
        public string SubjectState
        {
            get { return action; }
            set { action = value; }
        }

        /// <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>
        public void Notify()
        {
            foreach (var o in observers)
            {
                o.Update();
            }
        }

    }
}

好的我们的抽象的通知者已经定义好了,接下来我们定义抽象的观察者,我们各个的观察者都需要一个更新自身的状态的方法.代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 观察者模式
{
    /// <summary>
    /// 抽象观察者
    /// </summary>
    abstract class Observer
    {

        //员工名字
        protected string name;
        //通知者的定义
        protected Subject sub;

        /// <summary>
        /// 构造函数进行初始化赋值操作
        /// </summary>
        /// <param name="name">名字</param>
        /// <param name="sub">通知者</param>
        public Observer(string name, Subject sub)
        {
            this.name = name;
            this.sub = sub;
        }

        /// <summary>
        /// 更新自身的状态
        /// </summary>
        public abstract void Update();

    }
}

接下来我们定义看股票观察者看NBA观察者并分别继承抽象观察者类,详细代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 观察者模式
{
    /// <summary>
    /// 实体类:看股票的同事
    /// </summary>
    class StockObserver : Observer
    {

        /// <summary>
        /// 继承父类的构造函数
        /// </summary>
        /// <param name="name">名字</param>
        /// <param name="sub">通知者</param>
        public StockObserver(string name, Subject sub) : base(name, sub)
        {

        }

        /// <summary>
        /// 自身的状态的改变
        /// </summary>
        public override void Update()
        {
            Console.WriteLine("{0},{1},关闭股票行情,继续工作", sub.SubjectState, name);
        }

    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 观察者模式
{
    class LookNBAObserver : Observer
    {

        /// <summary>
        /// 继承的构造函数
        /// </summary>
        /// <param name="name"></param>
        /// <param name="sub"></param>
        public LookNBAObserver(string name, Subject sub) : base(name, sub) { }

        /// <summary>
        /// 改变自身的状态
        /// </summary>
        public override void Update()
        {
            Console.WriteLine("{0},{1},关闭NBA,继续工作", sub.SubjectState, name);
        }

    }
}

前台的调用的代码,详细代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 观察者模式
{
    class Program
    {
        static void Main(string[] args)
        {
            //前台花花
            FrontDesk frontDesk = new FrontDesk();
            //看屁股(哈哈哈)的同事
            StockObserver stockObserver = new StockObserver("张雷", frontDesk);
            //看NBA的同事
            LookNBAObserver lookNbaObserver = new LookNBAObserver("骚鹏", frontDesk);
            //增加观察者
            frontDesk.Attach(stockObserver);
            frontDesk.Attach(lookNbaObserver);
            //花花发送消息,老板回来了
            frontDesk.SubjectState = "老板回来了";
            //减少观察者,这时候我不想通知张雷了,等着被老板抓吧,哈哈。
            frontDesk.Detach(stockObserver);
            //发出消息
            frontDesk.Notify();
            Console.ReadKey();
        }
    }
}

截图显示:

观察者模式的特点

我们接下来需要思考一个问题:用观察者模式的动机的什么呢?

将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维护一致性而是各类紧密耦合,这样会给维护,扩展和重用带来不便。

我们什么使用使用观察者模式呢:当一个对象的改变需要同时的改变其他的对象的时候。

其实当我们的通知者发出消息的时候,并不知道谁是他的观察者,也就是说,具体观察者是谁,他根本不需要知道是谁。而任何一个观察者也不知道也不需要知道其他的观察者的存在。

总体来说:观察者所做的工作其实是在解除耦合,让耦合的双方都依赖于抽象,而不是依赖于具体,从而使得各自的变化都不影响到另一边的变化。

观察者模式的不足

尽管我们用了依赖倒转原则,但是抽象通知者还是依赖于抽象观察者,也就是说,万一没有了抽象观察者这样的接口的实现,这边的功能就实现不了了。

我们接下来的改造:我们只需要在通知者中广播一个方法,至于是谁关注他不管,如果观察者对这条信息感兴趣就关注,不感兴趣就不关注。

事件委托实现观察者模式(推荐使用)

定义抽象通知者接口,代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 委托与事件的方式实现观察者模式
{
    interface Subject
    {
        //发送通知
        void Notify();
        //前台的状态
        string SubjectState { get; set; }
    }
}

 


定义前台类实现接口,代码如下:

using System;
using System.Collections.Generic;
using System.Diagnostics.Eventing.Reader;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 委托与事件的方式实现观察者模式
{
    //委托的声明
    delegate void EventHandler();
    /// <summary>
    /// 前台通知者
    /// </summary>
    class FrontDesk : Subject
    {

        //事件的声明
        public static event EventHandler Update;

        /// <summary>
        /// 前台的状态 所说的话或者所做的事情
        /// </summary>
        private string _subjectState;
        public string SubjectState
        {
            get { return _subjectState; }
            set { _subjectState = value; }
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        public void Notify()
        {
            Update();
        }

    }
}

定义抽象观察者,代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 委托与事件的方式实现观察者模式
{
    abstract class Observer
    {
        //名字
        protected string name;
        //对通知者的引用
        protected Subject sub;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="name">名字</param>
        /// <param name="sub">通知者</param>
        public Observer(string name, Subject sub)
        {
            this.name = name;
            this.sub = sub;
        }

        /// <summary>
        /// 更新自身的状态
        /// </summary>
        public abstract void Update();
    }
}

观察者的实现类,代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 委托与事件的方式实现观察者模式
{
    class StockObserver : Observer
    {
        public StockObserver(string name, Subject sub) : base(name, sub)
        {
            //感兴趣进行关注,进行事件的注册
            FrontDesk.Update += Update;
        }
        /// <summary>
        /// 重写父类的方法,更新自己行为的方法
        /// </summary>
        public override void Update()
        {
            Console.WriteLine("{0} {1},关闭股票行情,继续工作", sub.SubjectState, name);
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 委托与事件的方式实现观察者模式
{
    class NBAObserver : Observer
    {
        public NBAObserver(string name, Subject sub) : base(name, sub)
        {
            //事件的注册
            FrontDesk.Update += Update;
        }

        public override void Update()
        {
            Console.WriteLine("{0} {1},关闭NBA直播,继续工作", sub.SubjectState, name);
        }
    }
}

客户端代码的调用,代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 委托与事件的方式实现观察者模式
{
    class Program
    {
        static void Main(string[] args)
        {
            //前台花花
            FrontDesk huahua = new FrontDesk();
            //看股票的同事
            new StockObserver("张三", huahua);
            //看NBA直播的同事
            new NBAObserver("李四", huahua);
            huahua.SubjectState = "老板来了";
            //消息的发送
            huahua.Notify();
            Console.ReadKey();
        }
    }
}

调试结果截图如下:

这样的话委托和事件的方式很好的实现了观察者模式!!好的我们的观察者模式就讲解完了,大家有什么问题和建议请大家积极的发言,明天就是10.24,程序员节快乐。

 

posted @ 2017-10-23 23:23  丢了蜡笔小新会哭〆  阅读(225)  评论(0编辑  收藏  举报