代码之家  ›  专栏  ›  技术社区  ›  Richard McGuire

带有视图模型的MVVM继承

  •  17
  • Richard McGuire  · 技术社区  · 15 年前

    我想知道如何使用MVVM模式中的视图模型来处理继承。在我的应用程序中,我有一个类似于以下内容的数据模型:

    class CustomObject
    {
        public string Title { get; set; }
    }
    
    class CustomItem : CustomObject
    {
        public string Description { get; set; }
    }
    
    class CustomProduct : CustomItem
    {
        public double Price { get; set; }
    }
    

    在我的应用程序中,我有一个ViewModelBase类,然后将拥有以下视图模型:

    • 自定义对象视图模型
    • 自定义项视图模型
    • 自定义产品视图模型

    CustomObjectViewModel的粗略实现如下所示:

    class CustomObjectViewModel : ViewModelBase
    {
        private readonly CustomObject _customObject;
    
        public CustomObjectViewModel( CustomObject customObject )
        {
            _customObject = customObject;
        }
    
        public string Title
        {
            // implementation excluded for brevity
        }
    }
    

    在我看来,我的视图模型将以与我的模型相同的方式扩展自己(customItemViewModel扩展customObjectViewModel等等)。但是,我注意到,当我沿着继承树往下走时,我将添加对同一对象的附加引用。这对我来说似乎太过分了,我想知道如何解决这个问题,以及是否有可能使它变得更干净。

    4 回复  |  直到 12 年前
        1
  •  17
  •   Enrico Campidoglio    15 年前

    一般我会推荐你 不要在不同的ViewModel类之间继承 而是直接从公共抽象基类继承它们。
    这是为了避免引入不必要的复杂性 污染ViewModel类的接口 成员来自高层,但是 没有充分的凝聚力 为了班级的主要目的。
    继承附带的耦合也可能使在不影响任何派生类的情况下更改ViewModel类变得困难。

    如果ViewModel类始终引用单个模型对象,则可以使用泛型将此规则封装到基类中:

    public abstract class ViewModelBase<TModel>
    {
        private readonly TModel _dataObject;
    
        public CustomObjectViewModel(TModel dataObject)
        {
            _dataObject = dataObject;
        }
    
        protected TModel DataObject { get; }
    }
    
    public class CustomObjectViewModel : ViewModelBase<CustomObject>
    {
        public string Title
        {
            // implementation excluded for brevity
        }
    }
    
    public class CustomItemViewModel : ViewModelBase<CustomItem>
    {
        public string Title
        {
            // implementation excluded for brevity
        }
    
        public string Description
        {
            // implementation excluded for brevity
        }
    }
    
        2
  •  5
  •   Jose    15 年前

    与Enrico的上述评论相关。视图模型不应该与视图紧密耦合,而是相反。视图应该松散地耦合到视图模型。视图模型不应该了解视图,这允许您轻松地对视图模型进行单元测试。视图与视图模型之间的所有交互应通过视图模型中的属性(操作的ICommand属性和数据绑定的其他属性)实现。

    有一点是正确的,那就是ViewModel与模型紧密耦合,所以上面的泛型的使用允许大量的可扩展性。这是我推荐的模式。

    通过提供一个基本上只公开属性的ViewModel类,它应该允许您将其放入任何类型的表示框架中,并利用以前使用过的所有代码。换句话说,如果实现得当,您可以将ViewModels程序集放到ASP.NET MVC应用程序中,并将视图与属性绑定,并且不更改代码。

    关于MVVM基础知识的一篇好文章是: this one. 我真的认为MVVM是UI开发的最佳选择。显然,我们不能全部使用它,因为它需要使用MVVM方法从头开始构建一个应用程序,但是当您构建一个新的应用程序时,这不是一个问题。

    我对ICommand的一个抱怨是它位于基本上用于WPF的表示核心组件中。如果微软想要松耦合,它应该在另一个程序集中。

        3
  •  4
  •   Martin Harris    15 年前

    我很想看看是否有更好的答案,但当我遇到同样的问题时,我总是将对象的显式强制转换作为私有财产,就像这样:

    class CustomObjectViewModel : ViewModelBase
    {
        protected readonly CustomObject CustomObject;
    
        public CustomObjectViewModel( CustomObject customObject )
        {
            CustomObject = customObject;
        }
    
        public string Title
        {
            // implementation excluded for brevity
        }
    }
    
    class CustomItemViewModel : CustomObjectViewModel 
    {
        protected CustomItem CustomItem  { get { return (CustomItem)CustomObject; } }
    
        public CustomItemViewModel( CustomItem customItem )
            :base(customItem)
        {
        }
    }
    

    它起作用了,这是我想到的最好的方法,但我从来没有觉得很干净。

        4
  •  4
  •   Cameron MacFarland    15 年前

    我认为这里的问题是每个视图应该有一个视图模型,而不是每个模型有一个视图模型。

    原因很明显,因为您只能将一个对象设置为DataContext,所以该对象应该是该视图的ViewModel。