Резюме
- Використання всіх моделей є ситуативним, і користь (якщо така є) завжди полягає у зменшенні складності.
- MVVM допомагає нам розподілити обов'язки між класами в програмі графічного інтерфейсу.
- ViewModel проектує дані з Моделі у формат, який відповідає поданню.
- Для тривіальних проектів MVVM непотрібний. Достатньо користуватися лише видом.
- Для простих проектів розбиття ViewModel / Model може бути непотрібним, і просто використання Model and View досить добре.
- Model і ViewModel не повинні існувати з самого початку і можуть бути введені, коли вони потрібні.
Коли використовувати шаблони, а коли уникати їх
Для досить простого застосування кожен шаблон дизайну є надмірним. Припустимо, ви пишете програму графічного інтерфейсу, яка відображає одну кнопку, яка при натисканні відображає "Hello world". У цьому випадку такі шаблони дизайну, як MVC, MVP, MVVM, додають багато складності, не додаючи жодної цінності.
Загалом, завжди неправильним рішенням є введення шаблону дизайну лише тому, що він дещо відповідає. Структури дизайну слід використовувати для зменшення складності або шляхом безпосереднього зменшення загальної складності, або шляхом заміни незнайомої складності на звичну складність. Якщо шаблон дизайну не може зменшити складність жодним із цих двох способів, не використовуйте його.
Для пояснення знайомої та незнайомої складності візьміть наступні 2 послідовності символів:
- "D. € | Ré% dfà? C"
- "CorrectHorseBatteryStaple"
Хоча друга послідовність символів удвічі довша за першу послідовність, її легше читати, швидше писати та легше запам’ятовувати, ніж першу послідовність, все тому, що вона більш звична. Те саме стосується знайомих шаблонів у коді.
Ця проблема набуває іншого виміру, якщо врахувати, що знайомство залежить від читача. Деяким читачам "3.14159265358979323846264338327950" запам'ятати легше, ніж будь-який із наведених вище паролів. Деякі не будуть. Отже, якщо ви хочете використати аромат MVVM, спробуйте використати той, який відображає його найпоширенішу форму певною мовою та структурою, яку ви використовуєте.
МВВМ
Тим не менш, давайте зануримось у тему MVVM на прикладі. MVVM допомагає нам розподілити обов'язки між класами в програмі графічного інтерфейсу (або між рівнями - про це пізніше) з метою мати невелику кількість класів, зберігаючи при цьому кількість обов'язків для класу невеликою та чітко визначеною.
«Правильний» MVVM передбачає принаймні помірно складний додаток, який має справу з даними, отриманими звідкись. Він може отримувати дані з бази даних, файлу, веб-сервісу або з безлічі інших джерел.
Приклад
У нашому прикладі ми маємо 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
У ViewModel
додатку немає, оскільки до цього Model
часу дані подають дані саме так, як це потрібно CSv, а також так, як View
це потрібно. Наявність ViewModel
між ними додало б складності без мети. Але тепер, коли дані Model
більше не подають дані так, як вони View
потрібні, ми пишемо a 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 не полягає в тому, що код у Моделі та ViewModel може бути поміщений в Unit Test. Основна мета MVVM полягає в тому, що код розбивається на класи з невеликою кількістю чітко визначених обов'язків. Однією з кількох переваг наявності коду, що складається з класів з невеликою кількістю чітко визначених обов’язків, є те, що простіше поставити код під модульний тест. Набагато більша перевага полягає в тому, що код легше розуміти, підтримувати та модифікувати.