通讯基本靠吼——EventAggregator

阅读: 评论:0

通讯基本靠吼——EventAggregator

通讯基本靠吼——EventAggregator

俗语中,笑话一个地方穷,就经常说“通讯基本靠吼,交通基本靠走”。无独有偶,今天我们也要介绍一个类似于通讯基本靠吼的事件处理方式——EventAggregator。
 

传统事件处理方式

首先回想一下,我们怎么完成事件处理?简单的三步。

  1. 定义主体(被收听者)的暴露的事件类型。
  2. 在订阅者中定义事件处理程序并关联到主体。
  3. 主体触发事件。

非常简单的结构,如下所示。订阅者依赖于主体。

如果一个主体有多个订阅者,那么多个订阅者将会依赖于同一个主体。

如果情况再变化一下,多个主体有多个订阅者,那么依赖就会更加复杂。
看到这里,相信大家已经明白了,这种传统的事件处理模式有如下弊端。

  1. 订阅者依赖于主体,这样会造成模块之间的强耦合,而解耦是优秀设计一直追求的目标。

  2. 订阅者必须要得到主体的引用,才能订阅主体事件。在某些情况下这个要求很难达到,有时为了达到目的,引用会在几个对象之间不停传递。

为了克服这些弊端,我们来试试事件总线——EventAggregator。
 

事件总线

参考设计模式中中介者模式的设计思想,我们引入一个事件总线充当中间人,无论订阅者也好,主体也好,都只和这个事件总线打交道。这个总线再以广播的形式把事件给广播出去——这也是通讯基本靠吼这个调侃的由来——同时,它带来的好处显而易见,再也不需要仅仅是为了订阅事件而辛苦的维持主体引用了。

这样的好处在于,无论有多少订阅者和主体,订阅关系的处理全权委托给了事件总线。同时双方只依赖于事件总线,不再相互依赖。而事件总线在工程里面一般位于Infrastructure这种基础公共类库里面,处于上层模块的主体和订阅者依赖于Infrastructure并无不妥。以这种依赖为代价换取之前的主体模块和订阅者模块之间的依赖,对于程序结构也是一大进步。
 

一个例子

俗话说,光说不练假把式。让我们看一个例子,试着更深入的了解EventAggregator的好处。
 
设想一个游戏编辑器,有主界面,拥有地图选择器可以选择不同的世界,也可以切换不同的操作界面风格,而具体的地图编辑器,单元格编辑器则需要根据所选世界的不同而动态装载它们所呈现的内容。当然,编辑器们也需要对操作界面风格的改变做出响应,最后,所有这些改变都需要通知日志记录器。
 
可以想象,这一切如果用传统的事件模型会多么复杂,地图编辑器,单元格编辑和日志记录器需要依赖地图选择器和操作界面风格选择器,而且如果之后这些子编辑器有任何新的扩展,这些新类都需要依赖于选择器。
 
我们试着用EventAggregator来处理。
 
使用EventAggregator来处理事件订阅非常简单,只需如下几步。

  • 自定义或引用EventAggregator
    我们引用Prism.Core类库,该类库提供了EventAggregator的实现。

    加入命名空间声明。
using Prism;
using Prism.Events;
  • 定义事件
    我们定义出两个事件,分别为WorldChangedEvent和StyleChangedEvent。两个事件都继承自PubSubEvent。PubSubEvent是一个泛型类,可以在EventAggregator中被订阅和发布。
	public class World{public string WorldName { get; set; }}public enum StyleColor{red,pink}public class Style{public string StyleName { get; set; }public StyleColor Color { get; set; }}public class WorldChangedEvent : PubSubEvent<World> //PubSubEvent的泛型实现,定义了该事件发布的时候可以传递的参数{}public class StyleChangedEvent : PubSubEvent<Style>{}
  • 建造一个EventAggregator实例
    虽然Prism已经有EventAggregator类,我们仍然需要创造一个EventAggregator的实例以供使用,一般定义为单例。
   public class EventBus{private static Lazy<EventAggregator> _eventAggregator = new Lazy<EventAggregator>();public static EventAggregator Instance{get{return _eventAggregator.Value;}}}
  • 在订阅者中订阅事件
    在订阅者中使用Subscribe订阅事件,以单元格编辑器为例。
	public class CellEditor : IDisposable{Action _disposeAction = null;public CellEditor(){var token = EventBus.Instance.GetEvent<StyleChangedEvent>().Subscribe(OnStyleChanged);  //订阅_disposeAction = () => { EventBus.Instance.GetEvent<StyleChangedEvent>().Unsubscribe(token); };}public void Dispose() //实现IDispose的目的在于当类实例销毁的时候,记得取消订阅。{_disposeAction?.Invoke();}private void OnStyleChanged(Style changedStyle){}}
  • 事件主体发布事件
    当事件主体想要触发事件的时候,使用EventAggregator的Publish方法。
	public class WorldSelector{private World _currentWorld;public void ChangeWorld(World newWorld){_currentWorld = newWorld;EventBus.Instance.GetEvent<WorldChangedEvent>().Publish(newWorld);}}public class StyleSelector{private Style _currentStyle;public void ChangeStyle(Style newStyle){_currentStyle = newStyle;EventBus.Instance.GetEvent<StyleChangedEvent>().Publish(newStyle);}}

就这样简单几步,我们实现了事件总线模式,达成了代码类之间相互解耦的目的,同时,如果有新的编辑器加入,它们只需选择感兴趣的事件进行订阅,而无需先苦苦寻找主体引用。怎么样,是不是很神奇呢。
 

没有银弹

不过,就像那句软件行业的名言一样,没有银弹,虽然EventAggregator可以提供一些好处,但是仍然不要滥用,如果在涉及到的场景、事件的订阅者和主体之间的关系不是特别复杂的情况下,还是直接用传统事件模式的好。总结一下,EventAggregator的优缺点如下:

优点
  • 帮助解耦
  • 事件广播,易于添加订阅
缺点
  • 增加EventAggregator相当于添加了一个间接层,增加了软件复杂度
  • 在事件关系很单纯的情况下没必要把事件广播到整个系统

以上就是关于EventAggregator的简单介绍,希望对大家能有一些帮助!

本文发布于:2024-02-01 06:56:47,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170674180734712.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:通讯   EventAggregator
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23