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

  观察者(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运行。

        }

猜你喜欢

转载自blog.csdn.net/CharmaineXia/article/details/110753104