Наследование MVVM с моделями просмотра

Мне интересно, как подойти к наследованию с помощью View Models в шаблоне 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
  • CustomItemViewModel
  • CustomProductViewModel

Грубая реализация CustomObjectViewModel будет выглядеть следующим образом:

class CustomObjectViewModel : ViewModelBase
{
    private readonly CustomObject _customObject;

    public CustomObjectViewModel( CustomObject customObject )
    {
        _customObject = customObject;
    }

    public string Title
    {
        // implementation excluded for brevity
    }
}

Мне кажется логичным, что мои модели просмотра будут расширяться так же, как моя модель (CustomItemViewModel расширяет CustomObjectViewModel и т.д.). Однако я заметил, что по мере того как я спускаюсь по дереву наследования, я буду добавлять дополнительные ссылки на один и тот же объект. Мне это кажется слишком чрезмерным, и мне было интересно, как подойти к этой проблеме, и если бы можно было сделать ее намного чище.

Ответы

Ответ 1

В общем, я бы рекомендовал вам не иметь наследования между разными классами ViewModel, но вместо этого наследовать их непосредственно из общего абстрактного базового класса.
Это делается для того, чтобы не вводить ненужную сложность с помощью загрязнение интерфейсов классов ViewModel с элементами, которые поступают из иерархии выше, но не являются полностью сплоченными для основной цели класса.
Связь, которая поставляется с Наследование также, вероятно, затруднит изменение класса ViewModel, не затрагивая ни один из его производных классов.

Если ваши классы ViewModel всегда ссылаются на один объект Model, вы можете использовать generics для инкапсуляции этого правила в базовый класс:

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

Относительно комментария выше Энрико. ViewModels не должны быть тесно связаны с представлениями, это должно быть наоборот. Представления должны быть слабо связаны с ViewModels. ViewModel не должен знать о представлении, это позволяет легко unit test ViewModel. Все взаимодействия между представлениями с ViewModel должны быть реализованы с помощью свойств в ViewModel (свойства ICommand для действий и других свойств для привязки данных).

Единственное, что верно, это то, что ViewModel тесно связан с моделью, поэтому использование генериков выше позволяет много расширяемости. Это шаблон, который я бы рекомендовал.

Предоставляя класс ViewModel, который в основном просто предоставляет свойства, он должен позволить вам внедрить его в любой вид структуры представления и использовать весь код, который вы использовали ранее. Другими словами, если вы правильно выполнили, вы можете сбросить сборку ViewModels в приложение ASP.NET MVC и привязать представление к свойствам и не изменить код.

Хорошая статья об основах MVVM: этот. Я действительно думаю, что MVVM - лучшая вещь для разработки пользовательского интерфейса. Очевидно, что мы все не можем использовать это, потому что для этого нужно создать приложение с нуля с использованием подхода MVVM, но когда вы создаете новое приложение, это не проблема.

Единственное, что у меня есть с ICommand, это то, что оно находится в AssemblyCore Assembly, которое в основном для WPF. Если Microsoft хочет иметь свободную связь, она должна быть в другой сборке вообще.

Ответ 3

Я думаю, что проблема здесь в том, что для каждого представления должен быть один ViewModel, а не один ViewModel для каждой модели.

Причина этого довольно очевидна, так как вы можете установить только один объект DataContext, этот объект должен быть ViewModel для этого представления.

Ответ 4

Мне было бы интересно узнать, есть ли лучший ответ для этого, но когда у меня была такая же проблема, я всегда добавлял явный вид объекта в качестве частного свойства, например:

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)
    {
    }
}

Это работает, и это лучшее, что я придумал, но никогда не чувствовал себя очень чистым для меня.