观察者模式(委托和事件)——老板来了我不知道

简介: 观察者模式(委托和事件)——老板来了我不知道

 观察者(Publish/Subscribe)模式定义: 又叫发布—订阅模式,定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象发生变化时,会通知所有观察者对象,使他们能够自动更新自己。


 优点:


解除耦合,让耦合的双方都依赖于抽象,而不是依赖于具体,从而使得各自的变化都不会影响另一边变化,符合依赖倒转原则。

 缺点:


抽象通知者还是依赖抽象观察者,不能进一步的解除耦合,改进方式是使用委托。

 使用场景:当一个对象的改变需要同时改变其他对象的时候,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。


 代码背景:公司的两个员工时刻监听老板是否回来了,因为他俩想要趁老板不在做自己的事情,如果有人通知他们老板回来了,他们就会放下手中的事情继续工作。很不巧这次是老板自己通知的这两个员工——他来了。

 通知者:老板。

 观察者:两个员工,根据通知的内容作出不一样的行为。


抽象通知者:把所有对观察者对象的引用(Attach、Detach、Notify)保存在一个聚集里,每个主题都可以有任何数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象,并且不需要知道具体观察者是谁,任何一个具体观察者也不需要知道其他观察者的存在。

    interface Subject   //通知者接口
    {
        void Attach(Observer observer);
        void Detach(Observer observer);
        void Notify();
        string SubjectState   //通知者状态
        {
            get;
            set;
        }
    }

抽象观察者 :观察者本来可以是任何一个对象,使用抽象增加代码可扩展性(或者用接口)

    abstract  class Observer    
    {
        protected string name;
        protected Subject sub; 
        public Observer (string name,Subject sub) 
        {
            this.name = name;
            this.sub = sub;
        }
        public abstract void Update();
    }

Boss类:具体主题

    class Boss:Subject  //实现接口
    {
        //同事列表
        private IList<Observer> observers = new List<Observer >();
        private string action;
        //增加要通知的对象
        public void Attach(Observer observer)
        {
            observers.Add(observer);
        }
        //减少要通知的对象
        public void Detach(Observer observer)
        {
            observers.Remove(observer);
        }
        //通知(包含了员工的Update方法,一旦被通知,员工就执行Update方法)
        public void Notify ()
        {
            foreach (Observer o in observers)//遍历所有要通知的对象,然他们依次执行Update方法
                o.Update();
        }
        //老板状态(重写接口)
        public string SubjectState 
        {
            get {return action;}
            set {action = value;}
        }
    }

员工类:具体观察者

    class StockObserver:Observer    ///看股票的员工
    {
        public StockObserver(string name, Subject sub) : base(name, sub)//抽象观察者,观察者本来就可以是任何人
        { }
        public override void Update()   //重写更新方法
        {
            Console.WriteLine("{0}{1}关闭股票行情,继续工作!", sub.SubjectState , name);
        }
    }
    class NBAobserver : Observer    //看NBA的员工
    {
        public NBAobserver(string name, Subject sub) : base(name, sub)
        { }
        public override void Update()   //重写更新方法
        {
            Console.WriteLine("{0}{1}关闭NBA直播,继续工作!", sub.SubjectState, name);
        }
    }

客户端:

        static void Main(string[] args)
        {
      //老板
            Boss bazong = new Boss();
            //摸鱼的同事
            StockObserver tongshi1 = new StockObserver("江小鱼",bazong);
            NBAobserver tongshi2 = new NBAobserver ("李建刚", bazong);
            //老板默默记下要通知的两位同事
            bazong.Attach(tongshi1);
            bazong.Attach(tongshi2);
            //老板大意了,江小鱼没听见,没被通知到,所以减去这个人
            bazong.Detach(tongshi1);
            //老板状态改变
            bazong.SubjectState = "我霸道总裁回来了!";
            //发出通知,员工执行Update方法
            bazong.Notify();
            Console.Read();
        }
    }

例子中没有关于数据和状态的变化通知,只是简单通知到各个观察者,告诉他们被观察者有行动。

观察者模式在关于目标角色、观察者角色通信的具体实现中,有两个版本。


一种情况便是目标角色在发生变化后,仅仅告诉观察者角色“我变化了”,观察者角色如果想要知道具体的变化细节,则就要自己从目标角色的接口中得到。这种模式被很形象的称为:拉模式——就是说变化的信息是观察者角色主动从目标角色中“拉”出来的。


还有一种方法,那就是我目标角色“服务一条龙”,通知你发生变化的同时,通过一个参数将变化的细节传递到观察者角色中去。这就是“推模式”——管你要不要,先给你啦。

这两种模式的使用,取决于系统设计时的需要。如果目标角色比较复杂,并且观察者角色进行更新时必须得到一些具体变化的信息,则“推模式”比较合适。如果目标角色比较简单,则“拉模式”就很合适。


  委托(delegate)定义:委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。

 委托是一种引用方法的类型,一旦被委托分配了方法,委托将与该方法具有相同的行为。

//Update是事件,EventHandler是委托类型,tongshi1.CloseGameMarket是被委托的方法
//表示将tongshi1的CloseGameMarket方法通过实例化委托EventHandler登记到laoban的事件Update当中。
//+=表示add_CatShout,增加委托实例对象
laoban.Update += new EventHandler(tongshi1.CloseGameMarket );

 委托是对函数的封装,可以给方法的特征制定一个名称(如下例的“EventHandler”)。


 而事件是委托的一种特殊形式,当发生事件时(如下例的“老板来视察”),委托类型(事件处理对象,也就是下例的“EventHandler”)通知过程(如下例的tongshi1.CloseGameMarket、tongshi2.CloseZbObserver)。


玩游戏的同事:

    class GameObserver
    {
        private string name;
        private Subject sub;
        public GameObserver(string name,Subject sub)
        {
            this.name = name;
            this.sub = sub;
        }
        //关闭游戏
        public void CloseGameMarket()
        {
            Console.WriteLine("{0}{1}关闭王者荣耀,继续工作!",sub.SubjectState,name);
        }
    }

看直播的同事

    class ZbObserver
    {
        private string name;
        private Subject sub;
        public ZbObserver (string name,Subject sub)
        {
            this.name = name;
            this.sub = sub;
        }
        //关闭直播
        public void CloseZbObserver()
        {
            Console.WriteLine("{0}{1}关闭直播,继续工作!",sub.SubjectState,name);
        }
    }

通知者状态接口:

    interface Subject
    {
        void Notify();
        string SubjectState     //通知者状态
        {
            get;
            set;
        }
    }

老板:

    delegate void EventHandler();
    class Boss :Subject     //老板是和前台都是通知者
    {
        //声明一事件Update,委托类型为EventHandler
        public event EventHandler Update;
        private string action;
        public void Notify()    //通知
        {
            Update();
        }
        public string SubjectState  //通知者状态——重写接口方法
        {
            get { return action; }
            set { action = value; }
        }
    }

客户端:

        static void Main(string[] args)
        {
            Boss laoban = new Boss();
            //玩游戏的同事
            GameObserver tongshi1 = new GameObserver("秦霜",laoban);
            //看直播的同事
            ZbObserver tongshi2 = new ZbObserver("楚楚",laoban);
      //实例化一个委托,委托的实例是tongshi1的CloseGameMarket
            laoban.Update += new EventHandler(tongshi1.CloseGameMarket );
            laoban.Update += new EventHandler(tongshi2.CloseZbObserver );
            //老板回来
            laoban.SubjectState = "老板:我来视察工作了\n";
            //发出通知
            laoban.Notify();//Notify()方法一运行,里面的Update事件就运行,Notify()方法就委托EventHandler去通知方法tongshi1.CloseGameMarket运行。
        }
相关文章
开门小例子学习观察者模式&事件与委托
3.2.一个喊话人(喊话人有权限),多个拿钥匙开门的人(每个人负责不同的门):米老师大喊给我开一下水麒麟的门——>此时听到消息并且拿着水麒麟钥匙的人就会过来开门
|
Java C# uml
观察者模式实例股票通知
观察者模式实例股票通知
144 0
观察者模式实例股票通知
|
设计模式 Java Android开发
Java设计模式之观察者模式的应用--简单的事件通知工具
Java设计模式之观察者模式的应用--简单的事件通知工具
174 0
Java设计模式之观察者模式的应用--简单的事件通知工具
|
设计模式 存储 Java
监听器模式和观察者模式的关系,写点你不知道的
监听器模式和观察者模式的关系,写点你不知道的
551 0
监听器模式和观察者模式的关系,写点你不知道的
一个插排引发的设计思想 (三) 委托与事件
一个插排引发的设计思想 (三) 委托与事件
101 0
艾伟_转载:把委托说透(4):委托与设计模式
委托与很多设计模式都有着千丝万缕的联系,在前面的随笔中已经介绍了委托与策略模式的联系,本节主要来讨论委托与其他两个模式:观察者模式和模板方法模式。 委托与观察者模式 在.NET中,很多设计模式得到了广泛应用,如foreach关键字实现了迭代器模式。
1000 0
|
.NET
艾伟_转载:把委托说透(2):深入理解委托
在上一篇随笔中我们通过示例逐步引入了委托,并比较了委托和接口。本文将重点剖析委托的实质。 委托在本质上仍然是一个类,我们用delegate关键字声明的所有委托都继承自System.MulticastDelegate。
1110 0
|
监控 C#
艾伟_转载:把委托说透(1):开始委托之旅 委托与接口
委托,本是一个非常基础的.NET概念,但前一阵子在园子里却引起轩然大波。先是Michael Tao的随笔让人们将委托的写法与茴香豆联系到了一起,接着老赵又用一系列文章分析委托写法的演变,并告诫“嘲笑孔乙己的朋友们,你们在一味鄙视“茴”的四种写法的同时,说不定也失去了一个了解中国传统文化的机会呢!”。
1002 0
|
C# 图形学
Unity 3D观察者设计模式-C#委托和事件的运用
C#观察者设计模式 本文提供全流程,中文翻译。 Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 —— 高分辨率用户请根据需求调整网页缩放比例) Chinar —— 心分享、心...
1808 0