Резюме
- Использование всех шаблонов ситуативно, и выгода (если есть) всегда заключается в уменьшении сложности.
- MVVM помогает нам распределить обязанности между классами в приложении с графическим интерфейсом.
- ViewModel проецирует данные из модели в формат, соответствующий представлению.
- Для тривиальных проектов MVVM не нужен. Использование только вида достаточно.
- Для простых проектов разделение ViewModel / Model может быть ненужным, и достаточно просто использовать Model и View.
- Model и ViewModel не должны существовать с самого начала и могут быть введены, когда они необходимы.
Когда использовать шаблоны и когда их избегать
Для достаточно простого применения каждый шаблон проектирования является излишним. Предположим, вы пишете приложение с графическим интерфейсом, которое отображает одну кнопку, которая при нажатии показывает «Hello world». В этом случае шаблоны проектирования, такие как MVC, MVP, MVVM, все добавляют большую сложность, не добавляя никакой ценности.
В общем, всегда сложно принять шаблон проектирования только потому, что он несколько подходит. Шаблоны проектирования должны использоваться для уменьшения сложности, либо путем непосредственного уменьшения общей сложности, либо путем замены незнакомой сложности на знакомую сложность. Если шаблон проектирования не может уменьшить сложность одним из этих двух способов, не используйте его.
Чтобы объяснить знакомую и незнакомую сложность, возьмите следующие 2 последовательности символов:
- "D. € |? Ré% DFA с"
- "CorrectHorseBatteryStaple"
Хотя вторая последовательность символов в два раза длиннее первой последовательности, ее легче читать, быстрее писать и легче запомнить, чем первую последовательность, и все это потому, что она более знакома. То же самое относится и к знакомым шаблонам в коде.
Помните о том, что некоторые шаблоны могут быть знакомы не всем разработчикам, которые собираются работать с кодом в будущем. В терминах предыдущего примера следующая последовательность может быть, а может и не быть легче запомнить, чем любая из приведенных выше последовательностей, в зависимости от опыта и подготовки человека, помнящего ее: «3.14159265358979323846264338327950». В некоторых случаях, когда используются более продвинутые шаблоны проектирования, имеет смысл использовать шаблон проектирования, только если разработчики обслуживания уже знакомы с ним.
MVVM
Тем не менее, давайте рассмотрим тему MVVM на примере. MVVM объясняет, как распределять обязанности между классами в приложении с графическим интерфейсом (или между уровнями - подробнее об этом позже), с целью иметь небольшое количество классов, сохраняя при этом количество обязанностей в классе небольшим и четко определенным.
«Правильный» MVVM предполагает, по крайней мере, умеренно сложное приложение, которое работает с данными, которые оно получает «откуда-то». Он может получать данные из базы данных, файла, веб-службы или из множества других источников.
* * Пример 1 042
В нашем примере у нас есть 2 класса View
и Model
, но нет ViewModel
. Model
упаковывает csv-файл, который он читает при запуске, и сохраняет его при завершении работы приложения со всеми изменениями, внесенными пользователем в данные. View
- это класс Window, который отображает данные из Model
в таблице и позволяет пользователю редактировать данные. Содержимое csv может выглядеть примерно так:
ID, Name, Price
1, Stick, 5$
2, Big Box, 10$
3, Wheel, 20$
4, Bottle, 3$
Новые требования: Показать цену в евро
Теперь нас просят внести изменения в наше приложение. Данные состоят из двумерной сетки, в которой уже есть столбец «цена», содержащий цену в долларах США. Нам нужно добавить новый столбец, который показывает цены в евро в дополнение к ценам в долларах США на основе заранее определенного курса. Формат csv-файла не должен изменяться, поскольку другие приложения работают с тем же файлом, и эти другие приложения не находятся под нашим контролем.
Возможное решение - просто добавить новый столбец в класс Model
. Это не лучшее решение, потому что Model
сохраняет все данные, которые он предоставляет в CSV - и мы не хотим, чтобы новый столбец цен в евро в CSV. Таким образом, изменение Model
было бы нетривиальным, и было бы также сложнее описать, что делает класс Model, который является запахом кода .
Мы также можем внести изменения в View
, но наше текущее приложение использует привязку данных для непосредственного отображения данных, как это предусмотрено нашим классом Model
. Поскольку наша структура графического интерфейса пользователя не позволяет нам вводить дополнительный вычисляемый столбец в таблицу, когда таблица связана с данными, привязанными к источнику данных, нам необходимо внести значительные изменения в View
, чтобы эта работа работала, чтобы View
намного сложнее.
Представляем модель представления
В приложении нет ViewModel
, потому что до сих пор Model
представляет данные в точности так, как это нужно Csv, как и в View
. Наличие ViewModel
между ними добавило бы сложности без цели. Но теперь, когда Model
больше не представляет данные так, как это нужно View
, мы пишем ViewModel
. ViewModel
проецирует данные Model
таким образом, что View
может быть простым. Ранее класс View
подписывался на класс Model
. Теперь новый класс ViewModel
подписывается на класс Model
и предоставляет данные Model
для View
- с дополнительным столбцом, отображающим цену в евро. View
больше не знает Model
, теперь он знает только ViewModel
, который с точки View
выглядит так же, как Model
ранее - за исключением того, что открытые данные содержат новое чтение только столбец.
Новые требования: другой способ форматирования данных
Следующий запрос клиента состоит в том, что мы не должны отображать данные в виде строк в таблице, а вместо этого отображать информацию о каждом элементе (иначе называемую строку) в виде карточки / ящика и отображать 20 ячеек на экране в сетке 4x5 , показывая 20 коробок одновременно. Поскольку мы придерживались простой логики View
, мы просто полностью заменили View
новым классом, который работает по желанию клиента. Конечно, есть еще один клиент, который предпочел старый View
, поэтому теперь нам нужно поддерживать оба. Поскольку вся общая бизнес-логика уже находится в ViewModel
, это не является большой проблемой. Таким образом, мы можем решить эту проблему, переименовав класс View в TableView
и написав новый класс CardView
, который показывает данные в формате карты. Нам также придется написать некоторый связующий код, который может быть одним из элементов функции запуска.
Новые требования: динамический обменный курс
Следующий запрос клиента заключается в том, чтобы мы брали курс обмена из Интернета, а не использовали предопределенный курс обмена. Это тот момент, когда мы возвращаемся к моему предыдущему утверждению о «слоях». Мы не меняем наш класс Model
, чтобы обеспечить обменный курс. Вместо этого мы пишем (или находим) совершенно независимый дополнительный класс, который обеспечивает обменный курс. Этот новый класс становится частью уровня модели, и наш ViewModel
объединяет информацию о csv-модели и модели обменного курса, которую он затем представляет View
. Для этого изменения старый класс Model и класс View даже не нужно трогать. Нам нужно переименовать класс Model в CsvModel
, и мы вызываем новый класс ExchangeRateModel
.
Если бы мы не ввели ViewModel, когда мы это делали, а вместо этого ждали до этого момента, объем работы по внедрению ViewModel сейчас будет выше, потому что нам нужно удалить значительные объемы функциональности из обоих View
и Model
и перенести функциональность в ViewModel
.
Послесловие о юнит-тестах
Основная цель MVVM не в том, чтобы код в Model и ViewModel можно было поместить в модульный тест. Основная цель MVVM состоит в том, чтобы код был разбит на классы с небольшим количеством четко определенных обязанностей. Одним из нескольких преимуществ наличия кода, состоящего из классов с небольшим количеством четко определенных обязанностей, является то, что легче поместить код в модульный тест. Гораздо большее преимущество в том, что код легче понять, поддерживать и изменять.