вступление
В MVVM обычная практика состоит в том, чтобы представления находили свои модели представления, разрешая их из контейнера внедрения зависимостей (DI). Это происходит автоматически, когда контейнеру предлагается предоставить (разрешить) экземпляр класса View. Контейнер вводит ViewModel в View, вызывая конструктор View, который принимает параметр ViewModel; эта схема называется инверсией управления (IoC).
Преимущества DI
Основное преимущество здесь заключается в том, что контейнер можно настроить во время выполнения с инструкциями о том, как разрешать типы, которые мы запрашиваем у него. Это обеспечивает большую тестируемость, давая ему указание разрешать типы (представления и модели представления), которые мы используем, когда наше приложение действительно работает, но инструктируя его по-другому при запуске модульных тестов для приложения. В последнем случае у приложения даже не будет пользовательского интерфейса (он не работает; есть только тесты), поэтому контейнер будет разрешать фиктивные типы вместо «нормальных» типов, используемых при запуске приложения.
Проблемы, проистекающие из DI
До сих пор мы видели, что подход DI позволяет легко тестировать приложение, добавляя уровень абстракции над созданием компонентов приложения. У этого подхода есть одна проблема: он плохо работает с визуальными дизайнерами, такими как Microsoft Expression Blend.
Проблема в том, что как при обычном запуске приложения, так и при запуске модульного теста кто-то должен настроить контейнер с инструкциями о том, какие типы разрешать; кроме того, кто-то должен попросить контейнер разрешить представления, чтобы в них можно было внедрить модели представления.
Однако во время разработки нашего кода нет . Дизайнер пытается использовать отражение для создания экземпляров наших представлений, что означает, что:
- Если конструктору View требуется экземпляр ViewModel, дизайнер вообще не сможет создать экземпляр View - он каким-то контролируемым образом выдаст ошибку.
- Если View имеет конструктор без параметров, View будет создан, но так и
DataContext
будет, null
поэтому мы получим «пустое» представление в дизайнере, что не очень полезно.
Введите ViewModelLocator
ViewModelLocator - это дополнительная абстракция, используемая следующим образом:
- Сам View создает экземпляр ViewModelLocator как часть своих ресурсов и привязывает свой DataContext к свойству ViewModel локатора.
- Локатор каким-то образом определяет, находимся ли мы в режиме разработки
- Если не в режиме разработки, локатор возвращает ViewModel, который он разрешает из контейнера DI, как описано выше.
- В режиме разработки локатор возвращает фиксированную «фиктивную» ViewModel, используя свою собственную логику (помните: во время разработки нет контейнера!); эта ViewModel обычно предварительно заполнена фиктивными данными
Конечно, это означает, что View должен иметь конструктор без параметров для начала (иначе дизайнер не сможет создать его экземпляр).
Резюме
ViewModelLocator - это идиома, которая позволяет вам сохранить преимущества DI в вашем приложении MVVM, а также позволяет вашему коду хорошо взаимодействовать с визуальными дизайнерами. Иногда это называется «смешиваемость» вашего приложения (имеется в виду Expression Blend).
После переваривания выше, см практического примера здесь .
Наконец, использование шаблонов данных - это не альтернатива использованию ViewModelLocator, а альтернатива использованию явных пар View / ViewModel для частей вашего пользовательского интерфейса. Часто вы можете обнаружить, что нет необходимости определять View для ViewModel, потому что вместо этого вы можете использовать шаблон данных.