观察者模式是一种行为设计模式, 允许你定义一种订阅机制, 可在对象事件发生时通知多个 “观察” 该对象的其他对象。
-
“订阅者”:将自己希望执行的方法在事件发布之前注册到发布者中。
-
“发布者”:事件的发布(触发)。
“发布事件”这种描述实际上描述的是调用Publisher#notifySubscribers
这个函数所代表的意义。但在实际写代码时,我们考虑的是这个函数的调用时机和调用后产生的结果。
何时使用
- 当一个对象状态的改变需要改变其他对象, 或实际对象是事先未知的或动态变化的时, 可使用观察者模式。
实现方法
-
定义订阅者容器/发布者
-
订阅者向订阅者容器注册(将订阅者的方法注册给订阅者容器)
-
事件发生/事件通知/通知观察者/观察者做出行为(第三步有很多描述方式,本质是通过订阅者容器所持有的所有订阅者的引用执行它们的方法)
在观察者注册之前,就需要事件存在。相比多个单例事件,可以创建单例的事件管理类,在事件管理类中通过字典的方式保存事件类型及其对应的订阅者引用,例如Prism
框架中的EventAggregator
。
观察者模式的结构
形式1 发布-订阅
-
在使用观察者之前,
Publisher
的实例需要存在,一般在实际项目中通过依赖注入容器并结合构造函数注入来管理。 -
“观察者”从名称上来看像是“主动进行观察动作”的一方,但实际上它却是“被通知”的一方,只有“注册”动作是主动的。
-
首先在观察者类型上实现接口,然后将观察者的引用通过发布者的
subscribe
方法添加到发布者的内部观察者集合中,如上图的数组。最后在需要的地方通过发布者来通知观察者做出行为(通过注册在观察者中的引用来调用接口Subscriber
定义的update
方法)。
形式2 事件管理者-发布-订阅
结合Prism.Events.EventAggregator
来分析
-
与发布-订阅模型不同,在事件管理者-发布-订阅模型中,观察者模式的几个组件被进一步解耦了:
- 通过事件管理者统一对事件进行管理
- 发布者由形式1中的接口被事件的概念(
EventBase
)代替了,本质仍然是一个订阅者容器(List<IeventSubscription>
) - 相比形式1,订阅者不再需要实现订阅者接口而是通过
Action
类直接在注册时把需要执行的方法传入,避免了耦合
-
在事件管理者
EventAggregator
中,仅有一个GetEvent
方法,每次注册和发布时都通过这个方法来获取指定类型的事件,在首次获取事件时会创建它的实例,并将这个事件的引用保存在字典events
中。
使用EventAggregator
的示例代码:
// 扩展方法,方便使用
public static class EventAggregatorExtensions
{
public static void PublishLoading(this IEventAggregator aggregator, bool opening)
{
// 调用所有注册在这上的方法对象
aggregator.GetEvent<LoadingEvent>().Publish(opening);
}
public static void RegisterLoading(this IEventAggregator aggregator, Action<bool> action)
{
// 注册到Event实例上,Event中有个订阅者List
aggregator.GetEvent<LoadingEvent>().Subscribe(action);
}
}
// 在主视图注册加载函数
public MainView(IEventAggregator aggregator, IDialogService dialogService)
{
InitializeComponent();
aggregator.RegisterLoading(Loading);
// ...
}
// 在需要的地方调用,比如进入节点加载时
public class NavigationViewModel : BindableBase, INavigationAware
{
public async void Init(Func<Task> action)
{
PublishLoading(true);
await action.Invoke();
PublishLoading(false);
}
// ...
}