理解MVVMLight—Laurent Bugnion的设计思想——〉Service Locator Pattern
目前Team在写WPF项目的时候,往往设计一个Business Manager类,它是一个Singleton,用来处理一些特定的业务,其实可以理解为Mvc中的Control,也可以理解为一个Service。当业务逻辑的需求需要应用程序的View上交互动作操作,View调用Service来完成特定的任务,如点击PhotoApp,PhotoApp调用Sevice的加载图片方法加载View所需的图片集合。通常最简单的办法是在App Load或者初始化时new创建一个Service对象,或者再高级一点,使用Factory Pattern来Decoupling(解耦)View与Serivce的具体实现部分,通过Config方便的获取该Serivce的实例。然而上述两种做法都有各自的缺陷,下面列述:
1.在View中直接维护对Serivce的引用,伪代码:
1 this.Load += Onload; 2 3 private void Onload(object sender,EvenetArgs e){ 4 Service.Instance.Open(); 5 }
上面是我们项目中经常见到的写法,这样直接造成View与Service之间的依赖关联,当需要替换服务的实现时,必须修改View中调用Service的部分并重新编译Solution。即使采用Factory Pattern来通过Config动态获取Service实例,也无法针对不同的Service向View提供View所需的服务实例。
2.由于这种依赖关联,使得项目的开发过程受到约束。在实际开发过程中,View开发和Service开发可能是同步进行的,很有可能当View需要调用Service时,Service还没开发完。遇到这种问题,通常先把坑留着,或者自己用Proxy Pattern写个Demo方法,等Service完成后再集成,这种做法不仅费时,增加了合成风险,也使责任不明,更加增加了出错风险提升了测试的复杂度。
3.针对View的Unit Test变得十分复杂,如果要做Unit Test,无法使用Service Stub来解决View与Service的依赖关系。
4.在View的Code-Behind中可能存在多处Call Service的Instance,在这种情况下,Service.Instance.Method会散步于整个应用程序中,造成一段代码存在多个副本,几何级的增加了维护和排错的成本。
5.当View需要Call多个Service时,不同Service 初始化各自的Instance的方式可能存在差异,Coder必须了解所有Service的初始化的API,才能在代码中正确使用这些Service。
6.某些Service的初始化过程需要耗费大量资源,多次重复初始化,大大增加应用程序的资源占用和性能消耗,项目中需要一个管理Service初始化过程的机制,在统一初始化接口的同时,需要为应用程序提供部分缓存功能,如文稿页面,相册图片等。
Laurent Bugnion所写的轻量级框架MVVMLight使用Service Locator Pattern来解决上述的问题。
Service Locator Pattern(服务定位器模式)
它能够为应用程序中Service的创建和初始化提供一个定位,并解决了上文中所提到的各种设计和开发问题。Service Locator Pattern主要有以下几种参与者:
1.Service:
Service是Service Locator需要返回给调用方的Instance。比如Service Locator(ViewModel Locator)可以根据调用方的需要,View从Service(ViewModel)从网络获取信息,或者View从Service(ViewModel)从本地文件系统获取信息。在这种情形下,这两种Service可能会有着不同的接口:对于前者,它只需要接收一个参数(即需要信息列表)就可以完成获取任务;而对于后者而言,它不仅要获得信息列表,而且还要获得一个正确的文件路径。因此,在实际应用中,我们通常会为不同的Service类型设计不同的接口,而Service Locator则应该根据调用方给定的Service Type,返回相应的Service Instance。
2.Service Factory
Service Factory是Factory Pattern的一种实现,它的职责是创建并初始化某种类型的Service。不同的Service Type有其特定的Service Factory,在实际应用中,Service Factory厂与一个特定的Service Intface所对应。使用Service Factory不仅可以Coupling Service的定义部分和具体实现部分,应用程序无需重新编译即可变更Service的不同实现方式,而且对于初始化过程需要消耗大量资源的Service而言,Service Factory还能够提供缓存功能,从而提高应用程序的性能。
3.Initial Context
由于不同的Service需要由不同的Service Factory New和实现Initialization,因此对于Service Locator来说,还需要一个特定的Locator来统一管理这些Service Factory,Initial Context就充当了这个角色。在调用方向Service Locator请求一个Service Instance时,Service Locator通过InitialContext获得Service Factory的Instance,然后由Service Factory new service并返回给调用方。使用Initial Context的优点是,它简化了Service Locator的职责,并为Service Factory的管理和缓存提供了有力保障。
4.Service Locator
Service Locator为调用方获得所需Service的Instance提供了定位。
Service Locator Pattern 的类图和时序图请参考Wiki,下面给出的参考资料链接中有,这里就不再引用。
MVVMLight 实现:
1.ViewModelLocator(Service Locator)
1 <?xml version="1.0" encoding="utf-8"?> 2 <Application x:Class="WpfApplication1.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="MainWindow.xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vm="clr-namespace:WpfApplication1.ViewModel" mc:Ignorable="d"> 3 <Application.Resources> 4 <ResourceDictionary> 5 <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" /> 6 <ResourceDictionary.MergedDictionaries></ResourceDictionary.MergedDictionaries> 7 </ResourceDictionary> 8 </Application.Resources> 9 </Application>
2. SimpleIoc (Initial Context)
1 using GalaSoft.MvvmLight; 2 using GalaSoft.MvvmLight.Ioc; 3 using Microsoft.Practices.ServiceLocation; 4 5 namespace WpfApplication1.ViewModel 6 { 7 /// <summary> 8 /// This class contains static references to all the view models in the 9 /// application and provides an entry point for the bindings. 10 /// </summary> 11 public class ViewModelLocator 12 { 13 /// <summary> 14 /// Initializes a new instance of the ViewModelLocator class. 15 /// </summary> 16 public ViewModelLocator() 17 { 18 ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default); 19 20 ////if (ViewModelBase.IsInDesignModeStatic) 21 ////{ 22 //// // Create design time view services and models 23 //// SimpleIoc.Default.Register<IDataService, DesignDataService>(); 24 ////} 25 ////else 26 ////{ 27 //// // Create run time view services and models 28 //// SimpleIoc.Default.Register<IDataService, DataService>(); 29 ////} 30 31 SimpleIoc.Default.Register<MainViewModel>(); 32 } 33 34 public MainViewModel Main 35 { 36 get 37 { 38 return ServiceLocator.Current.GetInstance<MainViewModel>(); 39 } 40 } 41 42 public static void Cleanup() 43 { 44 // TODO Clear the ViewModels 45 } 46 }
3.MainWindow.DataContext Binding MainViewModel
是为View作为调用方引用Service的实例
具体使用案例:
1.CodeProject:http://www.codeproject.com/Articles/297624/Implementing-MVVM-Light-with-Structure-Map
2.GBTouch Team:参考VoteApp实现
参考资料:
Deep Dive MVVM: http://channel9.msdn.com/events/MIX/MIX11/OPN03
MVVMLight:http://www.galasoft.ch/mvvm/
Service Locator Pattern(Wiki) http://en.wikipedia.org/wiki/Service_locator_pattern
Service Locator Pattern(MSDN)http://msdn.microsoft.com/en-us/library/ff648968.aspx
Dependency Injection(MSDN)http://msdn.microsoft.com/en-us/library/dd458879.aspx
IOC[Inversion of Control]http://msdn.microsoft.com/en-us/library/dd458907.aspx