MVVM - это бинты для плохо спроектированных слоев привязки данных. В частности, он широко использовался в мире WPF / silverlight / WP7 из-за ограничений в привязке данных в WPF / XAML.
С этого момента, я собираюсь предположить, что мы говорим о WPF / XAML, поскольку это прояснит ситуацию. Давайте рассмотрим некоторые недостатки, которые MVVM намеревается устранить в WPF / XAML.
Форма данных против формы пользовательского интерфейса
«VM» в MVVM создает набор объектов, определенных в C #, которые отображаются на набор объектов представления, определенных в XAML. Эти объекты C # обычно подключаются к XAML через свойства DataContext в объектах представления.
В результате граф объекта viewmodel должен отображаться на графе объекта презентации вашего приложения. Это не означает, что сопоставление должно быть взаимно-однозначным, но если элемент управления списком содержится в элементе управления окна, то должен быть способ добраться от объекта DataContext окна до объекта, который описывает данные этого списка.
Граф объекта модели представления успешно отделяет граф объекта модели от графа объекта пользовательского интерфейса, но за счет дополнительного слоя модели представления, который должен быть построен и поддерживаться.
Если я хочу переместить некоторые данные с экрана A на экран B, мне нужно возиться с моделями представления. По мнению делового человека, это изменение пользовательского интерфейса. Это должно происходить исключительно в мире XAML. К сожалению, это редко может. Хуже того, в зависимости от того, как структурированы модели представления и насколько активно изменяются данные, для выполнения этого изменения может потребоваться совсем небольшая повторная маршрутизация данных.
Обойти невыразительную привязку данных
Привязки WPF / XAML недостаточно выразительны. Вы в основном получаете способ добраться до объекта, путь свойства для обхода и связывающие преобразователи, чтобы адаптировать значение свойства данных к тому, что требует объект представления.
Если вам нужно привязать свойство в C # к чему-то более сложному, вам не повезло. Я никогда не видел приложение WPF без конвертера привязки, который превращал бы true / false в Visible / Collapsed. Многие приложения WPF также имеют свойство NegatingVisibilityConverter или аналогичное, которое меняет полярность. Это должно быть отключение сигнала тревоги.
MVVM дает вам рекомендации по структурированию кода C #, которые можно использовать для сглаживания этого ограничения. Вы можете выставить свойство на вашей модели представления с именем SomeButtonVisibility и просто связать его с видимостью этой кнопки. Теперь ваш XAML хорош и хорош ... но вы превратились в клерка - теперь вы должны выставлять + обновлять привязки в двух местах (пользовательский интерфейс и код на C #), когда ваш пользовательский интерфейс развивается. Если вам нужна эта же кнопка для отображения на другом экране, вы должны выставить похожее свойство в модели представления, к которой может получить доступ этот экран. Хуже того, я не могу просто посмотреть на XAML и увидеть, когда кнопка будет видна больше. Как только привязки становятся немного нетривиальными, я должен выполнять детективную работу в коде C #.
Доступ к данным агрессивно ограничен
Поскольку данные обычно поступают в пользовательский интерфейс через свойства DataContext, трудно представить глобальные данные или данные сеанса единообразно во всем приложении.
Идея «пользователя, вошедшего в данный момент», является отличным примером - часто это действительно глобальная вещь в экземпляре вашего приложения. В WPF / XAML очень сложно обеспечить глобальный доступ к текущему пользователю согласованным образом.
Что я хотел бы сделать, так это использовать слово «CurrentUser» в привязках данных, чтобы свободно ссылаться на пользователя, вошедшего в систему. Вместо этого я должен убедиться, что каждый DataContext дает мне способ добраться до текущего объекта пользователя. MVVM может приспособиться к этому, но модели представлений будут беспорядочными, поскольку все они должны предоставлять доступ к этим глобальным данным.
Пример, где MVVM падает
Скажем, у нас есть список пользователей. Рядом с каждым пользователем мы хотим отобразить кнопку «удалить пользователя», но только в том случае, если в данный момент зарегистрированный пользователь является администратором. Кроме того, пользователям не разрешается удалять себя.
Объекты вашей модели не должны знать о текущем вошедшем в систему пользователе - они будут просто представлять записи пользователя в вашей базе данных, но каким-то образом зарегистрированный в данный момент пользователь должен подвергаться привязкам данных в строках списка. MVVM требует, чтобы мы создавали объект viewmodel для каждой строки списка, которая составляет текущего зарегистрированного пользователя с пользователем, представленным этой строкой списка, а затем предоставляет свойство «DeleteButtonVisibility» или «CanDelete» для этого объекта viewmodel (в зависимости от ваших чувств о привязке конвертеров).
Этот объект будет выглядеть очень похожим на объект User во многих других отношениях - ему может потребоваться отразить все свойства объекта пользовательской модели и переслать обновления этих данных при их изменении. Это кажется очень странным - опять же, MVVM превращает вас в клерка, заставляя вас поддерживать этот объект, похожий на пользователя.
Подумайте - вам, вероятно, также необходимо представить свойства вашего пользователя в базе данных, модели и представлении. Если у вас есть API между вами и вашей базой данных, то это еще хуже - они представлены в базе данных, сервере API, клиенте API, модели и представлении. Я бы не решался принять шаблон проектирования, в который добавлялся еще один слой, к которому нужно прикасаться при каждом добавлении или изменении свойства.
Хуже того, этот уровень масштабируется в зависимости от сложности вашего пользовательского интерфейса, а не от сложности вашей модели данных. Часто одни и те же данные представлены во многих местах и в вашем пользовательском интерфейсе - это не только добавляет слой, но добавляет слой с большой дополнительной площадью поверхности!
Как все могло быть
В случае, описанном выше, я хотел бы сказать:
<Button Visibility="{CurrentUser.IsAdmin && CurrentUser.Id != Id}" ... />
CurrentUser будет открыт для всех XAML в моем приложении. Идентификатор будет ссылаться на свойство в DataContext для строки моего списка. Видимость конвертируется из логического значения автоматически. Любые обновления Id, CurrentUser.IsAdmin, CurrentUser или CurrentUser.Id будут инициировать обновление видимости этой кнопки. Очень просто.
Вместо этого WPF / XAML заставляет своих пользователей создавать полный беспорядок. Насколько я могу судить, некоторые творческие блоггеры добавили имя в этот беспорядок, и это имя было MVVM. Не обманывайте себя - это не тот же класс, что и шаблоны проектирования GoF. Это уродливый хак, чтобы обойти уродливую систему привязки данных.
(Этот подход иногда упоминается как «Функциональное реактивное программирование» на случай, если вы ищете дальнейшее чтение).
В заключение
Если вы должны работать в WPF / XAML, я все еще не рекомендую MVVM.
Вы хотите, чтобы ваш код был структурирован, как в приведенном выше примере «как все могло бы быть» - модель экспонировалась непосредственно для просмотра со сложными выражениями привязки данных + гибкими приведениями значений. Это намного лучше - более читабельно, более доступно для записи и более легко обслуживаемо.
MVVM говорит вам, чтобы структурировать ваш код более подробным и менее понятным способом.
Вместо MVVM создайте материал, который поможет вам приблизиться к хорошему опыту: Разработайте соглашение для последовательного представления глобального состояния вашему пользовательскому интерфейсу. Создайте себе некоторые инструменты из конвертеров связывания, MultiBinding и т. Д., Которые позволяют вам выражать более сложные выражения связывания. Создайте для себя библиотеку конвертеров привязки, чтобы облегчить общие случаи принуждения.
Еще лучше - замените XAML чем-то более выразительным. XAML - это очень простой формат XML для создания экземпляров объектов C # - не составит труда найти более выразительный вариант.
Моя другая рекомендация: не используйте наборы инструментов, которые навязывают подобные компромиссы. Они повредят качеству вашего конечного продукта, подталкивая вас к дерьму, как MVVM, вместо того, чтобы сосредоточиться на проблемной области.