从Prism中学习设计模式之Event Aggregator 模式

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值