一、设计模式-Observer观察者模式

Observer设计模式是为了定义对象间的一种一对多的依赖关系,以便于当一个对象的状态改变时,其他依赖于它的对象会被自动告知并更新。Observer模式是一种松耦合的设计模式。

Observer设计模式中主要包括如下两类对象:
Subject:监视对象,它往往包含着其他对象所感兴趣的内容。(如下例中的Boss,需要一直被手下盯着等候他给出指令)
Observer:监视者,它监视Subject,当Subject中的某件事发生的时候,会告知Observer,而Observer则会采取相应的行动。(如下例中的MemberA和B,他们需要一直盯着Boss准备接收指令采取行动)

例:

老大带着小弟去谈判,如果老大举起左手则小A行动,如果老大举起右手则小B行动,如果情况不妙老大摔了杯子则小A小B一起行动。

要完成以上需求,我们知道如果使用传统编码方式会采用,在老大的Boss类中有一个举手()和一个摔杯子()方法,在这个方法中调用小A和小B的行动方法。

public class Boss
{
private MemberA memberA = new MemberA();
private MemberB memberB = new MemberB();
/// <summary>
/// 举手
/// </summary>
/// <param name="hand"></param>
public void Raise(string hand)
{
if (hand == "左")
{
memberA.Attack();
}
else if (hand == "右")
{
memberB.Attack();
}
}
/// <summary>
/// 摔杯子
/// </summary>
public void Fail()
{
memberA.Attack();
memberB.Attack();
}
}
public class MemberA
{
public void Attack()
{
//do something...
}
}
public class MemberB
{
public void Attack()
{
//do something...
}
}

好了,我们按照这种方式完成了编码,此时需求变更了,对方人数过多我们需要多带些人手,增加MemberC、MemberD,就要在Boss类中频繁的修改Raise()和Fail(),使程序毫无扩展性,那么我们改为事件委托的方式试试看。

#region 观察者模式
/// <summary>
/// 左/右手
/// </summary>
public enum Hand
{
Left,
Right
}
/// <summary>
/// 监视对象
/// </summary>
public class Boss
{
//我要举起的手
public Hand MyHand { get; set; }
//举手-委托-事件
public delegate void RaiseDelegate(Hand myHand);
public event RaiseDelegate RaiseEventHandler;
//摔杯子-委托-事件
public delegate void FailDelegate();
public event FailDelegate FailEventHandler; public void RaiseHand()
{
Console.WriteLine("举手-" + MyHand);
if (RaiseEventHandler != null)
{
RaiseEventHandler(MyHand);
}
}
public void FailAll()
{
Console.WriteLine("摔杯子");
if (FailEventHandler != null)
{
FailEventHandler();
}
}
}
/// <summary>
/// 监视者A
/// </summary>
public class MemberA
{
public void HandLeft(Hand myHand)
{
if (myHand == Hand.Left)
Attack();
}
public void Attack()
{
Console.WriteLine("MemberA攻击");
}
}
/// <summary>
/// 监视者B
/// </summary>
public class MemberB
{
public void HandRight(Hand myHand)
{
if (myHand == Hand.Right)
Attack();
}
public void Attack()
{
Console.WriteLine("MemberB攻击");
}
}
#endregion
#region 调用代码

Boss boss = new Boss();
MemberA memberA = new MemberA();
MemberB memberB = new MemberB(); boss.RaiseEventHandler += memberA.HandLeft;
boss.FailEventHandler += memberA.Attack; boss.RaiseEventHandler += memberB.HandRight;
boss.FailEventHandler += memberB.Attack; boss.MyHand = Hand.Left;
boss.RaiseHand();
boss.FailAll(); #endregion

改造完成,又有需求变更了,增加老大带的人手,此时我们主类Boss类不需要进行任何修改,只需要增加新的小弟类MemberC、MemberD等等即可,这样也实现了“对扩展说Yes,对修改说No”的宗旨。

二、.Net中的事件与委托

仔细看会发现,我们写的和.Net中自带的委托事件不太一样,尤其是Webform时的那些按钮事件

首先.Net有几条编码规范:

1、委托类型的名称都应该以EventHandler结尾

2、委托的原型定义,要无返回值void,要有两个输入参数,一个object类型,一个EventArgs类型(或继承EventArgs)

3、事件的名称为委托去掉EventHandler剩余的部分

4、继承EventArgs的子类名称都应以EventArgs结尾

结合我们的例子解释一下:

1、委托声明原型参数中object对象代表了Subject,也就是监视对象,本例中就是Boss自己,注册委托的方法(MemberA)中可以访问触发事件的对象(Boss)

2、EventArgs对象代表了Observer,也就是监视者,其中包含了监视者所需要的数据,本例中就是举起的那只手左还是右

这样的设计并不是硬套编码规范,它还具有很强的灵活性,如果需要在Observer下显示Subject的信息,比如想显示出手(MemberA)下保护了那个老大(Boss.Name),就需要在委托方法中增加很多参数,而这样我们直接

把Boss对象引用传递给MemberA的方法,就可以根据自己的需求直接访问Boss了

#region 观察者模式
/// <summary>
/// 左/右手
/// </summary>
public enum Hand
{
Left,
Right
}
/// <summary>
/// 监视对象
/// </summary>
public class Boss
{
//我的手
public Hand MyHand { get; set; }
//我的名字
public string MyName
{
get { return "Taiyonghai"; }
}
//举手-委托-事件
public delegate void RaiseEventHandler(object sender, RaiseEventArgs e);
public event RaiseEventHandler Raise;
//举手事件参数对象(此对象就是为了整合所有监视者关心的属性,便于直接传递给监视者使用,也就是监视者需要什么字段就定义什么字段)
public class RaiseEventArgs : EventArgs
{
public Hand myHand;
public RaiseEventArgs(Hand myHand)
{
this.myHand = myHand;
}
}
//举手后需要执行事件方法(执行是否调用已注册到此事件上的方法,设为虚方法为继承此类的子类提供的可重写的能力,
//以便子类自己选择是否接受监视并调用已注册方法,或者不被监视不调用任何方法)
public virtual void OnRaiseHand(RaiseEventArgs e)
{
if (Raise != null) //有否已有注册方法
{
Raise(this, e); //调用所有已注册的方法
}
}
//举手方法
public void RaiseHand()
{
Console.WriteLine("举手-" + MyHand);
RaiseEventArgs e = new RaiseEventArgs(MyHand);
OnRaiseHand(e);
} //摔杯子-委托-事件
public delegate void FailEventHandler(object sender);
public event FailEventHandler Fail;
//摔杯子需要执行事件方法
public virtual void OnFailAll()
{
if (Fail != null)
{
Fail(this);
}
}
//摔杯子方法
public void FailAll()
{
Console.WriteLine("摔杯子");
OnFailAll();
}
}
/// <summary>
/// 监视者A
/// </summary>
public class MemberA
{
public void HandLeft(object sender, Boss.RaiseEventArgs e)
{
Boss boss = (Boss)sender;
if (e.myHand == Hand.Left)
Console.WriteLine("MemberA单独行动,保护" + boss.MyName);
}
public void Attack(object sender)
{
Boss boss = (Boss)sender;
Console.WriteLine("MemberA一起行动,保护" + boss.MyName);
}
}
/// <summary>
/// 监视者B
/// </summary>
public class MemberB
{
public void HandRight(object sender, Boss.RaiseEventArgs e)
{
Boss boss = (Boss)sender;
if (e.myHand == Hand.Right)
Console.WriteLine("MemberB单独行动,保护" + boss.MyName);
}
public void Attack(object sender)
{
Boss boss = (Boss)sender;
Console.WriteLine("MemberB一起行动,保护" + boss.MyName);
}
}
#endregion
#region 调用代码

Boss boss = new Boss();
MemberA memberA = new MemberA(); boss.Raise += memberA.HandLeft; //普通注册方法(也可以静态方法注册)
boss.Fail += memberA.Attack; boss.Raise += new MemberB().HandRight; //匿名对象注册方法
boss.Fail += new MemberB().Attack; boss.MyHand = Hand.Left;
boss.RaiseHand();
boss.FailAll(); #endregion

参照别人的讲解,编写了这个小Demo,总算是理解了委托、事件、Observer设计模式及.Net规范下的委托事件写法,重点还是要应用到工作当中才能真正把知识变成自己的

继续共享出我参照的文章地址:http://www.tracefact.net/CSharp-Programming/Delegates-and-Events-in-CSharp.aspx

还有一篇解释细节的文章,我还没有时间看,先做个记录,文章地址:http://www.tracefact.net/CSharp-Programming/Delegates-and-Events-Advanced.aspx

委托、事件、Observer观察者模式的使用解析二的更多相关文章

  1. C# ~ 从 委托事件 到 观察者模式 - Observer

    委托和事件的部分基础知识可参见 C#/.NET 基础学习 之 [委托-事件] 部分: 参考 [1]. 初识事件 到 自定义事件: [2]. 从类型不安全的委托 到 类型安全的事件: [3]. 函数指针 ...

  2. 委托、事件、Observer观察者模式的使用解析一

    一.前言 委托.事件得理论我就不解释了,不会的时候觉得很难,会了发现挺简单的,回头想想其实在JavaScript中常常用到,譬如:setTimeout()就是典型的委托. 二.传统编码方式 传统的调用 ...

  3. 委托 事件 observer

    详细介绍http://www.cnblogs.com/jcz1206/articles/2730793.html  ---摘录别人的 using System;using System.Collect ...

  4. java设计模式解析(1) Observer观察者模式

      设计模式系列文章 java设计模式解析(1) Observer观察者模式 java设计模式解析(2) Proxy代理模式 java设计模式解析(3) Factory工厂模式 java设计模式解析( ...

  5. C#固定时间执行指定事件(观察者模式+异步委托)

    最近有个项目需要每天固定的时间去执行指定的事件,发现网上关于这样的文章比较少,而且比较散.通过学习了几篇文章后终于实现了这个功能,在此也特别感谢这些文章的作者们,这也是我第一次在园子里面发文章,望多指 ...

  6. Observer设计模式中-委托事件-应用在消息在窗体上显示

    Observer设计模式:监视者模式.在类中的方法中处理的结果或者消息通过事件委托 的方式发送给主窗体. 因为在其它类中直接访问主窗体类,显示内容是不能直接调用控件赋值的,当然也有别的类似查阅控件名, ...

  7. .NET Core 跨平台物联网开发:设置委托事件(二)

    系列教程目录 (一) 连接阿里云IOT (二) 设置委托事件 (三) 上报属性 (四)  SDK文档 属性.方法.委托.类 http://pan.whuanle.cn/index.php?dir=up ...

  8. C# 委托 事件

    一:什么叫委托 通过反射发现,委托其实是一个类,继承自System.MulticastDelegate,但是System.MulticastDelegate这个类是特殊类,不能被继承 二:委托的声明 ...

  9. 设计模式18:Observer 观察者模式(行为型模式)

    Observer 观察者模式(行为型模式) 动机(Motivation) 在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系”——一个对象(目标对象)的状态发生改变,所有依赖对象(观察者对象) ...

随机推荐

  1. mbatis_逆向工程

    mybatis逆向工程 什么是逆向工程? mybatis需要程序员自己编写sql语句,mybatis官方提供逆向工程,可以针对单表自动生成所需的代码(mapper,java,po...)等,对于新手不 ...

  2. Spring五个事务隔离级别和七个事务传播行为

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt216 Spring五个事务隔离级别和七个事务传播行为 1. 脏读 :脏读就是 ...

  3. visual studio xamarin 离线安装文件以及 android 模拟器

    介绍 为了使用vs开发android我也是煞费苦心,先是从网上各种搜刮文章,然后找各种各样的离线包(因为国内网络是下载不了C#/Xamain)的包的,还有各种各样的安装包,都已快接近奔溃的边缘.每次不 ...

  4. 扫雷游戏制作过程(C#描述):第三节、雷区绘制

    前言 这里给出教程原文地址. 该项目已经放在github上托管. 绘制雷区 这一节我们主要涉及界面中雷区的绘制.绘制雷区需要三个变量来保存雷区行数.列数.以及地雷的数量.而且我们希望能够自动获取上次游 ...

  5. 第06周-接口、内部类与Swing

    1. 本周作业简评与建议 作业简评 Q1.覆盖clone需要:a.要implements标记接口 Cloneable接口.b.要区分浅拷贝与深拷贝.c.一般来说要调用super.clone,然后在此基 ...

  6. del命令实现全盘删除指定文件

    @echo off Rem :全盘删除指定文件 set "fileName=Normal.dotm" set "outPutPath=C:\result.txt" ...

  7. JS中有关分支结构、循环结构以及函数应用的一些简单练习

    案例一:搬桌子    年龄大于七岁男女都可以搬桌子,年龄小于七岁大于五岁的男生可以搬桌子: var num =parseInt(prompt("请输入你的年龄")) var sex ...

  8. Java:final、static关键字 详解+两者结合使用

    一  final关键字 1) 关于final的重要知识点 final关键字可以用于成员变量.本地变量.方法以及类. final成员变量必须在声明的时候初始化或者在构造器中初始化,否则就会报编译错误. ...

  9. java.lang.IllegalArgumentException: object is not an instance of declaring class

    今天在使用反射的时候,出现了java.lang.IllegalArgumentException: object is not an instance of declaring class错误-具体是 ...

  10. Struts2第十一篇【简单UI标签、数据回显】

    Struts2UI标签 Sturts2为了简化我们的开发,也为我们提供了UI标签-也就是显示页面的标签-.. 但是呢,Struts2是服务端的框架,因此使用页面的标签是需要在服务器端解析然后再被浏览器 ...