zoukankan      html  css  js  c++  java
  • 观察者模式与.NET的delegate、event机制

    1.引言

             最近在写一些程序玩的时候,接触到了delegate(委托)和event(事件),网上查找了很多的资料,有些博文说可以把delegate近似当做C++当中的函数指针来看,由于自己本身对C++的理解并不是很透彻,所以看得仍然朦朦胧胧。今天上课学习了设计模当中的观察者模式。仔细学习之下,又对委托和事件有了新的体悟,特分享给大家。也希望能够与各位大虾多多交流。

    2.观察者模式介绍

    2.1概述

          观察者模式是建立一种对象与对象之间的依赖关系,一种对象发生改变时将自动通知其他对象,其他对象相应的做出反应。在观察者模式中,发生改变的对象称为观察目标,被通知对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间可以没有任何相互联系。就像在大街上,红灯亮起,来往的汽车停止,绿灯亮起,汽车可以继续前进,在这个过程中,交通信号是汽车的观察目标,大街上的汽车是观察者,并且大街上的每个汽车都是单独的个体没有相互之间的联系。

    2.2观察者模式的定义

    观察者模式:定义对象之间一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象都得到通知并被更新。

    Observer Pattern: Define a one-to-manydependency between objects so that when one object changes state , all itsdependts are notified and update automatically.

    2.3观察者模式结构

     观察者模式包含以下四个角色:

    (1)Subject(目标)

    (2)ConcreteSubject(具体目标)

    (3)Observer(观察者)

    (4)ConcreteObserve(具体观察者)

    2.4代码实例 

    using System.Collection
    
    
    //定义一个抽象目标类Subject
    
    abstract class Subject
    {
           //定义一个观察者集合用于存储所有观察者对象
          protected ArrayList observers = new ArrayList();
    
          //声明抽象注册方法,用于向观察者集合中增加一个观察者
          public abstract void Attach(Observer observer);
        
          //声明抽象的注销方法,用于在观察者集合中删除一个观察者
          public abstract void Detach(Oberver observer)
    
         //声明抽象通知方法
          public abstract void Notify();     
    }    
    
    //具体目标类ConcreteSubject 是实现了抽象目标类Subject的一个具体
    //子类,它实现了上述的3中方法
    
    class ConcreteSubject : Subject
    {
        public override void Attach(Observer observer)
        {
            observers.Add(observer);
        }
        
        public override void Detach(Observer observer)
        {
            observers.Remove(observer);
        }
        
        //实现通知方法
        public override void Notify()
        {
            //遍历观察者几何,调用每一个观察者的相应方法
            foreach (object obs in observers)
            {
                ((Observer)obs).Update();
            }
        }
        
        /*抽象观察者一般定义为一个借口,通常只声明一个Update()方法
         *为不同观察者的更新(相应)行为定义相同的借口,这个方法在其
         *子类中实现。
         */
         interface Observer
         {
            void Update();
         }
         
         //具体观察者ConcreteObserver中实现Update()方法
         class ConcreteObserver : Observer
         {
            //实现方法
            public void Update()
            {
            //具体更新代码
            }
         }
         static void Main(string[] args)
         {
            ...
            Subject subject = new ConcreteSubject();
            Observer observer = new ConcreteObserver();
            subject.Attach(observer);
            subject.Notify();
            ...
         }
    }

    3..NET中的delegate与event

    .NET中的委托事件模型是观察者模式在.NET中的经典应用,在WinForm编程中需要编写时间处理程序对所发生的事件(例如鼠标单击、菜单项选取等)做出反应,并执行相应的操作。事件被触发后,并执行响应该事件的一个或多个事件处理程序,可以将一个事件对象(例如按钮、文本框、菜单等)成为事件的发送者(事件源对象),接收并响应事件的对象称为时间的接收者(事件处理对象)。与观察这模式相对应,事件源对象充当观察目标角色,事件处理对象充当具体观察者角色,如果事件源对象的某个事件触发,则调用事件处理对象中的事件处理程序对事件进行处理。

    在.NETzhong ,如果需要从WinForm控件获取事件,提供一个委托(Delegate)类型的EventHandler,然后将它注册到事件源。在这里委托对象充当着抽象观察者的角色,所有事件处理方法都必须和委托方法具有相同的函数签名。

    eventSource.someEvent += new SomeEventHandler(someMethod);

    在该语法中,eventSource表示事件源,someEvent表示定义在事件源中的事件,someEventHandler表示用于处理事件的委托,someMethod表示与委托SomeEventHandler具有相同函数签名的事件处理方法。用户只需要修改someMethod,即可实现相同的时间对应不同的时间处理程序。

    在.NET事件中,事件源并不需要知道哪些对象或方会受到将要发生的通知,它只持有与签名相符合的方法的引用,即委托;还可以通过多重传送事件来实现一个事件有过个订阅者,即通过委托将多个方法添加到该事件中,当该事件被触发时,同时执行对应的多个事件处理方法。

    代码实例:

    using System;
    
    namespace ObserverExtend
    {
    	class EventTest
    	{
    		//定义一个委托
    		public delegate void UserInput(object sender, EventArgs e);
    		
    		//定义一个此委托类型的事件
    		public event UserInput OnUserInput;
    		
    		//模拟事件触发,当输入“0”时引发事件
    		public void Method()
    		{
    			bool flag = false;
    			Console.WriteLine("请输入数字:");
    			while(!flag)
    			{
    				if(Console.ReadLine() == "0")
    				{
    					OnUserInput(this ,new EventArgs());
    				}
    			}
    		}
    	}
    	
    	class Program
    	{
    		public Program(EventTest test)
    		{
    			//注册或订阅事件
    			test.OnUserInput += newEventTest.UserInput(Handler);
    			test.OnUserInput += newEventTest.UserInput(HandlerMore);
    			
    			//注销或取消订阅
    			//test.OnUserInput -= newEventTest.UserInput(Handler);
    		}
    		public void Handler(object sender,EventArgs e)
    		{
    			Console.WriteLine("数据输入结束!");
    		}
    		public void HandlerMore(object sender,EventArgs e)
    		{
    			Console.WriteLine("数据输入真的结束!");
    		}
    		
    		static void Main(string[] args)
    		{
    			EventTest test = new EventTest();
    			Program program = new Program();
    			tset.Method();
    		}
    	}
    }
    

      

    在类EventTest中定义了一个委托UserInput和一个事件 OnUserInput,EventTest充当观察目标类的角色而委托充当抽象观察者角色,在方法Method()中引发了事件,即调用与委托具有相同函数签名的方法,方法Method()即为目标类的通知方法。在客户端测试类Program中提供具体的时间处理方法,并将该方法和事件绑定在一起,这个过程称为订阅事件。
    在Program的构造函数中订阅事件,在此处,通过委托将两个方法添加到事件中,即该事件有两个订阅者,当事件触发时同时触发这些方法的执行,Program类充当具体观察者角色,可以对目标类的事件作出响应,方法Handler()和HandlerMore() 即为响应方法。

    4.感悟

    设计模式的学习让我提升了不少对C#的理解,记得有句话,有抱负的开发人员还需大力发扬钻研精神,不拘泥与可视化软件实际带来的便利之处,深入理解相关的技术细节,这是初级开发人员的必由之路。我们要会的不仅仅是拖一个Button。加油,与君共勉~

    5.参考资料

    http://www.cnblogs.com/wangjq/archive/2012/07/12/2587966.html

    《C#设计模式》刘伟 胡志刚 闫朝坤 清华大学出版社

  • 相关阅读:
    ZeptoLab Code Rush 2015
    UVa 10048 Audiophobia【Floyd】
    POJ 1847 Tram【Floyd】
    UVa 247 Calling Circles【传递闭包】
    UVa 1395 Slim Span【最小生成树】
    HDU 4006 The kth great number【优先队列】
    UVa 674 Coin Change【记忆化搜索】
    UVa 10285 Longest Run on a Snowboard【记忆化搜索】
    【NOIP2016提高A组模拟9.28】求导
    【NOIP2012模拟10.9】电费结算
  • 原文地址:https://www.cnblogs.com/MaFeng0213/p/5055326.html
Copyright © 2011-2022 走看看