Рефакторинг Bloated ViewModel

Я пишу приложение PRISM/MVVM/WPF. Это приложение LOB, поэтому существует множество сложных правил. Я заметил, что модель просмотра начинает раздуваться. Есть два основных вопроса.

Один из них заключается в том, что для поддержания MVVM я делаю много вещей, которые кажутся взломанными, например, добавление кучи свойств в мою виртуальную машину. Представление связывается с этими свойствами, чтобы отслеживать то, что похоже на просмотр конкретной информации. Например, логическое отслеживание состояния долгого процесса в виртуальной машине, поэтому представление может отключить некоторые из его элементов управления, пока работает длительный процесс. Я читал, что этот вопрос можно решить с помощью Attached Behaviors. Я посмотрю больше на это. В примерах приложений MVVM, которые вы видите в Интернете, это не большая проблема, потому что они чрезмерно упрощены.

Другой проблемой является количество команд в моей виртуальной машине. Сейчас есть четыре команды. Я определяю команды на виртуальной машине, используя Josh Smith RelayCommand (в основном DelegateCommand в PRISM), поэтому вся бизнес-логика живет в виртуальной машине. Я решил переместить каждую команду в отдельную единицу работ. Я не уверен, как это сделать.

Какие шаблоны вы используете, чтобы ваши виртуальные машины были чистыми? Я уже чувствую, что кто-то отвечает: "Ваше мнение и виртуальная машина слишком сложны, вы должны разбить их на многие точки зрения/виртуальные машины". Это, конечно, не слишком сложно с точки зрения Ux - есть две кнопки, выпадающая скобка и список. Кроме того, с логической точки зрения, это один сплошной домен. Сказав это, я очень заинтересован в том, чтобы узнать, как другие сталкиваются с этим типом проблемы.

Спасибо за ваш вклад.

Ответы

Ответ 1

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

Я очень беспокоюсь о "раздувании" в базовом классе ViewModel, но не столько в конкретных подклассах ViewModel. Это часто заставляет забрасывать зависимость, используемую 2-3 ViewModels, в базовый класс, но этого следует избегать.

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

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

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

Ответ 2

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

Не могли бы вы рассказать о своей ViewModel?

EDIT Основываясь на комментариях.

SaveCustomer и DeleteCustomer звучат как методы Model или Service, поэтому я бы поставил их в какой-то уровень Persistence.

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

В общем, стоит поставить общий код (который несколько ViewModels могут использовать) в службу, а затем бросить эту службу в ваш IOC. Таким образом, ваши модели ViewModels становятся легкими "директорами" информации из представления или модели или службы.

Ответ 3

У меня есть две основные мысли по этому поводу, но YMMV.

Во-первых, я бы переместил конкретный код реализации из виртуальной машины в "службу", которая в основном является классом, который выполняет эту работу. Это может быть, например, класс customerService, который реализует методы сохранения, удаления, обновления и реализации RelayCommand, будет вызывать службу для выполнения действия и обновлять любые свойства вида по мере необходимости.

Примером может быть то, что RelayCommand может установить значение свойства ShowProgressBar, которое связано с видимостью индикатора выполнения. Затем он будет вызывать функцию сохранения в сервисе, и когда она будет завершена (это должно быть асинхронным btw), она должна снова обновить "ShowProgressBar". Таким образом, ViewModel контролирует состояние представления независимо от логики основного приложения.

Во-вторых, я бы посмотрел в базовую модель просмотра, которую вы используете, и старайтесь держать это как можно проще, добавляя только те компоненты, которые являются общими для всех ваших моделей. Затем вы можете создать другие модели базового представления или интерфейсы. Например, вы можете решить, что все ваши модели представлений будут показывать свойство ShowProgressBar, которое может быть привязано к строке выполнения на вашей странице, или вы можете решить создать ProgressViewModelBase или IAllowProgress, которые вы наследуете/реализуете на страницах, требующих индикаторы выполнения.

В самом представлении должен быть как можно меньше кода, вы должны стремиться к 0-обратному коду (хотя есть вещи, которые могут быть сделаны только в коде, к сожалению).

Одна вещь, которую я обнаружил в своем вреде, заключается в том, что создание сложных моделей просмотра может быть дорогостоящим с точки зрения производительности, особенно если вы регистрируете релейные команды и источники/слушатели агрегаторов событий, поэтому сохранение базовой модели представления slim нам очень полезно. Это особенно важно, если вы используете ViewModelCollections для отображения списков объектов Model и где создается представление ViewModel в потоке пользовательского интерфейса (что, скорее всего, если ViewModel создается при создании представления).