1. 引言
事件总线这个概念对你来说可能很陌生,但提到观察者(发布-订阅)模式,你也许就很熟悉。事件总线是对发布-订阅模式的一种实现。它是一种集中式事件处理机制,允许不同的组件之间进行彼此通信而又不需要相互依赖,达到一种解耦的目的。
我们来看看事件总线的处理流程:
了解了事件总线的基本概念和处理流程,下面我们就来分析下如何去实现事件总线。
2.回归本质
在动手实现事件总线之前,我们还是要追本溯源,探索一下事件的本质和发布订阅模式的实现机制。
2.1.事件的本质
我们先来探讨一下事件的概念。都是读过书的,应该都还记得记叙文的六要素:时间、地点、人物、事件(起因、经过、结果)。
我们拿注册的案例,来解释一下。
用户输入用户名、邮箱、密码后,点击注册,输入无误校验通过后,注册成功并发送邮件给用户,要求用户进行邮箱验证激活。
这里面就涉及了两个主要事件:
注册事件:起因是用户点击了注册按钮,经过是输入校验,结果是是否注册成功。
发送邮件事件:起因是用户使用邮箱注册成功需要验证邮箱,经过是邮件发送,结果是邮件是否发送成功。
其实这六要素也适用于我们程序中事件的处理过程。开发过WinForm程序的都知道,我们在做UI设计的时候,从工具箱拖入一个注册按钮(btnRegister),双击它,VS就会自动帮我们生成如下代码:
void btnRegister_Click(object sender, EventArgs e){ // 事件的处理}
其中object sender
指代发出事件的对象,这里也就是button对象;EventArgs e
事件参数,可以理解为对事件的描述 ,它们可以统称为事件源。其中的代码逻辑,就是对事件的处理。我们可以统称为事件处理。
说了这么多,无非是想透过现象看本质:事件是由事件源和事件处理组成。
2.2. 发布订阅模式
定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。 ——发布订阅模式
发布订阅模式主要有两个角色:
发布方(Publisher):也称为被观察者,当状态改变时负责通知所有订阅者。
订阅方(Subscriber):也称为观察者,订阅事件并对接收到的事件进行处理。
发布订阅模式有两种实现方式:
简单的实现方式:由Publisher维护一个订阅者列表,当状态改变时循环遍历列表通知订阅者。
委托的实现方式:由Publisher定义事件委托,Subscriber实现委托。
总的来说,发布订阅模式中有两个关键字,通知和更新。
被观察者状态改变通知观察者做出相应更新。
解决的是当对象改变时需要通知其他对象做出相应改变的问题。
如果画一个图来表示这个流程的画,图形应该是这样的:
3 实现发布订阅模式
相信通过上面的解释,对事件和发布订阅模式有了一个大概的印象。都说理论要与实践相结合,所以我们还是动动手指敲敲代码比较好。
我将以『观察者模式』来钓鱼这个例子为基础,通过重构的方式来完善一个更加通用的发布订阅模式。
先上代码:
/// <summary>/// 鱼的品类枚举/// </summary>public enum FishType
{
鲫鱼,
鲤鱼,
黑鱼,
青鱼,
草鱼,
鲈鱼
}
钓鱼竿的实现:
/// <summary>
/// 鱼竿(被观察者)
/// </summary>
public class FishingRod
{ public delegate void FishingHandler(FishType type); //声明委托
public event FishingHandler FishingEvent; //声明事件
public void ThrowHook(FishingMan man) {
Console.WriteLine("开始下钩!");
//用随机数模拟鱼咬钩,若随机数为偶数,则为鱼咬钩
if (new Random().Next() % 2 == 0)
{ var type = (FishType) new Random().Next(0, 5);
Console.WriteLine("铃铛:叮叮叮,鱼儿咬钩了"); if (FishingEvent != null)
FishingEvent(type);
}
}
}
垂钓者:
/// <summary>/// 垂钓者(观察者)/// </summary>public class FishingMan{ public FishingMan(