使用面向对象的思想 用c#控制台代码模拟猫抓老鼠

我们先来分析一下猫抓老鼠的过程

1.猫叫了

2.所有老鼠听到叫声,知道是哪只猫来了

3.老鼠们逃跑,边逃边喊:"xx猫来了,快跑啊!我是老鼠xxx"

一  双向耦合的代码

首先需要一个猫类Cat 一个老鼠类Rat 和一个测试类Program

老鼠类的代码如下

//老鼠类

public classRat

{public string Name { get; set; } //老鼠的名字

public Cat MyCat { get; set; } //老鼠遇到的猫//老鼠逃跑的方法

public voidRun()

{

Console.WriteLine(MyCat.Name+

"猫来了,大家快跑!!我是" +Name);//打印出信息 包含了猫的名字和老鼠本身的名字

}//带参和无参构造

publicRat() { }public Rat(stringname, Cat cat)

{this.Name =name;this.MyCat =cat;

}

}

要让猫叫的时候依次打印出老鼠的逃跑方法,需要在Cat类里添加一个存放Rat对象的集合

Cat类的代码如下

public classCat

{public string Name { get; set; } //猫的名字

List list = new List(); //存放Rat对象的集合//为集合增加老鼠

public voidAdd(Rat rat)

{

list.Add(rat);

}//移除

public voidRemove(Rat rat)

{

list.Remove(rat);

}//猫叫的方法

public voidShout()

{

Console.WriteLine("喵喵喵!");//如果集合非空,循环执行每只老鼠的Run()方法

if (list != null)

{foreach (Rat item inlist)

{

item.Run();

}

}

}publicCat() { }public Cat(stringname)

{this.Name =name;

}

}

在Main方法中,我们需要构建几个Rat对象和一个Cat对象,将Rat对象添加到Cat对象的集合中,调用Cat对象的Shout方法

代码如下

static void Main(string[] args)

{//构建一个Cat对象和两个Rat对象 老鼠遇到的猫是构建的cat

Cat cat = new Cat("Tom");

Rat rat1= new Rat("Jerry", cat);

Rat rat2= new Rat("TaoQi", cat);//调用猫类的Add方法添加老鼠对象

cat.Add(rat1);

cat.Add(rat2);//调用猫的Shout方法

cat.Shout();

Console.ReadKey();

}

运行结果如下

这样的代码缺陷很明显,Cat类和Rat类紧密耦合

猫可能不止要抓老鼠 还要抓小鸟

当然不止是猫会抓 也可能狗拿耗子多管闲事

于是我们可以把猫和狗提出来 继承自一个抽象类Pet

抓捕的小动物老鼠和小鸟没有什么关系 但是都能(逃)跑

先不去管小鸟是飞,我们把它们称作 可以跑的 都实现一个IRunable接口

二  观察者 模式(发布-订阅模式)

修改后的代码如下

新增抽象类Pet ,猫类继承自Pet   (猫类的代码变化不大 略去不写

public abstract classPet

{public List list = new List();public voidAdd(IRunable i)

{

list.Add(i);

}public voidRemove(IRunable i)

{

list.Remove(i);

}public abstract voidShout();

}

接口IRubable 里面定义一个Run方法

public interface IRunable

{

void Run();

}

老鼠Rat和鸟Bird两个类都实现了这个接口

以Bird为例 代码如下

classBird : IRunable

{//鸟的名字和遇到的猫

public string Name { get; set; }public Cat MyCat { get; set; }public voidRun()

{

Console.WriteLine(MyCat.Name+ "猫来了,快跑吧!我是小鸟" +Name);

}publicBird() { }public Bird(stringname, Cat cat)

{this.Name =name;this.MyCat =cat;

}

}

Rat类的代码几乎没有变化

那么在Main方法中也只需要稍作修改,增加一个Bird对象 略去不写 执行后的结果如下

以上猫抓老鼠的例子实际上就是用了一个设计模式:观察者模式

观察者模式又名发布-订阅模式(Publish-Subscribe)

1.Subject类 (通知者 主题)

//抽象类 里面有一个Observer类集合

把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有多个观察者.抽象主题提供一个接口,可以增加和删除观察着对象

2.Observer类 (观察者)

//抽象类

抽象观察者,为所有的具体观察者定义一个接口,在得到主题的更新时提醒自己

3.ConcreteObserver类

//父类是Observer类

具体观察者,实现抽象观察者角色所需求的更新接口,以便使本身的状态与主题的状态相协调

4.ConcreteSubject类

//父类是Subject

具体主题,将有关状态存入具体观察者对象,在具体主题的内部状态改变时,给所有登记过的观察者发出通知

观察者模式的特点

1.一个主题可以有任意数量依赖他的观察者,一旦主题的状态发生改变,所有观察者都可以得到通知

2.主题发出通知不需要知道具体观察者

3.具体观察者也不需要知道其他观察者的存在

但是

将一个系统分割成一系列相互作用的类有一个很不好的副作用,就是需要维护相关对象间的一致性,使得各个类紧密耦合,这样会给维护,扩展和重用都带来不便

应用时机:

当一个对象的改变需要同时改变其他对象的时候使用观察模式

不知道具体有多少对象有待改变时,应考虑使用观察者模式

一个抽象模型有两个方面,其中一个方面依赖于另一个方面

三 委托

举个栗子 正如之前所说,老鼠会跑Run,小鸟会飞Fly,这根本是两个毫不相干的方法

但是的确有相同点--它们的返回值类型都是空,传进的参数列表也都为空

我们怎么样能把这两个不相关的类Bird和Rat的对象都装到Cat中去,再判断是哪个类依次调用它们的方法?

其实我们可以直接拿出它们的方法来装到Cat中去.

//委托

委托是什么?

和类一样是一种用户自定义类型

委托提供了方法的抽象

委托存储的是一系列具有相同签名和返回值类型的方法的地址,调用委托的时候,委托包含的所有方法将被执行

1.定义委托

访问修饰符 delegate 返回值类型 委托名(参数列表);

public delegate void MyDel(int x);

2.声明和初始化

委托名 委托对象名;

委托对象名=new 委托名(类名.方法名);

MyDel del;

del=new MyDel(Cat.Shout);

委托名=方法名

del=Cat.Shout;

3.委托的运算

委托可以使用额外的运算符来组合.这个运算最终会创建一个新的委托,其调用列表是两个操作数的委托调用列表的副本连接.委托是恒定的,操作数委托创建后不会被改变,委托组合拷贝的是操作数的副本

MyDel del2=Cat.Catch;

MyDel del3=del+del2;

使用+=为委托新增方法

使用-=为委托移除方法

del+=Cat.Catch;

del-=Cat.Shout;

4.委托调用

if(del!=null){

del();//委托调用

}

5.匿名方法

匿名方法是在初始化委托时内联声明的方法

delegate(参数){代码块}

delegate int MyDel(intx);

MyDel del=delegate(intx){returnx;

};

//匿名方法不声明返回值

Lambda表达式

用来简化匿名方法的语法

MyDel del1 = (int x) => {return x;};

在参数列表和匿名方法之间放置Lambda运算符=>

Lambda运算符读作 goes to

MyDel del1 = x => {returnx};

MyDel del1= x => x;

定义委托像定义枚举一样,可以定义在类的外部

然后在类中可以创建委托的对象

比如 修改后的我们的程序

(只是一个测试 不写的很详细了 还是拿猫和老鼠举例子 猫类和老鼠类 以及定义的委托的代码如下)

//定义一个委托 名叫CatShoutEventHandler 没有返回值 参数列表为空

public delegate voidCatShoutEventHandler();//猫类

public classCat

{//在猫类里定义一个该委托的对象CatShout

publicCatShoutEventHandler CatShout;public voidShout()

{

Console.WriteLine("喵喵喵");//判断委托内是否为空,若不为空,执行该委托

if (CatShout != null)

{

CatShout();

}

}

}//老鼠类

public classRat

{public string Name { get; set; } //老鼠的名字//老鼠的逃跑方法

public voidRun()

{

Console.WriteLine(Name+ "跑了!");

}//无参和带参构造

publicRat() { }public Rat(stringname)

{this.Name =name;

}

}

那么接下来就是我们的Main方法

static void Main(string[] args)

{//构建一个猫对象和两个老鼠对象

Cat cat = newCat();

Rat rat1= new Rat("Jerry");

Rat rat2= new Rat("TaoQi");//向cat的委托对象CatShout中依次添加老鼠对象的Run方法//注!!添加的是整个方法 不需要加括号

cat.CatShout +=rat1.Run;

cat.CatShout+=rat2.Run;//调用cat的Shout方法

cat.Shout();

Console.ReadKey();

}

运行结果是这样的

然而 然而 然而

比如说我来做一个很贱的操作

在Main方法中来一个 cat.CatShout=null;

好了 不管Cat类中的CatShout有没有初始值或者有没有赋值过都没了

我们知道面向对象的的三大特征 封装,继承,多态

我们一样可以把委托封装起来,以控制它的访问权限

这就是事件

四 事件

事件(Event)是一个用户操作,或是一些特定的出现情况.应用程序需要在事件发生时响应事件

事件在类中声明且生成,且通过使用同一个类或者其他类中的委托与时间处理程序关联

声明事件

在类的内部声明事件,首先必须声明该事件的委托类型

public delegate void CatShoutEventHandler();

然后声明事件本身,使用 event 关键字

public event CatShoutEventHandler CatShout;

上面的代码定义了一个名为CatShoutEventHandler的委托和一个CatShout的事件,该事件在生成时会调用委托

声明事件后可以实例化事件,注册函数到事件解除事件函数注册方法

CatShout+=newCatShoutEventHandler(rat.Run);

CatShout+=rat2.Run;//将函数Run注册到事件CatSHout上

在使用事件的时候,如果在封装类的外部,则该事件只能出现在+=或-=的左边

所以 用事件封装过的代码我们再来一遍

额..好像顺理成章的就写了下去

代码就这里变了一行 唯一的变化就是多了个event关键字

我们在这里使用了一个高大上的工具 reflector

将我们的代码反编译之后,可以看到CatShout这个事件其实做了两件事

里面有两个方法 一个是add_CatShout(CatShoutEventHandler)  另一个是remov_Cat(CatShoutEventHandler)

分别对应这个事件的运算符号 +=  和 -=

所以其实事件就是相当于对委托的封装

五 Object sender和EventArgs e

然而我又要找问题了,我们翻回去看要求

有一点是老鼠知道猫的名字,要调用猫对象的Name属性,我们现在试着给猫加上这个属性

public string Name { get; set; }

我们要排除Cat类与Rat类的耦合,所以不能在Rat类中存放一个Cat对象

当然我们可以在老鼠的Run方法中增加传进去一个Cat对象,但是这样需要定义一个这个程序自己使用的委托类型

系统已经有一些定义好的委托类型

public delegate voidAction();public delegate void EventHandler(object sender, EventArgs e);

第一个委托叫Action 它是没有参数 没有返回值类型的

第二个叫做EventHandler 它有两个参数 Object类型的sender 和 EventArgs类型的e

第一个还好,第二个,我去,这是什么东西啊?

其实第二个这个委托类型我们都十分熟悉 因为在winForm窗体应用程序中,控件生成的方法就带着这两个参数

我们一般把触发事件的整个对象封装成Object类型 做第一个参数

而第二个参数呢 我们首先需要知道什么是EventArgs

[Serializable, ComVisible(true), __DynamicallyInvokable]public classEventArgs

{

[__DynamicallyInvokable]public static readonly EventArgs Empty = newEventArgs();

}

这是代码 首先我们知道了,这是个类

是系统定义的类,里面只有一个静态的readonly的自身变量 为空

也就是通过调用这个静态方法来返回一个新的(空的)EventArgs本身

要弄懂它,我们来先看一个例子

这是WinForm窗体中的TreeView的事件对应在窗体类中生成的方法

private void tvList_AfterSelect(object sender, TreeViewEventArgs e)

我们可以看到 诶 诶 里面传进去的第一个参数没错是object sender 然而第二个变成了TreeViewEventArgs类型的e

那这个TreeViewEventArgs是什么东西呢

首先没错的是它继承自EventArgs

然后它加多了许多方法,我们知道的是它可以返回我们当前选中的那个节点(而sender则代表的是整个TreeView控件)

sa,我们可以把要用到的一些属性封装到EventArgs之中 来排除两个类的耦合

应用到猫和鼠的例子中去,我们最后的代码是这样子的

//自定义的CatShoutEventArgs类,继承自EventArgs//用作保存Cat对象的Name属性,还可以扩展其他的功能

public classCatShoutEventArgs : EventArgs

{public string CatName { get; set; }public CatShoutEventArgs(stringname)

{this.CatName =name;

}

}//定义一个委托 名叫CatShoutEventHandler

public delegate voidCatShoutEventHandler

(objectsender,CatShoutEventArgs e);//猫类

public classCat

{public string Name { get; set; } //猫的名字//在猫类里定义一个事件CatShout,返回值类型是定义的委托

public eventCatShoutEventHandler CatShout;public voidShout()

{

Console.WriteLine("喵喵喵");//判断委托内是否为空,若不为空,执行该委托

if (CatShout != null)

{//new一个CatShoutEventArgs类,传入参数是自身的Name

CatShoutEventArgs e = newCatShoutEventArgs(Name);//执行CatShout事件,传入自身和e

CatShout(this, e);

}

}//无参和带参构造

publicCat() { }public Cat(stringname)

{this.Name =name;

}

}//老鼠类

public classRat

{public string Name { get; set; } //老鼠的名字//老鼠的逃跑方法

public void Run(objectsender, CatShoutEventArgs e)

{//打出一句话,包括了猫的名字和老鼠的名字

Console.WriteLine(e.CatName + "来了!" + Name + "跑了!");

}//无参和带参构造

publicRat() { }public Rat(stringname)

{this.Name =name;

}

}

嗯 然后Program类中的Main方法并不需要改动

为了视觉效果,我再放上来一遍

static void Main(string[] args)

{//构建一个猫对象和两个老鼠对象

Cat cat = new Cat("Tom");

Rat rat1= new Rat("Jerry");

Rat rat2= new Rat("TaoQi");//向cat的委托对象CatShout中依次添加老鼠对象的Run方法//注!!添加的是整个方法 不需要加括号

cat.CatShout +=rat1.Run;

cat.CatShout+=rat2.Run;//调用cat的Shout方法

cat.Shout();

Console.ReadKey();

}

好了 好了 最后的最后 看看我们程序的运行效果吧

by天命 2016.11.9

c#事件的发布-订阅模型_C# 委托和事件 与 观察者模式(发布-订阅模式)讲解 by天命...相关推荐

  1. c#事件的发布-订阅模型_C#事件发布设计模式

    Windows程序最基本的原理是"以消息为基础,以事件驱动之(Message Based, Event Driven)".程序的运行是要靠外部发生的事件来驱动的,也就是说程序需要不 ...

  2. c#事件的发布-订阅模型_C#事件订阅发布实现原理详解

    我们用一个简单的例子,来说明一下这种消息传递的机制. 有一家三口,妈妈负责做饭,爸爸和孩子负责吃...将这三个人,想象成三个类. 妈妈有一个方法,叫做"做饭".有一个事件,叫做&q ...

  3. 3.0 面向对象 委托和事件 异常和错误

    一.委托和事件 委托和事件这两个概念是完全配合的.委托仅仅是函数指针,那就是说,它能够引用函数,通过传递地址的机制完成.委托是一个类,当你对它实例化时,要提供一个引用函数,将其作为它构造函数的参数.事 ...

  4. 委托、Lambda表达式、事件系列06,使用Action实现观察者模式,体验委托和事件的区别...

    在"实现观察者模式(Observer Pattern)的2种方式"中,曾经通过接口的方式.委托与事件的方式实现过观察者模式.本篇体验使用Action实现此模式,并从中体验委托与事件 ...

  5. c# 创建委托 消息订阅_C#面向对象之委托和事件

    委托 1.定义:委托也叫代理,就是把事情交给别人来做. 2.声明委托语法: delegate 返回类型 委托名称(); 委托使用步骤 1.声明委托 public delegate int Calcul ...

  6. 委托和事件[delegate and event]_C#

    委托和事件: 1. 委托:一个能够表示方法的数据类型:它将方法作为对象封装起来,允许在运行时间接地绑定一个方法调用. 2. 声明委托数据类型: public delegate  bool Greate ...

  7. vue发布订阅模式,发布订阅模型

    1.什么是发布订阅模式 (又叫做观察者模式) 他定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于他的对象都将得到通知. 2.发布订阅模型 1.发布者会给订阅者提供一个方法以便 ...

  8. 点击事件调用匿名函数如何传参_事件发布/订阅模式的简单实现

    这是一种广泛应用于异步编程的模式,是回调函数的事件化,常常用来解耦业务逻辑.事件的发布者无需关注订阅的侦听器如何实现业务逻辑,甚至不用关注有多少个侦听器存在.数据通过消息的方式可以灵活的传递. --& ...

  9. kafka 发布订阅_在Kafka中发布订阅模型

    kafka 发布订阅 这是第四个柱中的一系列关于同步客户端集成与异步系统( 1, 2, 3 ). 在这里,我们将尝试了解Kafka的工作方式,以便正确利用其发布-订阅实现. 卡夫卡概念 根据官方文件 ...

最新文章

  1. Python使用matplotlib可视化自定义背景色实战:自定义可视化图像的背景色(Background Color)
  2. 微信公众号开发之微信JSSDK
  3. 2016.11.17随笔
  4. 六、linux虚拟平台设备注册
  5. 鸡啄米vc++2010系列40(文档、视图和框架:分割窗口)
  6. linux配置串口不支持serial,linux 串口serial1和serial2不能用?
  7. etl工程师 面试题_数据仓库工程师面试题笔试.doc
  8. CallBack函数 回调函数
  9. 20200721:每日一题之不同的二叉搜索树 II(leetcode95)
  10. java mysql emoj报错_MySQL插入emoji表情报错 SQLException: Incorrect string value 的两种解决方案...
  11. Bailian4110 圣诞老人的礼物-Santa Clau’s Gifts【背包+贪心】
  12. 全球及中国创新药产业研发格局及应用价值分析报告2021-2027年
  13. android studio keymap eclipse,android studio eclipse keymap theme 快捷键 主题风格设置
  14. 批量cherry-pick
  15. Qt 常见错误及坑锦集(更新中....)
  16. 【基础版】大学计算机-计算思维导论
  17. AcWing 2019. 拖拉机(双端队列BFS)
  18. uva10935卡片游戏
  19. Visual Paradigm社区版安装教程
  20. 2020-04-19-如何在博客中添加视频链接

热门文章

  1. java 中几种常用数据结构
  2. 《太空帝国 4》(Space Empires IV)以及 xx-Mod 英文版 中文版 TDM Mod 英文版 中文版
  3. 2018年常用的7款Java框架
  4. Kubernetes--自动缩扩容
  5. 又一个短小精悍的软件包管理器-pnpm
  6. CVPR 2022 | 多快好省的高光谱图像重建
  7. 攻击法国海军病毒Conficker在中国网络同步蔓延
  8. 微信JSSDK多图片上传
  9. python程序员能做什么副业好_聊一聊程序猿该如何做副业
  10. skype客户端搜不到联系人解决办法