Event Aggregator 模式定义:渠道事件从多个对象通过一个单一的对象来简化clients的注册。
结构图:
Prism的Event Aggregator 模式:Event Aggregator允许多对象定位和发布、订阅事件。
我们从Prism源代码中的EventAggregator和CompositePresentationEvent可以学习它是如何管理和传递事件消息的。
1 public interface IEventAggregator 2 { 3 TEventType GetEvent<TEventType>() where TEventType : EventBase, new(); 4 } 5 6 public class EventAggregator : IEventAggregator 7 { 8 private readonly Dictionary<Type, EventBase> events = new Dictionary<Type, EventBase>(); 9 10 public TEventType GetEvent<TEventType>() where TEventType : EventBase, new() 11 { 12 EventBase existingEvent = null; 13 14 if (!this.events.TryGetValue(typeof(TEventType), out existingEvent)) 15 { 16 TEventType newEvent = new TEventType(); 17 this.events[typeof(TEventType)] = newEvent; 18 19 return newEvent; 20 } 21 else 22 { 23 return (TEventType)existingEvent; 24 } 25 } 26 } 27 28 public class CompositePresentationEvent<TPayload> : EventBase 29 { 30 private IDispatcherFacade uiDispatcher; 31 32 private IDispatcherFacade UIDispatcher 33 { 34 get 35 { 36 if (uiDispatcher == null) 37 { 38 this.uiDispatcher = new DefaultDispatcher(); 39 } 40 41 return uiDispatcher; 42 } 43 } 44 45 public SubscriptionToken Subscribe(Action<TPayload> action) 46 { 47 return Subscribe(action, ThreadOption.PublisherThread); 48 } 49 50 public SubscriptionToken Subscribe(Action<TPayload> action, ThreadOption threadOption) 51 { 52 return Subscribe(action, threadOption, false); 53 } 54 55 public SubscriptionToken Subscribe(Action<TPayload> action, bool keepSubscriberReferenceAlive) 56 { 57 return Subscribe(action, ThreadOption.PublisherThread, keepSubscriberReferenceAlive); 58 } 59 60 public SubscriptionToken Subscribe(Action<TPayload> action, ThreadOption threadOption, bool keepSubscriberReferenceAlive) 61 { 62 return Subscribe(action, threadOption, keepSubscriberReferenceAlive, null); 63 } 64 65 public virtual SubscriptionToken Subscribe(Action<TPayload> action, ThreadOption threadOption, bool keepSubscriberReferenceAlive, Predicate<TPayload> filter) 66 { 67 IDelegateReference actionReference = new DelegateReference(action, keepSubscriberReferenceAlive); 68 IDelegateReference filterReference; 69 if (filter != null) 70 { 71 filterReference = new DelegateReference(filter, keepSubscriberReferenceAlive); 72 } 73 else 74 { 75 filterReference = new DelegateReference(new Predicate<TPayload>(delegate { return true; }), true); 76 } 77 EventSubscription<TPayload> subscription; 78 switch (threadOption) 79 { 80 case ThreadOption.PublisherThread: 81 subscription = new EventSubscription<TPayload>(actionReference, filterReference); 82 break; 83 case ThreadOption.BackgroundThread: 84 subscription = new BackgroundEventSubscription<TPayload>(actionReference, filterReference); 85 break; 86 case ThreadOption.UIThread: 87 subscription = new DispatcherEventSubscription<TPayload>(actionReference, filterReference, UIDispatcher); 88 break; 89 default: 90 subscription = new EventSubscription<TPayload>(actionReference, filterReference); 91 break; 92 } 93 94 95 return base.InternalSubscribe(subscription); 96 } 97 98 public virtual void Publish(TPayload payload) 99 { 100 base.InternalPublish(payload); 101 } 102 103 public virtual void Unsubscribe(Action<TPayload> subscriber) 104 { 105 lock (Subscriptions) 106 { 107 IEventSubscription eventSubscription = Subscriptions.Cast<EventSubscription<TPayload>>().FirstOrDefault(evt => evt.Action == subscriber); 108 if (eventSubscription != null) 109 { 110 Subscriptions.Remove(eventSubscription); 111 } 112 } 113 } 114 115 public virtual bool Contains(Action<TPayload> subscriber) 116 { 117 IEventSubscription eventSubscription; 118 lock (Subscriptions) 119 { 120 eventSubscription = Subscriptions.Cast<EventSubscription<TPayload>>().FirstOrDefault(evt => evt.Action == subscriber); 121 } 122 return eventSubscription != null; 123 } 124 125 }
- 订阅
- 取消订阅
- 发布
- 比较定位
简单用法:
1 public class Model{ 2 private readonly IEventAggregator eventAggregator; 3 4 this.eventAggregator = eventAggregator; 5 this.eventAggregator.GetEvent<UpdatedEvent>().Subscribe(this.Updated, ThreadOption.UIThread); 6 7 private void Updated() 8 { 9 //Todo Something 10 } 11 12 } 13 14 public class UpdatedEvent: CompositePresentationEvent<IDictionary<string, decimal>> 15 { 16 }
参考资料:
http://www.martinfowler.com/eaaDev/EventAggregator.html
接[从Prism中学习设计模式之Event Aggregator模式]一文,上文中从源码的角度分析了Prism中EventAggregator的实现。
Lz想通过本文再深入谈下EventAggregator,将自己对Prism项目组的设计意图的理解做下记录,并希望和其他对Prism有兴趣的兄弟一起探讨。
对于Prism的设计团队来说,设计EventAggregator肯定是经过一番详细考虑的,不会像我们平时在项目中图怎么简单怎么偷懒怎么来~[耻远了]。通过对Prism的源码进行阅读,给Lz一个很强的感觉就是Prism大量运用了设计模式和领域设计的理念,随着对源码理解的深入,这种感觉越来越强,每个设计都可以在软件工程设计理论中找到理论依据^-^。
好了,开篇结束,下面进入正题,了解EventAggregator前,我先来认识一个名词Aggregator。
Aggregator,中文含义:聚合。这是DDD设计中一个非常重要的概念。
参考:Eric Evans-Domain-Driven Design 一书中的定义:
聚合(Aggregator)是一组领域(Domain)对象,包括实体(Entity Object)和值对象(Value Object)。这组(Group)Domain Object的组合(Union)来描述一个Full Domain Model。
在实际应用中,Domain模型中不是每个Entity Object都能描述一个完整的领域概念。举例就拿GBTouch项目中的会议与议程的关系来说,系统需要为每个会议维护多个议程,此时议程是一个Entity实体,而不是值对象。这样Domain模型就存在会议和议程两个实体对象,而事实,议程对象离开会议对象没有任何的实际意义,议程对象依附于会议对象,完整的表达了“会议可以有多个议程,并对这些议程进行维护”的思想,而会议即为议程的聚合根(Aggregator Root)。同理,会议与表决项的关系也是一样的概念。
回归正题,每个聚合都有一个根实体即为聚合根,这个根实体所表述一个Domain概念的主题,外部对象需要访问聚合内实体时,只能通过该聚合根访问。聚合确定了实体生命周期的关注范围。
--这里引出的Domain对象的生命周期问题不是本文的讨论范围,大家可以google相关内容。
我们来看下Prism是不是实现了聚合的概念,果不其然,在命名空间Microsoft.Practices.Prism.Events
Prism定义了IEventAggregator接口,接口中定义了GetEvent方法
TEventType GetEvent<TEventType>() where TEventType : EventBase, new();
IEventAggregator接口对基于EventAggregator的实现进行了约束。泛型TEventType继承一个抽象类EventBase,EventBase包含了基本事件的行为抽象。
这样就完成了一个经典的DDD设计。
我们看下Prism提供的Demo StrokeUI, MockEventAgrregator(实则为一个聚合根)通过实现IEventAggregator对其进行约束,MockPriceUpdatedEventAggregator定义了一个价格变更事件:
1 class MockEventAggregator : IEventAggregator 2 { 3 Dictionary<Type, object> events = new Dictionary<Type, object>(); 4 public TEventType GetEvent<TEventType>() where TEventType : EventBase, new() 5 { 6 return (TEventType)events[typeof(TEventType)]; 7 } 8 9 public void AddMapping<TEventType>(TEventType mockEvent) 10 { 11 events.Add(typeof(TEventType), mockEvent); 12 } 13 }
1 class MockPriceUpdatedEventAggregator : MockEventAggregator 2 { 3 public MockMarketPricesUpdatedEvent MockMarketPriceUpdatedEvent = new MockMarketPricesUpdatedEvent(); 4 public MockPriceUpdatedEventAggregator() 5 { 6 AddMapping<MarketPricesUpdatedEvent>(MockMarketPriceUpdatedEvent); 7 } 8 9 public class MockMarketPricesUpdatedEvent : MarketPricesUpdatedEvent 10 { 11 public bool PublishCalled; 12 public IDictionary<string, decimal> PublishArgumentPayload; 13 public EventHandler PublishCalledEvent; 14 15 private void OnPublishCalledEvent(object sender, EventArgs args) 16 { 17 if (PublishCalledEvent != null) 18 PublishCalledEvent(sender, args); 19 } 20 21 public override void Publish(IDictionary<string, decimal> payload) 22 { 23 PublishCalled = true; 24 PublishArgumentPayload = payload; 25 OnPublishCalledEvent(this, EventArgs.Empty); 26 } 27 } 28 }
单元测试实现:
1 [TestMethod] 2 public void PublishedEventContainsTheUpdatedPriceList() 3 { 4 var eventAgregator = new MockPriceUpdatedEventAggregator(); 5 var marketFeed = new TestableMarketFeedService(eventAgregator); 6 Assert.IsTrue(marketFeed.SymbolExists("STOCK0")); 7 8 marketFeed.InvokeUpdatePrices(); 9 10 Assert.IsTrue(eventAgregator.MockMarketPriceUpdatedEvent.PublishCalled); 11 var payload = eventAgregator.MockMarketPriceUpdatedEvent.PublishArgumentPayload; 12 Assert.IsNotNull(payload); 13 Assert.IsTrue(payload.ContainsKey("STOCK0")); 14 Assert.AreEqual(marketFeed.GetPrice("STOCK0"), payload["STOCK0"]); 15 }
好了,通过本文,大家应该对Prism中EventAggregator基于DDD的设计有所概念,其实关于EventAggregator还涉及了CQRS的设计概念,Lz将另外开篇讲述。
在园子里和Google上看到很多朋友都把Prism的EventAggregator用于WPF的UI层面,我想通过本文阐述EventAggregator于WPF的前端UI交互没有任何关系。
推荐的用法:
1.WPF UIControl Event通过Event to Command
Prism: use CommandBehaviorsBase 实现 event to command
MVVMLight: use i:Interaction.Triggers 实现 event to command(framework 4.0以上引用System.Windows.Interactivity.dll)
2.Command
Prism: DelegateCommand
MVVMLight:ReleyCommand
3.Command trigger domain Layer
domain Layer实现domain model的行为和状态。
至于ORM,持久化,NoSQL,不是Prism考虑的问题,Prism已实现对外部组件的抽象。至于你用NHibernate,EF,MongoDB还是CouchDB基于Repository,是Infrastructure Layer考虑的问题,也是技术选型和技术架构问题,不是DDD设计的概念。
DDD Sytem Architecture Layer:[自底向上]
- Infrastructure
- Domain
- Apllication
- Presentation
后记:
关于EventAggregator涉及的Event-Sourceing概念,现在有个很不错的框架可供参考。
Open Source Web:http://geteventstore.com/
Git Link:https://github.com/eventstore/eventstore/wiki
Nuget: Event Stroe