WinPhone开发中的IOC MSDN

从早期的面向对象编程,开发人员面临的问题类的创建和检索实例的应用程序和库。各种解决方案已经提出了这个问题。在过去的几年里,依赖注入(DI)和控制反转(IOC)流行在程序员和已经优先于一些老Singleton模式等解决方案。

单例模式是一种方便的方法来创建和暴露一个类的实例,但它也有一些缺点。例如,图1显示了一个类中的代码将一个属性后,单例模式。

图1所示。单例模式的实现

public class DataService
{
  private static DataService _instance;
  public static DataService Instance
  {
    get
    {
      return _instance ?? (_instance = new DataService());
    }
  }
}

这有几件事要注意的代码如图1所示:

  • 类的构造函数是私有的,这意味着创建实例的唯一方法是使用实例静态属性。我们可以改变,通过内部甚至公共构造函数。
  • 对需求的实例被创建,这通常是一件好事,但有时我们希望尽快准备好应用程序实例开始。我们可以通过调用实例属性尽快。
  • 更讨厌,没有办法删除实例,在某些情况下,这可能会导致内存泄漏。解决这个问题的一个方法是添加一个静态的删除方法的类。
  • 可以创建实例以外的主要实例属性,但每个需要不同的访问器,属性或方法。实例属性不允许任何参数被传递给构造函数。

这些改进和其他问题,我们可以将这种模式转换为有用的东西,并增加灵活性。但是一个更清洁的方法是删除从每个类我们实现这个基础设施代码,而是使用一个外部的对象,就像一个缓存实例,我们需要在我们的应用程序的各个部分。

这就是IOC容器中派上用场。控制反转这个词意味着创建和保持的行为实例没有消费阶层的责任了,因为它是在传统的面向对象编程,而是委托给外部容器。虽然这不是一种义务,缓存实例往往注入消费者类的构造函数或通过消费者的属性。这就是为什么我们谈论依赖注入,或DI。注意,依赖注入不需要IOC容器才能作,但这是一个方便的方式将消费者从从缓存中缓存实例和本身。

例如,一个典型的应用程序将组成如图2所示。在这个例子中,程序员决定提供服务的两种实现,一个用于运行时和一个用于测试目的。在某些情况下,开发人员可能希望提供第三个实现设计时数据,用于Expression Blend或Visual Studio设计师。

图2。消费者和服务的经典组合

public class Consumer
{
  private IDataService _service;
  public Consumer()
  {
    if (App.IsTestMode)
    {
      _service = new TestDataService();
    }
    else
    {
      _service = new DataService();
    }
  }
}
public interface IDataService
{
  Task<DataItem> GetData();
}
public class DataService : IDataService
{
  public async Task<DataItem> GetData()
  {
    // TODO Provide a runtime implementation
    // of the GetData method.
    // ...
  }
}
public class TestDataService : IDataService
{
  public async Task<DataItem> GetData()
  {
    // TODO Provide a test implementation
    // of the GetData method.
    // ...
  }
}

与依赖注入,代码变得更加简洁,如图3所示。这是DI的核心原则:另一个类中,某个地方,是创建正确的照顾服务的实现,并注入。

图3。依赖注入

public class ConsumerWithInjection
{
  private IDataService _service;
  public ConsumerWithInjection(IDataService service)
  {
    _service = service;
  }
}

当然,我们需要照顾创建服务并将它注入到消费者,而这就是IOC容器变得有用。

使用IOC容器

相当多的IOC容器市场上是可用的。例如,统一(微软),StructureMap和温莎城堡(两个开源项目)很受欢迎。在网店IOC容器,可用于多种平台。在这篇文章中,我将使用MVVM光的SimpleIoc说明MVVM-based的IOC容器应用程序的有效性。

注意:本文提供的示例应用程序展示了所有的技术详细使用MVVM光和SimpleIoc容器。应用程序是一个简单的RssReader,将从CNN.com的最新文章列表并显示它们。这个应用程序有两个页面:主页显示了标题的文章列表。点击,应用DetailsPage导航,显示文章的标题,摘要,以及主要的故事在CNN网站上的链接。示例包包含两个实现的应用程序,一个用于Windows 8,另一个用于Windows Phone 8。

SimpleIoc就好像这个名字是一个相当简单的IOC容器,它允许注册和从缓存中以简单的方式获得实例。它还允许组合与依赖注入对象的构造函数。IDataService SimpleIoc,你可以注册,实现类或类和消费阶层,如图4所示。

图4。注册IDataService和消费者

if (App.IsTestMode)
{
  SimpleIoc.Default.Register<IDataService, TestDataService>();
}
else
{
  SimpleIoc.Default.Register<IDataService, DataService>();
}
SimpleIoc.Default.Register<ConsumerWithInjection>();

在明文,图4中的代码意味着:如果应用程序在测试模式下,每次有人需要一个IDataService,通过TestDataService的缓存实例;否则,使用DataService的缓存实例。注意,注册的作用并不创建任何实例的鲜草实例化需求,执行,只有当对象是实际需要。

下一步是创建ConsumerWithInjection实例代码,如图5所示。

图5。让ConsumerWithInjection实例

public ConsumerWithInjection ConsumerInstance
{
  get
  {
    return SimpleIoc.Default.GetInstance<ConsumerWithInjection>();
  }
}

当属性在图5中,SimpleIoc容器将运行以下步骤:

  1. ConsumerWithInjection检查实例是否已经存在的缓存。如果是,返回的实例。
  2. 如果实例不存在,检查ConsumerWithInjection的构造函数。它需要IDataService的实例。
  3. 检查是否IDataService的实例已经在缓存中可用。如果是的,通过ConsumerWithInjection的构造函数。
  4. 缓存IDataService如果没有的话,创建一个实例,并将其传递给ConsumerWithInjection的构造函数。

当然,因为缓存实例,调用GetInstance方法可以多次执行应用程序的各个部分。它并不是一定需要使用构造函数注入早些时候显示在图3中,尽管它是一个优雅的方式来组合对象和将对象之间的依赖关系。

GetInstance方法也可以返回的实例。这意味着IOC容器可以创建同一个类的多个实例,让他们与一个关键索引。那样的IOC容器充当缓存,以及实例上创建需求:当调用GetInstance关键,IOC容器检查是否这个类的一个实例已经保存的关键。如果不是,在返回之前创建的实例。然后在缓存中保存,以备重用。还可以获得给定类的所有实例,如图6所示。最后一行的代码返回IEnumerable < ConsumerWithInjection >包含先前创建的四个实例。

图6。获得的情况下,以及所有的实例

// Default instance
var defaultInstance = SimpleIoc.Default.GetInstance<ConsumerWithInjection>();
// Keyed instances
var keyed1 = SimpleIoc.Default.GetInstance<ConsumerWithInjection>("key1");
var keyed2 = SimpleIoc.Default.GetInstance<ConsumerWithInjection>("key2");
var keyed3 = SimpleIoc.Default.GetInstance<ConsumerWithInjection>("key3");
// Get all the instances (four)
var allInstances = SimpleIoc.Default.GetAllInstances<ConsumerWithInjection>();

各种方法来注册一个类

一个IOC容器的最有趣的特性是一个类的方法可以注册为了创建实例。每个IOC容器都有某些特性,使其独特的注册类。其中的一些使用-代码配置,而其他人则可以读取外部XML文件,允许灵活的类是由容器实例化。其他人允许强大的工厂使用。比如MVVM光的SimpleIoc,更简单明了。使用哪个IOC容器是一种决定,取决于几个条件,比如团队的熟悉特定的容器,应用程序所需的功能等等。

注册可以发生在一个中央位置(通常称为服务定位器),可以采取重要的决定,比如当使用测试实现的所有服务。当然,也有可能(通常是必要的)注册一些类应用程序中其他地方。

在一些MVVM应用程序(特别是基于MVVM应用光工具包),类名为ViewModelLocator用于创建和公开的一些应用程序的视图模型。这是一个方便的位置,注册的服务和服务消费者。事实上,一些视图模型也可以注册IOC容器。在大多数情况下,只有长期的视图模型在ViewModelLocator类注册。其他人可能会临时创建的。在导航软件,如Windows 8或Windows Phone应用,这些实例可能传递给页面导航发生时。在某些情况下,SimpleIoc可能用作键缓存实例为了简化这一步,我们将在本文的后面。

使IOC容器更容易交换与另一个(如有必要),许多这样的容器(包括MVVM光SimpleIoc)使用公共服务定位器实现。这依赖于一个通用接口(IServiceLocator)和ServiceLocator类,用于抽象的IOC容器的实现。因为SimpleIoc实现IServiceLocator,我们可以在我们的应用程序中,图7所示的代码中执行相同的方式如图6中的代码。如果在以后点另一个IOC容器是为应用程序选择,只有引用SimpleIoc类需要交换。ServiceLocator的引用,另一方面,可以保持。

图7。注册ServiceLocator

ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
// Default instance
var defaultInstance = ServiceLocator.Current.GetInstance<ConsumerWithInjection>();
// Keyed instances
var keyed1 = ServiceLocator.Current.GetInstance<ConsumerWithInjection>("key1");
var keyed2 = ServiceLocator.Current.GetInstance<ConsumerWithInjection>("key2");
var keyed3 = ServiceLocator.Current.GetInstance<ConsumerWithInjection>("key3");
// Get all the instances (four)
var allInstances = ServiceLocator.Current.GetAllInstances<ConsumerWithInjection>();

而不是登记类和委托实例创建IOC容器,也可以注册一个工厂的表情。这代表(通常表示为一个lambda表达式)返回一个实例。因为代表可以包含任何逻辑,可以执行一些复杂的创建代码如果需要,甚至还创建一个实例,在另一个应用程序的一部分。请注意,在需要的时候工厂只是计算表达式的值。

图8中的代码,例如,显示了如何注册一个DataItem接受一个DateTime作为构造函数参数。时,因为构造函数只执行调用GetInstance(而不是调用注册时),该参数将准确地显示的时间被称为第一次工厂代码。然而,随后的调用GetInstance将显示在同一时间,因为已经创建并缓存实例。

图8。注册一个工厂

public async void InitiateRegistration()
{
  // Registering at 0:00:00
  SimpleIoc.Default.Register(() => new DataItem(DateTime.Now));
  Debug.WriteLine("Registering at " + DateTime.Now);
  await Task.Delay(5000);
  // Getting at 0:00:05
  var item = ServiceLocator.Current.GetInstance<DataItem>();
  Debug.WriteLine("Creating at " + item.CreationTime);
  await Task.Delay(5000);
  // Getting at 0:00:10. Creation time is still the same
  item = ServiceLocator.Current.GetInstance<DataItem>();
  Debug.WriteLine("Still the same creation time: " + item.CreationTime);
}

MVVM光的ViewModelLocator

当MVVM光安装(使用MSI在http://mvvmlight.codeplex.com/,在下载部分),可以创建一个新的应用程序在Visual Studio使用提供的项目模板。例如,下面的步骤创建一个新的Windows 8(WinRT)应用:

  1. 安装MVVM光。
  2. 使用Readme文件中自动打开您喜欢的浏览器,安装VSIX文件
  3. Visual Studio 2012启动(或重新启动)。
  4. 选择文件,新项目。
  5. 在Visual c#,窗户商店,选择MvvmLight(Win8中的)项目模板,输入一个项目名称和位置并单击OK。

注意,MVVM光支持所有XAML-based框架(Windows Presentation Foundation,Silverlight,Windows Phone,Windows 8),所以,可以复制这些框架。一旦创建了项目,ViewModelLocator打开文件。cs ViewModel文件夹中。注意图9中的代码复制。

图9。ViewModelLocator静态构造函数

static ViewModelLocator()
{
  ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
  if (ViewModelBase.IsInDesignModeStatic)
  {
    SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
  }
  else
  {
    SimpleIoc.Default.Register<IDataService, DataService>();
  }
  SimpleIoc.Default.Register<MainViewModel>();
}

因为MainViewModel预计IDataService在它的构造函数,SimpleIoc会照顾的创建和组合服务和ViewModel需求。ViewModelLocator走得更远一点,MainViewModel作为财产公开,使数据绑定主页的DataContext和利用设计时服务实现(DesignDataService)或运行时实现(DataService)。可视化的区别,按Ctrl + F5运行应用程序(在模拟器或在本地机器上)。主页显示文本“欢迎来到MVVM。“那么,在Visual Studio,右键单击主页。xaml在解决方案资源管理器,选择开在设计师的混合或视图。相同的界面显示,这一次与文本“欢迎来到MVVM[设计]。”

运行时数据和设计时数据的区别在这个非常简单的,默认的应用程序成为可能的两种实现IDataService,ViewModelBase。IsInDesignModeStatic财产,触发正确的服务实现注册。一旦MainViewModel解决第一次IOC容器能够创建正确的服务,缓存和通过它MainViewModel构造函数,然后缓存实例。

当然,IOC容器还支持取消一个类。在某些情况下已经创建并缓存,注销方法将从缓存中删除这些实例。如果这些实例不是在应用程序中引用的其他地方,他们将被删除的垃圾收集器和内存将被回收。

处理视图服务

本文已经谈到了服务上,类视图模型提供数据和功能(例如,从Web服务)。有时,然而,ViewModel也需要另一种服务为了使用视图中的功能。在这种情况下,我们讨论观点服务。

两个典型NavigationService和DialogService观点服务。第一个提供了导航功能,如NavigateTo返回,等等。Windows Phone和Windows 8的现代应用程序几乎总是使用NavigationService因为它们使用页面导航应用程序。从ViewModel DialogService是非常有用的,因为开发人员不想知道消息将显示给用户。ViewModel仅仅提供了错误信息。设计师(或集成商)负责显示消息的例子中,在状态栏或一个自定义对话框。DialogService通常提供功能,如ShowStatus,ShowMessage ShowError等等。

NavigationService不同的实现取决于所使用的平台。在Windows 8的现代应用,例如,NavigationService可以是一个独立的类(在一个实用程序库),使用当前窗口的框架来执行实际的导航。这是,事实上,每个页面如何执行导航,通过使用其内置的NavigationService财产。当然,视图模型是一个简单的对象,所以没有这样一个内置的财产,并再次在这里IOC容器方便:ViewModelLocator NavigationService登记,缓存的IOC容器,可以根据需要注入每个视图模型。这个操作如图10所示(从本文提供的参考实现)。

图10。创建和注册NavigationService,MainViewModel注射

public class ViewModelLocator
{
  static ViewModelLocator()
  {
    ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
    if (ViewModelBase.IsInDesignModeStatic)
    {
      SimpleIoc.Default.Register<IRssService, Design.DesignRssService>();
    }
    else
    {
      SimpleIoc.Default.Register<IRssService, RssService>();
    }
    SimpleIoc.Default.Register<INavigationService, NavigationService>();
    SimpleIoc.Default.Register<MainViewModel>();
  }
  public MainViewModel Main
  {
    get
    {
      return ServiceLocator.Current.GetInstance<MainViewModel>();
    }
  }
}
public class MainViewModel : ViewModelBase
{
  private readonly IRssService _rssService;
  private readonly INavigationService _navigationService;
  public ObservableCollection<RssItem> Items
  {
    get;
    private set;
  }
  public MainViewModel(
    IRssService rssService,
    INavigationService navigationService)
  {
    _rssService = rssService;
    _navigationService = navigationService;
    Items = new ObservableCollection<RssItem>();
  }
  // ...
}

结束

本文是一个系列的一部分在Windows 8 MVVM MVVM光。下一篇文章将描述耦结构的一些挑战,如触发页面之间导航,显示对话框向用户和使用动画。在那篇文章中,我将介绍解决方案,涉及MVVM光的信使服务组件和使用视图。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值