目前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 ViewModelLocator12 {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 models23 //// SimpleIoc.Default.Register<IDataService, DesignDataService>();24 ////}25 ////else26 ////{27 //// // Create run time view services and models28 //// SimpleIoc.Default.Register<IDataService, DataService>();29 ////}30 31 SimpleIoc.Default.Register<MainViewModel>();32 }33 34 public MainViewModel Main35 {36 get37 {38 return ServiceLocator.Current.GetInstance<MainViewModel>();39 }40 }41 42 public static void Cleanup()43 {44 // TODO Clear the ViewModels45 }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