Должна ли MVVM ViewModel выполнять преобразование / проверку типов?

Мы только начинаем знакомство с MVVM в WPF.

Мы реализовали наши модели представления со «строго типизированными» свойствами (int, double и т. Д.), К которым мы привязываемся в представлении.

Преобразование типов в основном работает нормально, поэтому ввод данных достаточно прост. Но мы сталкиваемся с проблемами с проверкой.

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

Я знаю, что со всем этим можно справиться с помощью преобразователей стоимости. Но я встречал несколько мнений о том, что конверсия вообще не должна быть обязанностью представления. В представление вводятся строки, а преобразование, проверка и т. Д. Должны лежать в сфере ответственности ViewModel (так следует аргумент).

Если это так, мы должны переписать большинство свойств в наших ViewModels в строку и предоставить информацию об ошибках, например, через интерфейс IErrorInfo. Это, несомненно, сделало бы представление XAML более простым и компактным. С другой стороны, преобразование, проверка и т. Д. Будут менее декларативными, явными и гибкими с точки зрения дизайнера представлений.

Нам кажется, что эти два подхода принципиально разные, поэтому, прежде чем мы решим, мы хотели бы получить некоторые информированные мнения SO по этому поводу.

Итак: должны ли ViewModels предоставлять упрощенный, «текстовый» интерфейс для представления и обрабатывать преобразование внутренне? Или свойства ViewModel должны раскрывать фактические типы данных, оставляя такую ​​рутинную работу представлению для обработки?

Обновление:

Здесь сложно выбрать победителя, но в конце концов я остановился на одном из вас, который пришел к выводу более или менее похожим на меня.

В частности, мы решили оставить свойства ViewModel типизированными. Основная причина этого - гибкость, которую он дает нам при разработке представления, и возможности явного декларативного преобразования / форматирования в XAML.

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

На самом деле, если детали пользовательского интерфейса дорабатывается в процессе, остается место для творчества, и, кроме того, на мой взгляд, даже тщательно проработанный дизайн всегда будет трансформироваться в процессе реализации.

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

В общем, отличное обсуждение, ребята, с хорошо сформулированными, информированными мнениями. Спасибо.


person Tor Haugen    schedule 26.08.2009    source источник
comment
Собирался опубликовать этот вопрос. +1   -  person Gishu    schedule 11.07.2010


Ответы (5)


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

Глядя на шаблон MVVM, как я его понимаю, цель ViewModel состоит в том, чтобы предоставить данные таким образом, чтобы представление могло их понять без каких-либо предположений о том, как представление будет их использовать. Для примера представим, что мы моделируем скорость автомобиля:

public class CarModel
{
    public int MilesPerHour { get; set; }
}

public class CarViewModel
{
    private CarModel _model;

    public int MilesPerHour
    {
        get { return _model.MilesPerHour; }
        set { _model.MilesPerHour = value; }
    }
}

В приведенном выше примере я представил свойство как int, так как оно есть в модели. Недостатки этого метода вы указали в своем вопросе, но главное преимущество состоит в том, что он дает создателю представления ценную информацию о том, как отображать эти данные. Помните, что мы (как авторы ViewModel) не знаем, как выглядит View. Приняв идею о том, что данные представляют собой int, View может использовать текстовое поле или какой-либо другой элемент управления, который принимает только числа (например, циферблат) для отображения информации. Если мы говорим, что собираемся форматировать данные так, как мы предполагаем, это полезно для представления, это лишает его этой важной силы.

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

Наконец (и я уверен, что вы это знаете, но для полноты картины ...) бизнес-логика должна выполняться в ViewModel. Если мы решим, что машина не должна превышать 70 миль в час, то это не обязанность обзора. Таким образом, вы все равно получите своего рода провайдер ошибок, но на уровне бизнеса, а не на уровне отображения.


Ладно, может, это и не было окончательно ....

Я хотел ответить на комментарии Кента, но мои мысли не укладывались в комментарий.

Очевидно, что основное различие между моей точкой зрения и точкой зрения Кента (насколько я понимаю) заключается в том, что он считает ViewModel моделью представления, а я читал, что это то, что представляет модель для представления. Я допускаю небольшую разницу, но я думаю, что в результате я не хочу удалять информацию, которую предоставляет модель, даже если это упрощает работу с конкретным представлением, которое я использую.

Моя точка зрения основана на предположении, что вы должны иметь возможность менять представления, это должны быть мимолетные вещи, которые могут меняться в зависимости от требований к размеру экрана, оборудованию, платформе, задержке и среде. Интересный поворот заключается в том, что мне никогда на самом деле не требовалась эта функциональность, и я не видел ничего (кроме доказательства концептуальных приложений), которые когда-либо использовали бы ее, но если мы примем, что мы не будем использовать ее сейчас или в любой момент в будущем, и что каждая ViewModel будет работать с одним и только одним View, тогда мы можем вернуться к помещению всего кода в файл кода программной части и полностью выбросить ViewModel - в конце концов, это так тесно связаны, что с тем же успехом это может быть один и тот же класс.

В идеале мне нужна ситуация, когда ViewModel может сказать: «это значение - int, оно всегда будет int, и вы можете отображать его как угодно. Но вы можете вернуть мне что угодно, и я сделаю все, что в моих силах». лучше всего подогнать, и если я не смогу, я дам вам знать ". В основном у моего свойства MilesPerHour должно быть средство получения int, но средство установки объекта. Таким образом, представления сохраняют всю информацию, которая, как я считаю, им нужна, но не нужно беспокоиться о преобразованиях или проверке.

person Martin Harris    schedule 26.08.2009
comment
Очень хорошо сказано, Мартин. Спасибо. - person Tor Haugen; 26.08.2009
comment
Я очень надеюсь, что вы знаете, как выглядит вид. Если нет, то в вашем сотрудничестве между разработчиками и дизайнерами что-то серьезно нарушено. Принятие на себя ответственности за определенные активы не означает, что мы работаем в вакууме - нам все равно нужно понимать, что делает другой. - person Kent Boogaart; 26.08.2009
comment
Я не знаю, как выглядит представление, если я работаю в приложении, которое может быть изменено сторонними лицами. Но даже вне этого сценария я имел в виду шаблон MVVM, подразумевающий, что в идеальном мире ViewModel не должен ничего знать о представлении (даже если это WPF, Webforms или что-то еще). - person Martin Harris; 26.08.2009
comment
ИМХО, это распространенное и досадное заблуждение. ВМ - это модель представления. Следовательно, он абсолютно должен знать о представлении, иначе он не сможет его эффективно смоделировать. Снятие шкур - это всего лишь шкура. Вы по-прежнему будете принимать решения в своей виртуальной машине, которые повлияют на то, насколько гибкой может быть оболочка. Например, выбор способа предоставления данных. - person Kent Boogaart; 26.08.2009
comment
@Kent Извини, мне кажется, мой повтор получился немного резким. Я согласен с вами (в моем текущем проекте я пишу представления, поэтому я знаю их наизнанку). Но я пытался ответить на абстрактный вопрос о шаблоне проектирования в идеализированном случае. - person Martin Harris; 26.08.2009
comment
@Martin: np, я не думал, что это было внезапно. Может, поговорим за пивом - я тоже в Кэнэри-Уорф :) - person Kent Boogaart; 26.08.2009
comment
Хороший ответ, но просто указываю: Согласно MS, бизнес-логика должен войти в Модель. Кажется, что у MVVM Google есть бизнес-логика в виртуальной машине, что я считаю неприятным, потому что у нас есть бизнес-логика и логика представления в одном классе: отличный рецепт ошибки. - person funct7; 09.10.2019

Абсолютно он принадлежит модели представления по всем обычным причинам, в том числе:

  • Дизайнеры владеют XAML. Вы хотите, чтобы дизайнеры понимали и реализовывали необходимую логику преобразования типов и проверки?
  • Проверяемость. Разве вы не хотите убедиться, что ваша логика преобразования и проверки работает правильно? Намного сложнее, если он встроен в представление.

С другой стороны, преобразование, проверка и т. Д. Будут менее декларативными, явными и гибкими с точки зрения дизайнера представлений.

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

person Kent Boogaart    schedule 26.08.2009

Должна ли MVVM ViewModel выполнять преобразование / проверку типов?

Да.

Модель представления - это уровень абстракции между представлением и моделью - идеальное место для выполнения любых преобразований типов (вместо громоздких преобразователей значений). Проверка должна обязательно происходить как часть модели представления.

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

Один конкретный вопрос, который вы подняли:

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

можно обработать, выставив ваш тип модели представления как тип допускающий значение NULL. Это должно по-прежнему позволять обновлять базовый источник, даже если введены недопустимые данные, и запускать проверку. Это сработало в аналогичной ситуации, которая была у нас с DateTime и средством выбора даты и времени.

Сохраняйте представление как можно более тупым. У нас нет официальных дизайнеров, наши разработчики - это наши дизайнеры, поэтому сохранение представления в тупике дает некоторые преимущества:

  • Мы (разработчики) должны сохранять рассудок (XAML несколько менее подробен)
  • Бизнес-логика (включая проверку) остается в модели представления и может включать тестирование

Удачи!

-Z

person Zach Bonham    schedule 26.08.2009

Это хороший вопрос, и я, безусловно, вижу обе стороны дискуссии.

Я считаю, что то, что вы действительно ищете, - это правильный NumericInputControl, который вы можете использовать в своем xaml. Это обеспечит лучший пользовательский интерфейс, поскольку ваши пользователи не смогут случайно ввести текст в числовое поле и, поскольку элемент управления ограничивает ввод без его проверки, вы можете поддерживать более строго типизированную ViewModel.

Я не уверен, как бы вы хотели его реализовать, я знаю, что классические элементы управления spinner / NumericUpDown теряют популярность, потому что они не удобны для сенсорного ввода, но я не верю, что введение таких элемент управления нарушит чистоту подхода к дизайну или ваших ViewModels. Вы получите номер, который затем сможете проверить в соответствующем месте, отправить отзыв через IDataErrorInfo, как обычно, и так далее. :) Этот метод позволяет вам получить лучшее из обоих миров без каких-либо реальных недостатков (кроме создания числового элемента управления).

person Greg D    schedule 26.08.2009
comment
Хм. Вы предлагаете разделение труда - представление преобразуется в правильный тип и обратно, а модель представления проверяет преобразованные данные. В этом есть смысл. - person Tor Haugen; 26.08.2009

Или свойства ViewModel должны раскрывать фактические типы данных, оставляя такую ​​рутинную работу представлению для обработки?

  1. Преобразование и шаблоны выполняются в View, потому что оба они представляют собой всего лишь преобразование values, models и viewmodels в controls! Controls доступны только внутри View.

  2. Проверка выполняется в ViewModel, потому что проверка выполняется в соответствии с бизнес-правилами и даже может выполняться посредством вызова удаленной службы. View ничего не знает о бизнес-правилах, но знает, как представить результаты проверки.

Если, скажем, нечисловое значение введено в текстовое поле, привязанное к числовому свойству

Правильно созданный элемент управления числовым текстовым полем никогда не позволяет пользователю вводить нечисловое значение.

person Lightman    schedule 21.08.2015