理解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

 

 

posted @ 2013-05-26 00:25  老鱼_678  阅读(2737)  评论(0编辑  收藏  举报