MVVM бессмысленен? [закрыто]


91

Бессмысленна ли ортодоксальная реализация MVVM? Я создаю новое приложение и рассмотрел Windows Forms и WPF. Я выбрал WPF, потому что он ориентирован на будущее и предлагает большую гибкость. Меньше кода и проще вносить существенные изменения в пользовательский интерфейс с помощью XAML.

Поскольку выбор WPF очевиден, я подумал, что могу пойти до конца, используя MVVM в качестве архитектуры приложения, поскольку он предлагает смешиваемость, проблемы разделения и возможность тестирования модулей. Теоретически это кажется прекрасным, как святой Грааль программирования пользовательского интерфейса. Это короткое приключение; Однако превратилось в настоящую головную боль. Как и ожидалось на практике, я обнаружил, что поменял одну проблему на другую. Я склонен быть одержимым программистом в том смысле, что хочу делать все правильно, чтобы получить правильные результаты и, возможно, стать лучшим программистом. Шаблон MVVM только что провалил мой тест на продуктивность и превратился в большой противный хакер!

Ярким примером является добавление поддержки модального диалогового окна. Правильный способ - создать диалоговое окно и привязать его к модели представления. Заставить это работать сложно. Чтобы извлечь выгоду из шаблона MVVM, вам необходимо распределить код в нескольких местах по слоям вашего приложения. Вы также должны использовать такие эзотерические программные конструкции, как шаблоны и лямба-выражения. Вещи, которые заставляют смотреть в экран, почесывая затылок. Как я недавно обнаружил, это превращает обслуживание и отладку в кошмар, ожидающий своего часа. У меня было окно about, работающее нормально, пока я не получил исключение во второй раз, когда я вызвал его, говоря, что он не может снова показать диалоговое окно после его закрытия. Мне пришлось добавить обработчик событий для функции закрытия диалогового окна, еще один в его реализации IDialogView и, наконец, еще один в IDialogViewModel. Я думал, что MVVM спасет нас от такого экстравагантного взлома!

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

Я планирую отказаться от шаблона представления MVVM, по крайней мере, от его ортодоксальной реализации. Что вы думаете? Стоило ли вам хлопот, если они у вас были? Я просто некомпетентный программист или MVVM не такой, как его рекламируют?


8
Я всегда сомневался, является ли MVVM чрезмерной инженерией. Интересный вопрос.
Тейлор Лиз,

12
Такие шаблоны, как MVVM и MVC, кажутся чрезмерно сложными, пока вам не придется внести некоторые изменения или изменить компонент. Когда вы делаете это в первый раз, вся церемония окупается.
Роберт Харви,

42
Лямбды эзотерические? новости для меня.
Ray Booysen 09

6
@Ray - Ха-ха, +1 за этот комментарий! : D
Venemo

8
Как отметил более десяти лет назад Алан Купер в « About Face» , если вы разрабатываете пользовательский интерфейс и модальные диалоги не являются крайним случаем, вы, вероятно, делаете что-то не так.
Роберт Россни

Ответы:


61

Извините, если мой ответ стал немного длинным, но не вините меня! Ваш вопрос тоже длинный.

Таким образом, MVVM не бесполезен.

Ярким примером является добавление поддержки модального диалогового окна. Правильный способ - создать диалоговое окно и привязать его к модели представления. Заставить это работать сложно.

Да, действительно так.
Однако MVVM позволяет отделить внешний вид пользовательского интерфейса от его логики. Никто не заставляет вас использовать его везде, и никто не держит пистолет у вашего лба, чтобы заставить вас создать отдельную ViewModel для всего.

Вот мое решение для этого конкретного примера:
как пользовательский интерфейс обрабатывает определенный ввод, не является делом ViewModel. Я бы добавил код в файл View .xaml.cs, который создает экземпляр диалогового окна и устанавливает тот же экземпляр ViewModel (или что-то еще, если необходимо) в качестве его DataContext.

Чтобы извлечь выгоду из шаблона MVVM, вам необходимо распределить код в нескольких местах по слоям вашего приложения. Вы также должны использовать такие эзотерические программные конструкции, как шаблоны и лямба-выражения.

Что ж, вам не обязательно использовать его в нескольких местах. Вот как я бы это решил:

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

Я думаю, что цель MVVM - в первую очередь разделить логику приложения и конкретный пользовательский интерфейс, что позволяет легко модифицировать (или полностью заменять) пользовательский интерфейс.
Я использую следующий принцип: View может знать и предполагать все, что хочет от ViewModel, но ViewModel НИЧЕГО не может знать о View.
WPF предоставляет удобную модель привязки, которую вы можете использовать для достижения именно этого.

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

Вещи, которые заставляют смотреть в экран, почесывая затылок.

Да, я знаю это чувство. Именно то, что я почувствовал, когда впервые увидел MVVM. Но как только вы освоитесь, это больше не будет плохо.

У меня коробка работала нормально ...

Зачем помещать ViewModel за окном «О нас»? Нет смысла в этом.

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

Да, потому что сам факт, что элемент пользовательского интерфейса находится в том же окне, или другом окне, или вращается на орбите Марса в данный момент, не касается ViewModels.
Разделение проблем

РЕДАКТИРОВАТЬ:

Вот очень красивое видео под названием « Создайте свой собственный фреймворк MVVM» . Стоит посмотреть.


3
+1 за последние три слова. Но и в остальном ответ тоже хорош. :)
Роберт Харви

13
+1 за совет по использованию кода программной части. Распространенное заблуждение, что использование кода программной части в MVVM «плохо» ... но для вещей, связанных исключительно с пользовательским интерфейсом, это правильный путь.
Thomas Levesque

1
@ Томас: Да, я не могу с этим согласиться. Я видел несколько реализаций, в которых люди помещали весь код (даже связанный с пользовательским интерфейсом) в ViewModel, потому что (по их мнению) «это то место, где находится код». Это было довольно взломано.
Венемо

4
@Venemo, я действительно думаю, что вы можете инкапсулировать много вещей, которые вы хотите добавить в код программной части, используя такие методы, как custom Behaviors, что удобно, если вы постоянно пишете связующий код. В общем, я считаю, что лучше использовать код программной части для клея, чем собирать вместе неудобный XAML. На мой взгляд, главная забота - убедиться, что в коде программной части нет ничего достаточно сложного, чтобы гарантировать модульное тестирование. Все, что достаточно сложно, лучше инкапсулировать в ViewModel или классе расширения, например Behavior или MarkupExtension.
Дэн Брайант

8
@ Thomas: Вы правы, самый большой миф о MVVM заключается в том, что цель MVVM - избавиться от кода программной части. Цель состоит в том, чтобы вывести код, не относящийся к пользовательскому интерфейсу, из кода. Помещение кода только для пользовательского интерфейса в ViewModel так же плохо, как и размещение кода проблемной области в коде программной части.
Джим Рейнери

8

Заставить это работать сложно. Чтобы извлечь выгоду из шаблона MVVM, вам необходимо распределить код в нескольких местах по слоям вашего приложения. Вы также должны использовать такие эзотерические программные конструкции, как шаблоны и лямба-выражения.

Для обычного модального диалогового окна? Вы определенно делаете что-то не так - реализация MVVM не должна быть такой сложной.

Учитывая, что вы новичок как в MVVM, так и в WPF, вполне вероятно, что вы везде используете неоптимальные решения и излишне усложняете ситуацию - по крайней мере, я сделал это, когда впервые перешел на WPF. Прежде чем сдаваться, убедитесь, что проблема действительно в MVVM, а не в вашей реализации.

MVVM, MVC, Document-View и т. Д. - это старое семейство шаблонов. Есть недостатки, но нет фатальных недостатков того типа, который вы описываете.


5

Я нахожусь в середине довольно сложной разработки MVVM с использованием PRISM, поэтому мне уже приходилось справляться с такими проблемами.

Мои личные выводы:

MVVM против MVC / PopUps & co

  • MVVM - действительно отличный шаблон, и в большинстве случаев он полностью заменяет MVC благодаря мощной привязке данных в WPF.
  • Вызов уровня обслуживания непосредственно от докладчика в большинстве случаев является законной реализацией.
  • Даже довольно сложные сценарии List / Detail могут быть реализованы с помощью чистого MVVM благодаря синтаксису {Binding Path = /}.
  • Тем не менее, когда необходимо реализовать сложную координацию между несколькими представлениями, контроллер в обязательном порядке
  • Могут использоваться события; старый шаблон, подразумевающий хранение экземпляров IView (или AbstractObserver) в контроллере, устарел
  • Контроллер может быть введен в каждый Presenter контейнером IOC
  • Сервис Prism IEventAggregator - еще одно возможное решение, если единственное использование контроллера - это диспетчеризация событий (в этом случае он может полностью заменить контроллер).
  • Если представления должны создаваться динамически, это очень хорошо подходит для контроллера (в призме контроллер будет вводить (IOC) IRegionManager)
  • Модальные диалоговые окна в большинстве своем устарели в современных составных приложениях, за исключением действительно блокирующих операций, таких как обязательные подтверждения; в этих случаях модальную активацию можно абстрагировать как службу, вызываемую внутри контроллера, и реализовать ее с помощью специального класса, который также позволяет проводить расширенное модульное тестирование на уровне представления. Контроллер, например, вызовет IConfirmationService.RequestConfirmation («вы уверены?»), Который вызовет модальное диалоговое окно во время выполнения и может быть легко смоделирован во время модульного тестирования.

5

Я решаю проблему с диалогами обманом. My MainWindow реализует интерфейс IWindowServices, который предоставляет все диалоговые окна для конкретных приложений. Затем мои другие ViewModels могут импортировать интерфейс служб (я использую MEF, но вы легко можете просто передать интерфейс через конструкторы вручную) и использовать его для выполнения того, что необходимо. Например, вот как выглядит интерфейс моей небольшой служебной программы:

//Wrapper interface for dialog functionality to allow for mocking during tests
public interface IWindowServices
{
    bool ExecuteNewProject(NewProjectViewModel model);

    bool ExecuteImportSymbols(ImportSymbolsViewModel model);

    bool ExecuteOpenDialog(OpenFileDialog dialog);

    bool ExecuteSaveDialog(SaveFileDialog dialog);

    bool ExecuteWarningConfirmation(string text, string caption);

    void ExitApplication();
}

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

Более `` чистый '' дизайн MVVM может быть важен для большого приложения, где вам нужна более чистая изоляция и более сложная композиция, но для приложений малого и среднего размера я думаю, что практического подхода с соответствующими службами для открытия требуемых хуков вполне достаточно. .


Но как это назвать? Разве не лучше было бы просто создать диалог внутри функций класса IWindowServices, а не передавать его внутрь. Таким образом, вызывающему представлению модели не нужно было бы ничего знать о конкретной реализации диалога.
Джоэл Роджерс

Интерфейс внедряется в любой из моих экземпляров ViewModel, которым требуется доступ к диалоговым окнам приложения. В большинстве случаев я передаю ViewModel для диалога, но я немного поленился и использовал WPF OpenFileDialog и SaveFileDialog для вызовов диалогового окна файла. Моей основной целью была изоляция для целей модульного тестирования, так что этого достаточно для этой цели. Если вам нужна лучшая изоляция, вы, вероятно, захотите создать OpenFileViewModel и SaveFileViewModel, которые дублируют необходимые свойства для диалогов.
Дэн Брайант

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

5

Паттерны дизайна призваны помочь вам, а не мешать. Небольшая часть хорошего разработчика - это знать, когда «нарушать правила». Если MVVM громоздок для задачи и вы определили, что будущая ценность не стоит затраченных усилий, не используйте шаблон. Например, как прокомментировали другие авторы, зачем вам тратить на это все накладные расходы, чтобы реализовать простую коробку с описанием?

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


2
Верный. Простое окно с описанием должно быть простым, но что, если вам нужно отобразить такую ​​информацию, как версия, лицензирование, запущенный процесс, название компании и т. Д. Это вся информация, которая где-то живет. В стандартных формах вы можете просто привязать всю эту информацию и покончить с ней. MVVM говорит, что вы должны создать для него модель представления, которая является избыточной.
ATL_DEV 05

1

Как и сам паттерн, MVVM великолепен. Но библиотека управления WPF, поставляемая с поддержкой привязки данных NET 4.0, очень ограничена, она намного лучше, чем WinForm, но все же ее недостаточно для привязываемого MVVM, я бы сказал, что ее мощность составляет около 30% от того, что необходимо для привязываемого MVVM.
Связываемый MVVM: это пользовательский интерфейс, в котором ViewModel связан с View только с использованием привязки данных.
Шаблон MVVM касается объектного представления ViewState, он не описывает, как вы поддерживаете синхронизацию между View и ViewModel, в WPF это привязка данных, но это может быть что угодно. И на самом деле вы можете использовать шаблон MVVM в любом наборе инструментов пользовательского интерфейса, который поддерживает события \ обратные вызовы, вы можете использовать его в чистом WinAPI в WinForms (я сделал, и это не намного больше работы с событиями \ обратными вызовами), и вы даже можете использовать его в тексте Консоль, как переписать DoS Norton Commander с использованием шаблона MVVM.

Вкратце: MVVM не бесполезен, это здорово. Библиотека элементов управления WPF в .NET 4.0 - это мусор.

Вот простое доказательство концепции ViewModel, которую вы не можете привязать к данным чистым способом MVVM с использованием WPF.

public class PersonsViewModel
{
    public IList<Person> PersonList;
    public IList<ColumnDescription> TableColumns;
    public IList<Person> SelectedPersons;
    public Person ActivePerson;
    public ColumnDescription SortedColumn;
}

Вы не можете привязать данные к заголовкам столбцов WPF DataGrid, вы не можете привязать данные к выбранным строкам и т.д. и т.д., вы либо сделаете это простым кодом, либо напишите 200 строк кода взлома XAML для этих 5 строк простейшей модели ViewModel. Вы можете только представить, насколько хуже обстоят дела со сложными ViewModels.
Так что ответ прост, если вы не пишете приложение Hello World, использование привязываемого MVVM в WPF бессмысленно. Вы потратите большую часть своего времени на размышления о взломе, чтобы привязать вас к ViewModel. Связывание данных - это хорошо, но будьте готовы вернуться к событию в 70% случаев.


Вы можете привязать это с помощью конвертеров к DataGrid.
Кэмерон МакФарланд

@CameronMacFarland: Не все, некоторые свойства доступны только для чтения и не могут быть привязаны, некоторые просто не существуют, и есть только события, которые сообщают об изменении состояния.
Alex Burtsev

Признаюсь, у меня нет большого опыта использования WPF DataGrid. Я стараюсь избегать этого, потому что это некрасиво и больше не подходит для WPF. Сказав, что комбинация преобразователей и AttachedProperties для обработки событий должна дать вам то, что вам нужно.
Кэмерон МакФарланд

1
Алекс, у вас проблемы с дизайном DataGrid, а не с MVVM. Просто неправильно сказать, что «Связывание данных - это хорошо, но будьте готовы вернуться к 70% времени события». Я написал несколько объективно огромных приложений WPF, в которых вообще нет обработчиков событий в пользовательском интерфейсе - за исключением обработчика событий, который требуется сетке данных (Telerik) для инициализации.
Роберт Россни

3
Я думаю, вы могли бы добиться большего успеха, если бы вместо того, чтобы занять позицию: «Это плохо спроектировано и не работает», вы попытаетесь: «Почему это работает для других людей, а не для меня?» Вы можете обнаружить, что причина того, что что-то сложно делать, в том, что вы еще не знаете, как это делать.
Роберт Россни

0

Нет, это не бессмысленно, но сложно обдумать, хотя сам узор смехотворно прост. Существует масса дезинформации и различные группы, которые борются за правильный путь. Я думаю, что с WPF и Silverlight вам следует использовать MVVM, иначе вы будете чрезмерно кодировать и пытаться решать проблемы в новой модели, используя «старую» методологию форм выигрыша, которая просто приводит вас к неприятностям. Это в большей степени относится к Silverlight, поскольку все должно быть асинхронным (в этом случае возможны взломы, но вы должны просто выбрать другую платформу).

Я бы посоветовал внимательно прочитать эту статью « Упрощение WPF TreeView с помощью шаблона ViewModel», чтобы увидеть, как можно хорошо реализовать MVVM, и позволит вам изменить менталитет форм выигрыша на новый образ мышления в MVVM. Короче говоря, когда вы хотите что-то сделать, сначала примените логику к ViewModel, а не к View. Вы хотите выбрать товар? Изменить значок? Не повторяйте элементы пользовательского интерфейса, просто обновите свойства модели и позвольте привязке данных сделать все возможное.


-1

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

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

Но не хватает:

  • Кто создает ViewModels?
  • Кто отвечает за рабочий процесс приложения?
  • Кто выступает посредником между ViewModels, когда им нужно общаться друг с другом?

Мой подход состоит в том, чтобы представить контроллер (прецедента) который отвечает за недостающие точки. Как это работает, можно увидеть в примерах приложений WPF Application Framework (WAF) .


Шаблон «Посредник», реализованный Джошем Смитом, решил все проблемы связи с моей моделью представления. Messenger.NotifyColleague предоставил способ иметь полностью независимые модели представлений, которые знали, как реагировать на глобальные события (если им это небезразлично), без того, чтобы какие-либо две модели представления знали друг о друге. Это уже несколько раз спасало наш бекон.
JasonD
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.