Что такое MVVM (Model-View-ViewModel)
Шаблон проектирования MVVM (сокращение от Model-View-ViewModel) применяется в сфере разработки ПО. Он разделяет приложение на отдельные слои:
- модель — содержит логику и данные самого приложения;
- представление — отображает данные в виде пользовательского интерфейса;
- ViewModel — соединяет представление с моделью, управляя потоком данных между ними, обеспечивая представление особой логикой.
Для чего нужно применять MVVM-паттерны
Паттерны MVVM полезны в следующих случаях:
- разделение ответственности — приложение разделяется на три разных слоя, что позволяет разработчикам сосредоточиться на конкретных аспектах реализации;
- тестируемость — модель отделена от представления, что облегчает тестирование каждого слоя независимо друг от друга;
- повторное использование кода— ViewModel может содержать логику, специфичную для представления, которая впоследствии может быть повторно использована в нескольких представлениях;
- разработка, управляемая тестированием (TDD) — MVVM хорошо подходит для TDD, поскольку позволяет легко тестировать бизнес-логику в модели;
- сложные пользовательские интерфейсы — подход упрощает управление сложными пользовательскими интерфейсами, где представление может динамически изменяться в зависимости от состояния модели;
- привязка данных — в шаблоне реализована двусторонняя привязка данных между моделью и представлением для автоматического обновления интерфейса после изменения данных;
- асинхронные операции — в MVVM реализован чистый способ обработки асинхронных операций, таких как сетевые вызовы.
Как работает MVVM
Разберем отдельно, как работают все три слоя в MVVM-подходе и как они связаны между собой.
- Модель содержит бизнес-логику в сочетании с данными, необходимыми для работы приложения. Она не имеет прямой зависимости ни от представления, ни от ViewModel.
- Представление — это слой интерфейса пользователя в приложении. Оно показывает всю необходимую информацию пользователю, параллельно обрабатывая его действия. Представление не должно включать в себя логику, связанную с какой-либо обработкой данных или бизнес-логикой.
- ViewModel является промежуточным слоем между двумя другими компонентами. Этот компонент управляет данными, а заодно отвечает за логику, специфичную для представления. ViewModel подписывается на изменения модели, обновляя представление соответствующим этим изменениям образом.
MVVM использует привязку данных для автоматического обновления представления при любых изменениях в модели. Она может быть односторонней или двусторонней:
- односторонняя привязка — данные передаются только от модели к представлению;
- двусторонняя привязка — изменения в представлении также отражаются в модели.
Команды представляют собой механизм для обработки действий пользователя. Они отделяют представление от бизнес-логики, что позволяет ViewModel обрабатывать действия пользователя без необходимости прямого доступа к представлению.
Команды обычно реализуются как объекты, связанные тем или иным способом с элементами пользовательского интерфейса, такими как кнопки или пункты меню. Когда пользователь взаимодействует с элементом интерфейса, вызывается соответствующая команда.
ViewModel гарантирует, что бизнес-логика остается отделенной от представления.
Отличие MVVM от MVC
Архитектурный паттерн проектирования MVVM (Model-View-ViewModel) похож на MVC (Model-View-Controller), но у них есть ряд важных различий.
- Привязка данных: MVVM использует двунаправленную привязку данных, которая автоматически синхронизирует их, что делает код более простым и поддерживаемым. MVC требует явной манипуляции данными контроллером.
- Обязанности: в MVVM модель ответственна за данные, представление — за их отображение, а ViewModel управляет взаимодействием этих двух слоев между собой. В MVC контроллер обрабатывает всю логику приложения.
- Тестируемость: MVVM облегчает тестирование благодаря разделению компонентов. MVC сложнее для тестирования из-за плотной взаимосвязи между контроллером, моделью, представлением.
В целом, MVVM больше подходит для приложений с высокой интерактивностью и быстрой разработкой, а MVC — для программ с более сложной логикой и неопределенными требованиями.
Преимущества MVVM
У MVVM есть целый ряд преимуществ по сравнению с другими архитектурными паттернами разработки программного обеспечения.
- Привязка данных: MVVM использует двунаправленную привязку, которая автоматически синхронизирует компоненты между собой. Это устраняет необходимость в явной манипуляции данными и делает код более простым и поддерживаемым.
- Разделение обязанностей: паттерн четко разделяет все три компонента. Это делает код модульным, а потому тестировать его становится легче.
- Тестируемость: паттерн облегчает тестирование, поскольку компоненты разделены и имеют четко определенные обязанности. Это позволяет разработчикам легко изолировать отдельные компоненты, чтобы потом их тестировать.
- Упрощенная разработка: паттерн упрощает разработку, особенно для приложений с высокой интерактивностью. Привязка данных устраняет необходимость в написании большого количества кода для обработки обновлений данных.
- Быстрое прототипирование: MVVM подходит для быстрого прототипирования, поскольку позволяет программистам оперативно разрабатывать пользовательские интерфейсы, не беспокоясь о сложной логике.
Недостатки
Несмотря на очевидные преимущества, у паттерна есть свои недостатки.
- Сложность: он чаще всего оказывается сложнее для понимания и реализации, чем другие паттерны, особенно для начинающих разработчиков.
- Производительность: привязка данных может повлиять на производительность в больших и сложных приложениях, особенно если используется чрезмерное количество привязок.
- Отладка: иногда бывает сложно отладить проблемы в приложениях, поскольку поток данных может быть неявным, а потому сложным для отслеживания.
- Зависимость от фреймворков: MVVM часто полагается на фреймворки для реализации привязки данных и управления жизненным циклом ViewModel. Выбор инструментов и технологий чаще всего оказывается ограничен.
- Повышенная сложность кода: хотя MVVM упрощает разработку некоторых аспектов приложения, он часто приводит к более сложному коду в других областях, таких как управление состоянием или обработка событий.
Применение MVVM-паттернов в коммерческой разработке
Приведем примеры применения MVVM в коммерческой среде.
Разработка пользовательских приложений с высокой степенью привязки данных. Технология позволяет разработчикам быстро создавать сложные пользовательские интерфейсы, не беспокоясь о синхронизации данных между компонентами архитектуры.
Разработка бизнес-приложений, где требуется четкое разделение между представлением и моделью данных. Привязка позволяет разработчикам создавать гибкие, легко изменяемые приложения, которые можно без особых проблем поддерживать и обновлять.
Одностраничные приложения (SPA): MVVM широко используется в разработке SPA на таких фреймворках, как Angular или Vue.js. Он помогает легко управлять состоянием приложения, а заодно создавать динамические и интерактивные интерфейсы.
Мобильная разработка: паттерн также применяется в мобильной разработке для создания нативных приложений для iOS и Android. Он обеспечивает чистую архитектуру и упрощает разработку сложных мобильных приложений.
Пример кода MVVM-приложения
Приведем пример кода всех трех компонентов MVVM-приложения с пояснениями.
ViewModel (Java):
public class MainViewModel {
// Наблюдаемая строка
private ObservableField<String> message = new ObservableField<>("");
// Наблюдатель за изменением поля
public void onMessageChanged(TextWatcher textWatcher) {
message.addOnPropertyChangedCallback(textWatcher);
}
// Геттер для наблюдаемой строки
public String getMessage() {
return message.get();
}
// Установка нового значения наблюдаемой строки
public void setMessage(String newMessage) {
message.set(newMessage);
}
}
Пояснения к ViewModel:
- ObservableField<String> message — наблюдаемая строка, которая хранит сообщение пользователя;
- onMessageChanged(TextWatcher textWatcher) — метод, позволяющий подписаться на изменения строки message;
- getMessage() — геттер для получения текущего значения строки message;
- setMessage(String newMessage) — сеттер для установки нового значения строки message.
View (XML):
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewModel"
type="com.example.mvvmdemo.MainViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Введите сообщение"
android:text="@{viewModel.message}" />
<TextView
android:id="@+id/text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Сообщение: @{viewModel.message}" />
</LinearLayout>
</layout>
Пояснения к View:
- <data> — раздел данных, который определяет переменную viewModel для привязки к экземпляру MainViewModel;
- <EditText> — поле ввода, которое получает сообщение от пользователя и привязывается к viewModel.message с помощью атрибута android:text;
- <TextView> — текстовое поле, которое отображает сообщение, привязанное к viewModel.message.
Привязка данных (Java)
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Получение привязки данных
MainViewModel viewModel = new MainViewModel();
BindingUtil.setContentView(this, R.layout.activity_main, viewModel);
// Настройка прослушивателя изменений текста
EditText editText = findViewById(R.id.edit_text);
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// Не реализовано
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Обновление сообщения при изменении текста
viewModel.setMessage(s.toString());
}
@Override
public void afterTextChanged(Editable s) {
// Не реализовано
}
});
}
}
Пояснения к привязке данных:
- MainViewModel viewModel = new MainViewModel() — создание экземпляра MainViewModel;
- BindingUtil.setContentView(this, R.layout.activity_main, viewModel) — привязка viewModel к макету activity_main;
- EditText editText = findViewById(R.id.edit_text) — получение ссылки на элемент EditText;
- editText.addTextChangedListener(new TextWatcher()) — добавление прослушивателя изменений текста к элементу EditText;
- в методе onTextChanged прослушивателя изменений текста вызывается viewModel.setMessage(s.toString()) для обновления наблюдаемой строки message с новым вводимым текстом.
MVVM улучшает разделение между логикой и представлением, повышает тестируемость, облегчает повторное использование кода. Он также позволяет разработчикам независимо обновлять модель и представление без влияния на остальные слои приложения.
В целом, MVVM-паттерны помогают создавать хорошо спроектированные, тестируемые, поддерживаемые приложения.