Mvvm 是否应通过构造函数、属性或方法调用初始化ViewModel

Mvvm 是否应通过构造函数、属性或方法调用初始化ViewModel,mvvm,constructor,initialization,viewmodel,Mvvm,Constructor,Initialization,Viewmodel,我在MVVM的上下文中讨论了一些不同的设计概念,这些概念主要源于何时初始化ViewModel的问题。更具体地说,在“初始化”方面,我指的是加载值,例如选择值、安全上下文,以及在某些情况下可能导致几秒钟延迟的其他内容 可能的战略: 将参数传递给ViewModel构造函数并在构造函数中进行加载 仅支持ViewModel上的无参数构造函数,而支持采用参数并执行加载的初始化方法 选项1和选项2的组合,其中参数被传递给ViewModel构造函数,但加载延迟到调用A Initialize方法 选项3的一个变

我在MVVM的上下文中讨论了一些不同的设计概念,这些概念主要源于何时初始化ViewModel的问题。更具体地说,在“初始化”方面,我指的是加载值,例如选择值、安全上下文,以及在某些情况下可能导致几秒钟延迟的其他内容

可能的战略:

  • 将参数传递给ViewModel构造函数并在构造函数中进行加载
  • 仅支持ViewModel上的无参数构造函数,而支持采用参数并执行加载的初始化方法
  • 选项1和选项2的组合,其中参数被传递给ViewModel构造函数,但加载延迟到调用A Initialize方法
  • 选项3的一个变体,其中参数不是传递给ViewModel构造函数,而是直接在属性上设置
  • 对ViewModel属性getter和setter的影响

    在初始化延迟的情况下,需要知道ViewModel是否处于一种被认为可用的状态,IsBusy属性通常用于该状态,就像它用于其他异步和耗时的操作一样。但这也意味着,由于ViewModel上的大多数属性都公开了从模型对象检索到的值,因此我们必须不断编写以下类型的管道,以确保模型可用

    public string Name
    {
        get 
        {  
            if (_customerModel == null) // Check model availability
            {
                return string.Empty;
            }
    
            _customerModel.Name;
        }
    }
    
    尽管检查很简单,但它只是增加了INPC的管道和其他类型的必需品,使得ViewModel的编写和维护变得有些麻烦。在某些情况下,问题变得更加严重,因为从属性getter返回的默认值可能并不总是合理的,这可能是布尔属性IsCommercialAccount的情况,如果没有可用的模型,则返回true或false是没有意义的,从而引发了一系列其他设计问题,例如作为可空性。在上面的选项1中,我们将所有内容传递到构造函数并加载它,那么我们只需要关注视图中的NULL ViewModel,并且当ViewModel不为NULL时,它保证被初始化

    支持延迟初始化

    对于选项4,还可以依赖于可以在ViewModel基类中实现的选项,以提供一致的方式来通知ViewModel是否已初始化,并通过标准方法启动初始化。这也可以用于选项2和3,但如果所有初始化参数都在一个原子事务中设置,则意义不大。至少在这种情况下,上述情况可能会变成类似的情况

    设计如何影响IoC

    就IoC而言,我理解选项1和3可以使用构造函数注入完成,这通常是首选的,选项2和4可以分别使用方法和属性注入完成。然而,我关心的不是IoC或如何传递这些参数,而是总体设计,以及它如何影响ViewModel的实现和它的公共界面,尽管我想成为一个好公民,在将来必要时让IoC变得更容易一些

    可测试性

    这三个选项似乎都同样支持可测试性的概念,这对在这些选项之间做出决定没有多大帮助,尽管选项4可能需要更广泛的测试集,以确保属性的正确行为(该行为取决于初始化状态),这是有争议的

    指挥能力

    选项2、3和4都有一个副作用,即需要视图中的代码隐藏来调用ViewModel上的初始化方法,但是如果需要,这些方法可以作为命令公开。在大多数情况下,可能会在构建之后直接调用这些方法,如下所示

    var viewModel = new MyViewModel();
    this.DataContext = viewModel;
    // Wrap in an async call if necessary
    Task.Factory.StartNew(() => viewModel.InitializeWithAccountNumber(accountNumber));
    
    一些其他想法

    我在使用MVVM设计模式时尝试了这些策略的各种变体,但尚未得出最佳实践的结论。我很想听听社区的想法,并尝试就初始化ViewModel或在其属性处于不可用状态时处理其属性的最佳方式找到合理的共识


    理想情况可能是使用状态模式,其中ViewModel本身与表示不同状态的不同ViewModel对象交换。因此,我们可以有一个通用BusyViewModel实现,它表示繁忙状态,该状态消除了对ViewModel上IsBusy属性的一个需求,然后当下一个ViewModel准备就绪时,它在视图上被调出,从而允许该ViewModel遵循选项1中概述的状态,在该状态下,它在建设这就留下了一些关于谁负责管理状态转换的问题,例如,BusyViewModel有责任抽象出类似于BackgroundWorker的内容,或者抽象出一个正在进行初始化的任务,并在准备就绪时显示内部ViewModel。另一方面,交换视图上的DataContext可能需要处理视图中的事件,或者向BusyViewModel提供对视图DataContext属性的有限访问权限,以便可以在传统的状态模式意义上进行设置。如果人们在这些方面做了类似的事情,我肯定很想知道,因为我的谷歌搜索结果还不多。

    我的面向对象设计的一般方法,无论是创建视图模型还是其他类型的类,都是这样的;可以传递给构造函数的所有内容都应该传递给构造函数。这减少了初始化某种
    的需要