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