Я прошу прощения за длинный вопрос, это звучит немного как напыщенная речь, но я обещаю, что это не так! Я кратко изложил свой вопрос (ы) ниже
В мире MVC все просто. Модель имеет состояние, представление показывает модель, а контроллер выполняет какие-либо действия с моделью (в основном), контроллер не имеет состояния. Для этого у Контроллера есть некоторые зависимости от веб-сервисов, хранилища, всего. Когда вы создаете экземпляр контроллера, вы заботитесь о предоставлении этих зависимостей, и ничего больше. Когда вы выполняете действие (метод в Controller), вы используете эти зависимости для извлечения или обновления Модели или вызова какой-либо другой доменной службы. Если есть какой-либо контекст, скажем, что какой-то пользователь хочет видеть детали определенного элемента, вы передаете Id этого элемента в качестве параметра для Action. Нигде в контроллере нет ни одной ссылки на какое-либо состояние. Все идет нормально.
Введите MVVM. Я люблю WPF, я люблю привязку данных. Я люблю фреймворки, которые делают привязку данных к ViewModels еще проще (с использованием Caliburn Micro atm). Я чувствую, что в этом мире все не так просто. Давайте снова выполнить упражнение: модель имеет состояние, вид показывает модель представления, и ViewModel делает материал к / с моделью ( в основном), модель представление действительно есть состояние! (уточнить, может быть , он делегирует все свойства в одной или нескольких моделей, но это означает , что он должен иметь ссылку на одну сторону модели или другой, что государство само по себе) Для того, чтобы сделатьВещи ViewModel имеют некоторые зависимости от веб-сервисов, репозитория, лота. Когда вы создаете экземпляр ViewModel, вы заботитесь о предоставлении этих зависимостей, а также о состоянии. И это, дамы и господа, раздражает меня до бесконечности.
Всякий раз, когда вам нужно создать экземпляр ProductDetailsViewModel
из ProductSearchViewModel
(из которого вы позвонили, ProductSearchWebService
который в свою очередь вернулся IEnumerable<ProductDTO>
, все еще со мной?), Вы можете сделать одну из следующих вещей:
- позвоните
new ProductDetailsViewModel(productDTO, _shoppingCartWebService /* dependcy */);
, это плохо, представьте себе еще 3 зависимости, это означает, чтоProductSearchViewModel
необходимо учитывать и эти зависимости. Также изменение конструктора является болезненным. - Вызовите
_myInjectedProductDetailsViewModelFactory.Create().Initialize(productDTO);
, фабрика просто Func, они легко генерируются большинством IoC-фреймворков. Я думаю, что это плохо, потому что методы Init - это утечка абстракции. Вы также не можете использовать ключевое слово readonly для полей, которые установлены в методе Init. Я уверен, что есть еще несколько причин. - вызов
_myInjectedProductDetailsViewModelAbstractFactory.Create(productDTO);
Итак ... это шаблон (абстрактная фабрика) , которая, как правило , рекомендуется для такого рода проблемы. Я думал, что это был гений, так как он удовлетворял мою тягу к статической типизации, пока я на самом деле не начал использовать его. Объем стандартного кода, я думаю, слишком велик (вы знаете, помимо нелепых имен переменных, которые я использую). Для каждой модели ViewModel, для которой требуются параметры времени выполнения, вы получите два дополнительных файла (заводской интерфейс и реализацию), и вам нужно будет ввести зависимости без времени выполнения, например, 4 дополнительных раза. И каждый раз, когда меняются зависимости, вы можете изменить их и на фабрике. Такое ощущение, что я даже больше не использую DI-контейнер. (Я думаю, что у Castle Windsor есть какое-то решение для этого [со своими собственными недостатками, поправьте меня, если я ошибаюсь]). - сделать что-нибудь с анонимными типами или словарем. Мне нравится моя статическая типизация.
Так что да. Смешивание состояния и поведения таким образом создает проблему, которой вообще нет в MVC. И я чувствую, что в настоящее время нет действительно адекватного решения этой проблемы. Теперь я хотел бы наблюдать некоторые вещи:
- Люди на самом деле используют MVVM. Таким образом, они либо не заботятся обо всем вышеперечисленном, либо у них есть какое-то блестящее другое решение.
- Я не нашел подробного примера MVVM с WPF. Например, пример проекта NDDD очень помог мне понять некоторые концепции DDD. Мне бы очень понравилось, если бы кто-то мог указать мне в направлении чего-то похожего для MVVM / WPF.
- Может быть, я неправильно делаю MVVM, и я должен перевернуть свой дизайн с ног на голову. Может быть, у меня не должно быть этой проблемы вообще. Ну, я знаю, что другие люди задавали тот же вопрос, поэтому я думаю, что я не единственный.
Подвести итоги
- Правильно ли я пришел к выводу, что наличие ViewModel в качестве точки интеграции как состояния, так и поведения является причиной некоторых трудностей с шаблоном MVVM в целом?
- Является ли использование шаблона абстрактной фабрики единственным / лучшим способом создания экземпляра ViewModel статически типизированным способом?
- Есть ли что-то похожее на детальную справочную реализацию?
- Имеет ли много моделей ViewModel с состоянием / поведением запах дизайна?
Ответы:
Проблема зависимостей при запуске новой модели представления может быть решена с помощью IOC.
При настройке контейнера ...
Когда вам нужна модель вашего вида:
При использовании каркаса, такого как caliburn micro, часто уже присутствует та или иная форма контейнера МОК.
источник
Я ежедневно работаю с ASP.NET MVC и работаю над WPF более года, и вот как я это вижу:
MVC
Контроллер должен координировать действия (получить это, добавить это).
Представление отвечает за отображение модели.
Модель, как правило, включает в себя данные (например, UserId, FirstName), а также состояние (например, заголовки) и, как правило, зависит от конкретного вида.
MVVM
Модель обычно содержит только данные (например, UserId, FirstName) и обычно передается
Модель представления охватывает поведение представления (методы), его данные (модель) и взаимодействия (команды) - аналогично активному шаблону MVP, когда докладчик осведомлен о модели. Модель вида специфична для вида (1 вид = 1 вид модели).
Представление отвечает за отображение данных и привязку данных к модели представления. Когда представление создается, обычно с ним создается связанная с ним модель представления.
Следует помнить, что шаблон представления MVVM специфичен для WPF / Silverlight из-за их привязки к данным.
Представление, как правило, знает, с какой моделью представления оно связано (или его абстракция).
Я бы посоветовал вам рассматривать модель представления как одноэлементную, даже если она создается для каждого представления. Другими словами, вы должны быть в состоянии создать его через DI через контейнер IOC и вызвать соответствующие методы, чтобы сказать; загрузить его модель на основе параметров. Что-то вроде этого:
В качестве примера в этом случае вы не будете создавать модель представления, специфичную для обновляемого пользователя, - вместо этого модель будет содержать данные, специфичные для пользователя, которые загружаются посредством некоторого вызова модели представления.
источник
Краткий ответ на ваши вопросы:
Длинная версия:
Мы столкнулись с той же проблемой, и нашли некоторые вещи, которые могут вам помочь. Хотя я не знаю «волшебного» решения, эти вещи немного облегчают боль.
Реализуйте привязываемые модели из DTO для отслеживания и проверки изменений. Эти «Data» -ViewModels не должны зависеть от сервисов и не приходят из контейнера. Они могут быть просто «новы» отредактированы, переданы и даже могут быть выведены из DTO. Суть в том, чтобы реализовать модель, специфичную для вашего приложения (например, MVC).
Развяжите ваши ViewModels. Caliburn позволяет легко соединить ViewModels вместе. Он даже предлагает это через свою модель экрана / проводника. Но эта связь делает ViewModels трудными для модульного тестирования, создает много зависимостей и самое важное: накладывает бремя управления жизненным циклом ViewModel на ваши ViewModels. Один из способов развязать их - использовать что-то вроде службы навигации или контроллера ViewModel. Например
открытый интерфейс IShowViewModels {void Show (объект inlineArgumentsAsAnonymousType, строка regionId); }
Еще лучше сделать это с помощью какой-либо формы обмена сообщениями. Но важно не обрабатывать жизненный цикл ViewModel из других моделей ViewModel. В MVC контроллеры не зависят друг от друга, а в MVVM ViewModels не должны зависеть друг от друга. Интегрируйте их другими способами.
INeedData<T1,T2,...>
и принудительное применение параметров создания безопасных типов, оно того не стоит. Также не стоит создавать фабрики для каждого типа ViewModel. Большинство контейнеров IoC предоставляют решения для этого. Вы получите ошибки во время выполнения, но разъединение и тестируемость устройства того стоят. Вы все еще проводите какой-то интеграционный тест, и эти ошибки легко обнаруживаются.источник
Как я обычно делаю (используя PRISM), каждая сборка содержит модуль инициализации контейнера, где все интерфейсы, экземпляры регистрируются при запуске.
И, учитывая ваши примеры классов, они будут реализованы следующим образом, с контейнером, проходящим через весь путь. Таким образом, любые новые зависимости могут быть легко добавлены, поскольку у вас уже есть доступ к контейнеру.
Весьма распространено иметь класс ViewModelBase, из которого получены все ваши модели представлений, который содержит ссылку на контейнер. Если вы привыкли разрешать все модели представлений вместо
new()'ing
них, это должно значительно упростить разрешение всех зависимостей.источник
Иногда лучше перейти к простейшему определению, а не к полноценному примеру: http://en.wikipedia.org/wiki/Model_View_ViewModel, возможно, чтение примера ZK Java более показательно, чем C #.
В другой раз прислушайся к своему внутреннему инстинкту
Являются ли ваши модели объектно-табличными сопоставлениями? Возможно, ORM поможет сопоставить объекты домена при обработке бизнеса или обновлении нескольких таблиц.
источник