Включите исполнение JavaScript в браузере, чтобы запустить приложение.

Что такое MVVM-шаблон: как работает, преимущества и примеры использования

Разберем что такое MVVM-шаблон (Model-View-ViewModel) простыми словами. Для чего применяются MVVM-паттерны в интерфейсах, как работают, отличие от других шаблонов, преимущества и недостатки Model-View-ViewModel. Примеры кода приложений с применением MVVM-шаблонов.

Что такое MVVM (Model-View-ViewModel)

Шаблон проектирования MVVM (сокращение от Model-View-ViewModel) применяется в сфере разработки ПО. Он разделяет приложение на отдельные слои:

  • модель — содержит логику и данные самого приложения;
  • представление — отображает данные в виде пользовательского интерфейса;
  • ViewModel — соединяет представление с моделью, управляя потоком данных между ними, обеспечивая представление особой логикой.

Для чего нужно применять MVVM-паттерны

Паттерны MVVM полезны в следующих случаях:

  • разделение ответственности — приложение разделяется на три разных слоя, что позволяет разработчикам сосредоточиться на конкретных аспектах реализации;
  • тестируемость — модель отделена от представления, что облегчает тестирование каждого слоя независимо друг от друга;
  • повторное использование кода— ViewModel может содержать логику, специфичную для представления, которая впоследствии может быть повторно использована в нескольких представлениях;
  • разработка, управляемая тестированием (TDD) — MVVM хорошо подходит для TDD, поскольку позволяет легко тестировать бизнес-логику в модели;
  • сложные пользовательские интерфейсы — подход упрощает управление сложными пользовательскими интерфейсами, где представление может динамически изменяться в зависимости от состояния модели;
  • привязка данных — в шаблоне реализована двусторонняя привязка данных между моделью и представлением для автоматического обновления интерфейса после изменения данных;
  • асинхронные операции — в MVVM реализован чистый способ обработки асинхронных операций, таких как сетевые вызовы.

Как работает MVVM

Разберем отдельно, как работают все три слоя в MVVM-подходе и как они связаны между собой.

  1. Модель содержит бизнес-логику в сочетании с данными, необходимыми для работы приложения. Она не имеет прямой зависимости ни от представления, ни от ViewModel.
  2. Представление — это слой интерфейса пользователя в приложении. Оно показывает всю необходимую информацию пользователю, параллельно обрабатывая его действия. Представление не должно включать в себя логику, связанную с какой-либо обработкой данных или бизнес-логикой.
  3. 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-паттерны помогают создавать хорошо спроектированные, тестируемые, поддерживаемые приложения.