WPF的MVVM框架Stylet开发文档 14.5 StyletIoC 工厂

14.5 StyletIoC 工厂

问题

构造函数/参数注入一切都很好,只要你只需要某物的一个实例。当您开始需要自己创建实例时(想想您的 ShellViewModel 想要显示对话框,并且需要为它们创建 ViewModels),即

class ShellViewModel
{
    
    
   // ...
   public void ShowDialog()
   {
    
    
      // 我们不想直接创建 DialogViewModel,因为它有自己的依赖项需要注入
      var dialogVm = new DialogViewModel();
      this.windowManager.ShowDialog(dialogVm);
   }
}

诱人的解决方案是让您的 ShellViewModel(或其他)了解您的 IoC 容器,因此它可以调用this.container.Get<DialogViewModel>(),如下所示:

class ShellViewModel
{
    
    
   // ...
   public ShellViewModel(IContainer container)
   {
    
    
      this.container = container;
   }

   public void ShowDialog()
   {
    
    
      var dialogVm = this.container.Get<DialogViewModel>();
      this.windowManager.ShowDialog(dialogVm);
   }
}

这被称为服务定位器模式,关于它是否实际上是一种反模式存在很多争论——本质上,它添加了一个不应该添加的依赖项(你的类需要知道你的(正确配置的)IoC容器)——同时隐藏您的类实际具有的依赖项。

解决方案

这个问题实际上在 1994 年被 GoF——抽象工厂模式解决了。由于您不能将 a 注入DialogViewModel到您的 中ShellViewModel,因此您可以注入一个可以创建的工厂DialogViewModels,如下所示:

class ShellViewModel
{
    
    
   // ...
   public ShellViewModel(Func<DialogViewModel> dialogViewModelFactory, IWindowManager windowManager)
   {
    
    
      this.dialogViewModelFactory = dialogViewModelFactory;
      this.windowManager = windowManager;
   }

   public void ShowDialog()
   {
    
    
      var dialogVm = this.dialogViewModelFactory();
      this.windowManager.ShowDialog(dialogVm);
   }
}

这非常有效:您可以在这里提供任何您喜欢的东西进行测试,而 IoC 容器可以提供适合正常运行的东西。

StyletIoC 支持两种类型的工厂:“Func-factories”——如上所示——和“抽象工厂”——你为工厂提供接口,StyletIoC 提供实现。两者都记录在下面各自的部分中。

功能工厂

这些是最容易使用的工厂类型。它们不需要您进行任何设置,而且非常灵活。然而,它们确实有一些限制:这些将在后面讨论,并由抽象工厂解决。

黄金法则是:如果你能执行container.Get<ISomeInterface>(),你也能执行container.Get<Func<ISomeInterface>()。构造函数和属性注入当然也是如此:如果您可以编写这样的构造函数:

class Foo
{
    
    
   public Foo(IBar bar)
   {
    
    
   }
}

你也可以这样写:

class Foo
{
    
    
   public Foo(Func<IBar> bar)
   {
    
    
   }
}

如果多次执行 Func<ISomeInterface> 工厂函数,效果就像多次调用 container.Get<ISomeInterface>() 一样:如果 ISomeInterface 被注册为单例,每次都会得到相同的实例。如果它被注册为瞬态,则每次都会得到不同的实例。

Func 工厂还有其他一些诀窍:如果您请求一个 Func<IEnumerable<ISomeInterface>> (可以通过 container.Get<Func<IEnumerable<ISomeInterface>>>() 或构造函数/属性注入方式实现),则会得到一个工厂,当调用它时,将返回与 container.GetAll<ISomeInterface>()(或者如果您已将 IEnumerable<ISomeInterface> 注入到构造函数/属性中)相同的东西。

类似地,如果您请求一个IEnumerable<Func<ISomeInterface>>(通过container.GetAll<Func<ISomeInterface>()),或者通过注入IEnumerable<Func<ISomeInterface>>构造函数或属性),您将获得一组工厂,并且每个工厂都能够创建该实现的新实例ISomeInterface

Func 工厂确实有一个限制:无法获取需要键的内容。回想一下,您可以调用 container.Get<ISomeInterface>("magicKey"),但是您不能像这样做 var factory = container.Get<Func<string, ISomeInterface>>(); factory("magicKey")。这是一个有意的决定-一开始所有这些都有点不明显,并且当您开始涉及到工厂集合和集合的工厂时,有一些非常棘手的角落情况。如果您需要这种行为,请使用抽象工厂(下面会介绍)。

抽象工厂

抽象工厂类似于 func 工厂,但不是每个工厂都有类型Func<ISomeInterface>,而是定义自己的工厂。

考虑这个例子:

interface IDialogViewModelFactory
{
    
    
   DialogViewModel CreateDialogViewModel();
}

class ShellViewModel
{
    
    
   // ...
   public ShellViewModel(IDialogViewModelFactory dialogViewModelFactory)
   {
    
    
      this.dialogViewModelFactory = dialogViewModelFactory;
   }

   public void ShowDialog()
   {
    
    
      var dialogVm = this.dialogViewModelFactory.CreateDialogViewModel();
      this.windowManager.ShowDialog(dialogVm);
   }
}

在最基本的情况下,你可以提供一个简单的实现,IDialogViewModelFactory它只是调用 IoC 容器以供生产使用,或者你可以提供一个模拟来进行测试。

但是编写该实现IDialogViewModelFactory有点痛苦,而 StyletIoC 有一个解决方案。与其他一些 IoC 容器一样,StyletIoC 能够采用这样的接口,并为您生成一个实现(不同之处在于 StyletIoC 不需要任何依赖项来执行此操作)。

要利用这一点,请使用绑定语法builder.Bind<IFactoryInterfaceType>().ToAbstractFactory(),例如:

public interface IDialogViewModelFactory
{
    
    
   DialogViewModel CreateDialogViewModel();
}

// ... 

builder.Bind<IDialogViewModelFactory>().ToAbstractFactory();

这样的工厂方法也可以创建类型的集合,例如:

public interface IVehicleFactory
{
    
    
   IEnumerable<IVehicle> CreateVehicleCollection();
}

限制

编写工厂接口时需要牢记一些事项。这些在下面展开:

  1. 该接口必须是公共的,或者您必须[assembly: InternalsVisibleTo(StyletIoC.FactoryAssemblyName)]在您的AssemblyInfo.cs.
  2. 工厂中的每个方法都必须返回一个在 IoC 容器中注册的类型,或该类型的 IEnumerable,并且必须具有零参数或单个字符串参数。

StyletIoC 在不同的程序集中生成工厂接口的实现,因此您的接口必须是公共的,或者您必须已将此程序集标记为您的“朋友”(使用[assembly: InternalsVisibleTo(StyletIoC.FactoryAssemblyName)])。你可能确实希望你的工厂接口是公共的(这样你就可以在编写单元测试时手动提供一个实现。

StyletIoC 只知道如何为返回某些东西(即不是void)、采用零参数或单个字符串参数(用作键,下面介绍)的方法生成方法实现。

在抽象工厂中使用键

当 StyletIoC 的实现创建一个类型的新实例时,您可以通过多种不同的方式指定要使用的键(请参阅StyletIoC 键)。第一个是使用[Inject(Key = "key")]属性:

public interface IDialogViewModelFactory
{
    
    
   [Inject(Key = "someKey")]
   DialogViewModel CreateDialogViewModel();
}

您还可以创建采用单个字符串参数的方法,该参数将被使用:

public interface IDialogViewModelFactory
{
    
    
   DialogViewModel CreateDialogViewModel(string key);
}

// ... then

this.dialogViewModel.CreateDialogViewModel("someKey");

在其内部运作中

在底层,StyletIoC 生成一个类型,其实现看起来很像这样:

public interface IFactoryInterface
{
    
    
   TypeWithoutKey CreateTypeWithoutKey();

   IEnumerable<ElementType> CreateCollection();

   [Inject(Key = "key")]
   TypeWithAttributeKey CreateTypeWithAttributeKey();

   TypeWithKeyParameter CreateTypeWithKeyParameter(string key);
}

public class FactoryInterface : IFactoryInterface
{
    
    
   private IContainer container;
   public FactoryInterface(IContainer container)
   {
    
    
      this.container = container;
   }

   public TypeWithoutKey CreateTypeWithoutKey()
   {
    
    
      return this.container.GetTypeOrAll(typeof(TypeWithoutKey));
   }

   public IEnumerable<ElementType> CreateCollection()
   {
    
    
      return this.container.GetTypeOrAll(typeof(IEnumerable<ElementType>));
   }

   public TypeWithAttributeKey CreateTypeWithAttributeKey()
   {
    
    
      return this.container.GetTypeOrAll(typeof(TypeWithAttributeKey), "key");
   }

   public TypeWithKeyParameter CreateTypeWithKeyParameter(string key)
   {
    
    
      return this.container.GetTypeOrAll(typeof(TypeWithKeyParameter), key);
   }
}

项目原地址:https://github.com/canton7/Stylet
当前文档原地址:https://github.com/canton7/Stylet/wiki/StyletIoC-Factories

上一节:WPF的MVVM框架Stylet开发文档 14.4 StyletIoC Keys
下一节:WPF的MVVM框架Stylet开发文档 14.6 StyletIoC 模块

上一篇:WPF的MVVM框架Stylet开发文档 13.验证模型基类ValidatingModelBase
下一篇:WPF的MVVM框架Stylet开发文档 15. 视图管理器 The ViewManager

猜你喜欢

转载自blog.csdn.net/qq_39427511/article/details/130363375