MVVM безглуздий? [зачинено]


91

Чи ортодоксальне впровадження MVVM безглуздо? Я створюю нову програму, і я розглядав Windows Forms та WPF. Я вибрав WPF, оскільки він захищений від майбутнього і пропонує велику гнучкість. Менше коду та легше вносити суттєві зміни у ваш інтерфейс за допомогою XAML.

Оскільки вибір для WPF очевидний, я зрозумів, що я можу також пройти весь шлях, використовуючи MVVM як мою архітектуру додатків, оскільки він пропонує суміш, проблеми розділення та перевірку одиниць. Теоретично це здається прекрасним, як святий Грааль програмування інтерфейсу користувача. Ця коротка пригода; однак перетворився на справжній головний біль. Як і очікувалося на практиці, я виявляю, що я обміняв одну проблему на іншу. Я, як правило, нав’язливий програміст, оскільки хочу робити все правильно, щоб отримати правильні результати і, можливо, стати кращим програмістом. Шаблон MVVM просто зірвав мій тест на продуктивність і щойно перетворився на великий хакі!

Очевидним випадком є ​​додавання підтримки для діалогового вікна Modal. Правильний спосіб - встановити діалогове вікно і прив’язати його до моделі перегляду. Змусити це працювати важко. Щоб отримати вигоду з шаблону MVVM, вам потрібно розподілити код у кількох місцях по шарах вашої програми. Ви також повинні використовувати езотеричні конструкції програмування, такі як шаблони та вирази lamba. Речі, які змушують дивитись на екран, чухаючи голову. Це робить технічне обслуговування та налагодження кошмаром, який чекає, як я нещодавно виявив. У мене було близько ящика, який працював нормально, доки я не отримав виняток, коли я вдруге викликав його, сказавши, що він не зможе знову показати діалогове вікно після його закриття. Мені довелося додати обробник подій для закриття функціональних можливостей у діалогове вікно, ще один у реалізації IDialogView і, нарешті, ще один у IDialogViewModel. Я думав, MVVM врятує нас від такого екстравагантного злому!

Є кілька людей із конкуруючими рішеннями цієї проблеми, і всі вони хаки і не забезпечують чистого, елегантного рішення, яке можна багаторазово використовувати. Більшість наборів інструментів MVVM перекривають діалоги, і коли вони звертаються до них, вони є просто вікнами попереджень, які не потребують користувальницьких інтерфейсів або моделей перегляду.

Я планую відмовитись від шаблону подання MVVM, принаймні його ортодоксальної реалізації. Що ти думаєш? Чи варто було б вам клопоту, якщо у вас вони були? Я просто некомпетентний програміст чи MVVM - це не те, що роздуто?


6
Я завжди сумнівався, чи MVVM надмірна інженерія. Цікаве питання.
Тейлор Ліз,

11
Шаблони, такі як MVVM і MVC, здаються надмірними розробками, поки вам не доведеться виконувати деякі модифікації або змінювати компонент. Перший раз, коли вам доводиться це робити, вся церемонія окупається.
Роберт Харві,

41
Лямбди езотеричні? новини для мене.
Ray Booysen,

5
@Ray - Ха-ха, +1 за цей коментар! : D
Венемо

7
Як зауважив Алан Купер більше десяти років тому у програмі About Face , якщо ви розробляєте інтерфейси та модальні діалоги - це не основний випадок, ви, мабуть, робите щось не так.
Роберт Росней,

Відповіді:


61

Вибачте, якщо моя відповідь стала трохи довгою, але не звинувачуйте мене! Ваше запитання також довге.

Таким чином, MVVM не безглуздо.

Очевидним випадком є ​​додавання підтримки для діалогового вікна Modal. Правильний спосіб - встановити діалогове вікно і прив’язати його до моделі подання. Змусити це працювати важко.

Так, це справді так.
Однак MVVM надає вам спосіб відокремити зовнішній вигляд інтерфейсу від його логіки. Ніхто не змушує вас використовувати його скрізь, і ніхто не тримає пістолет біля чола, щоб змусити вас створити окремий ViewModel для всього.

Ось моє рішення для цього конкретного прикладу:
Те, як користувальницький інтерфейс обробляє певний вхід, не стосується ViewModel. Я додав би код до файлу .xaml.cs подання, який створює діалогове вікно та встановлює той самий екземпляр ViewModel (або щось інше, якщо потрібно) як його DataContext.

Щоб отримати вигоду з шаблону MVVM, вам потрібно розподілити код у кількох місцях по шарах вашої програми. Ви також повинні використовувати езотеричні конструкції програмування, такі як шаблони та вирази lamba.

Ну, вам не потрібно використовувати його в декількох місцях. Ось як би я це вирішив:

  • Додайте XAML до подання, а в .xaml.cs нічого
  • Запишіть кожну логіку програми (крім матеріалів, які безпосередньо працюють з елементами інтерфейсу) у ViewModel
  • Весь код, який повинен робити інтерфейс, але не має нічого спільного з бізнес-логікою, надходить у файли .xaml.cs

Я думаю, що мета MVVM полягає в першу чергу у розділенні логіки програми та конкретного інтерфейсу користувача, що дозволяє легко модифікувати (або повністю замінити) інтерфейс.
Я використовую такий принцип: View може знати і приймати все, що хоче, від ViewModel, але ViewModel НІЧОГО не може знати про View.
WPF пропонує приємну модель прив'язки, яку ви можете використовувати для досягнення саме цього.

(BTW, шаблони та лямбда-вирази не є езотеричними при правильному використанні. Але якщо ви цього не хочете, не використовуйте їх.)

Речі, які змушують дивитись на екран, чухаючи голову.

Так, я знаю це відчуття. Саме те, що я відчував, коли вперше побачив MVVM. Але як тільки ви звикнете, це вже не буде погано.

У мене було близько ящика працює нормально ...

Чому ви ставите ViewModel за ящик приблизно? В цьому немає сенсу.

Більшість наборів інструментів MVVM перекривають діалоги, і коли вони звертаються до них, вони є просто вікнами попереджень, які не потребують користувальницьких інтерфейсів або моделей перегляду.

Так, тому що сам факт того, що елемент інтерфейсу знаходиться в тому самому вікні, чи в іншому вікні, або в даний момент обертається навколо Марса, не є предметом інтересів ViewModels.
Поділ проблем

РЕДАГУВАТИ:

Ось дуже гарне відео, назва якого - Створіть свій власний фреймворк MVVM . Варто подивитися.


2
+1 за останні три слова. Але решта відповідей теж хороша. :)
Роберт Харві,

12
+1 за пораду щодо використання кодового коду. Поширеною помилкою є думка, що використовувати "позаду коду" в MVVM "погано" ... але для суто матеріалів, пов'язаних з інтерфейсом користувача, це шлях.
Томас Левеск,

@Thomas: Так, я не міг більше погодитися. Я бачив кілька реалізацій, коли люди вкладали весь код (навіть пов'язаний з користувацьким інтерфейсом) у ViewModel, оскільки (за їхніми словами) "саме там знаходиться код". Це було досить хакі.
Венемо

3
@Venemo, я думаю, ти можеш інкапсулювати багато речей, які ти хотів би помістити в код, використовуючи такі методи, як власні поведінки, що зручно, якщо ти неодноразово пишеш клейовий код. Однак загалом, я вважаю, що краще використовувати кодовий код для клею, ніж руйнувати незграбний XAML. На мою думку, головним завданням є переконатися, що в коді, що стоїть позаду, немає нічого досить складного, щоб гарантувати модульне тестування. Все, що є достатньо складним, краще інкапсулювати у ViewModel або класі розширення, наприклад Behaviour або MarkupExtension.
Ден Брайант,

7
@ Томас: Ви праві, найбільший міф про MVVM полягає в тому, що метою MVVM є позбавлення від коду. Мета полягає в тому, щоб вивести ocde Non-UI із коду позаду. Вставити лише код інтерфейсу користувача у ViewModel так само погано, як вставити проблемний код домену в код позаду.
Jim Reineri

8

Змусити це працювати важко. Щоб отримати вигоду з шаблону MVVM, вам потрібно розподілити код у кількох місцях по шарах вашої програми. Ви також повинні використовувати езотеричні конструкції програмування, такі як шаблони та вирази lamba.

Для звичайного модального діалогового вікна? Ви, звичайно, робите щось не так - впровадження MVVM не повинно бути таким складним.

Враховуючи, що ви новачок як у MVVM, так і у WPF, швидше за все, ви використовуєте неоптимальні рішення скрізь і без потреби ускладнюєте справи - принаймні, я зробив це, коли вперше пішов на WPF. Перш ніж відмовлятися, переконайтеся, що проблема насправді MVVM, а не ваша реалізація.

MVVM, MVC, Document-View тощо - це стара родина шаблонів. Є недоліки, але фатальних вад, яких ви описуєте, немає.


5

Я переживаю досить складну розробку MVVM з використанням PRISM, тому мені вже довелося впоратися з подібними проблемами.

Мої особисті висновки:

MVVM проти MVC / PopUps & co

  • MVVM - справді чудовий шаблон, і в більшості випадків він повністю замінює MVC завдяки потужному прив'язуванню даних у WPF
  • Виклик рівня обслуговування безпосередньо від презентатора в більшості випадків є законним рішенням
  • Навіть досить складні сценарії списку / деталізації можуть бути реалізовані чистим MVVM завдяки синтаксису {Binding Path = /}
  • Тим не менше, коли потрібно впровадити складну координацію між кількома поданнями, контролер обов'язковий
  • Події можуть бути використані; старий шаблон, який передбачає зберігання екземплярів IView (або AbstractObserver) у контролері, застарів
  • Контролер можна вводити в кожного ведучого контейнером IOC
  • Послуга IEventAggregator від Prism - ще одне можливе рішення, якщо єдиним використанням контролера є диспетчеризація подій (у цьому випадку він може повністю замінити контролер)
  • Якщо подання мають створюватися динамічно, це дуже підходить робота для контролера (у призмі контролер буде вводити (IOC) IRegionManager)
  • Модальні діалогові вікна здебільшого застарілі в сучасних композитних програмах, за винятком дійсно блокуючих операцій, таких як обов’язкові підтвердження; у цих випадках модальна активація може бути абстрагована як послуга, що викликається всередині контролера, і реалізована спеціалізованим класом, який також дозволяє просунути модульне тестування рівня презентації. Наприклад, контролер викличе IConfirmationService.RequestConfirmation ("Ви впевнені"), який запустить модальне діалогове відображення під час виконання і може бути легко знущаний під час тестування модулів

5

Я маю справу з проблемою діалогів, обдурюючи. My MainWindow реалізує інтерфейс IWindowServices, який відкриває всі діалогові вікна, що стосуються конкретної програми. Потім інші мої ViewModels можуть імпортувати інтерфейс служб (я використовую MEF, але ви можете легко просто пропустити інтерфейс через конструктори вручну) і використовувати його для виконання необхідного. Наприклад, ось як виглядає інтерфейс для мого невеликого утилітного додатка:

//Wrapper interface for dialog functionality to allow for mocking during tests
public interface IWindowServices
{
    bool ExecuteNewProject(NewProjectViewModel model);

    bool ExecuteImportSymbols(ImportSymbolsViewModel model);

    bool ExecuteOpenDialog(OpenFileDialog dialog);

    bool ExecuteSaveDialog(SaveFileDialog dialog);

    bool ExecuteWarningConfirmation(string text, string caption);

    void ExitApplication();
}

Це ставить всі дії діалогу в одне місце, і їх можна легко заглушити для модульного тестування. Я дотримуюся шаблону, який клієнт діалогового вікна повинен створити відповідний ViewModel, який він потім може налаштувати за потреби. Блоки викликів Execute, а потім клієнт може переглянути вміст ViewModel, щоб побачити результати діалогу.

Більш «чистий» дизайн MVVM може бути важливим для великих додатків, де вам потрібна чистіша ізоляція та більш складна композиція, але для малих та середніх додатків, я думаю, практичного підходу з відповідними послугами для викриття необхідних гачків цілком достатньо .


Але де ти це називаєш? Чи не було б краще просто побудувати діалогове вікно в межах функцій класу IWindowServices, а не передавати його. Таким чином, вигляд моделі, що викликає, не повинен знати нічого про конкретну реалізацію діалогового вікна.
Джоель Роджерс

Інтерфейс вводиться в будь-який із моїх екземплярів ViewModel, який потребує доступу до діалогових вікон програми. У більшості випадків я передаю ViewModel для діалогового вікна, але я трохи лінувався і використовував WPF OpenFileDialog та SaveFileDialog для викликів діалогових файлів. Моєю основною метою була ізоляція для цілей модульного тестування, тому цього цілком достатньо. Якщо ви хочете покращити ізоляцію, ви, мабуть, захочете створити OpenFileViewModel та SaveFileViewModel, які дублюють необхідні властивості для діалогів.
Ден Брайант

Зверніть увагу, що це точно не чисто підхід, оскільки ViewModel, який використовує діалоги, знає про конкретний ViewModel для кожного діалогового вікна, яке він хоче відкрити. Я вважаю, що це досить чисто, але ви завжди можете додати додатковий шар ізоляції з класом, який суто виставляє параметри, необхідні для діалогового використання, приховуючи будь-яку непотрібну видимість властивостей ViewModel, які використовуються під час прив'язки. Для менших додатків я вважаю, що ця додаткова ізоляція є надмірною.
Ден Брайант

5

Шаблони дизайну допоможуть вам, а не завадять. Невелика частина хорошого розробника - це знання, коли "порушувати правила". Якщо MVVM громіздкий для завдання, і ви визначили, що майбутнє значення не варте зусиль, тоді не використовуйте шаблон. Наприклад, як коментували інші плакати, чому б вам пройти всі накладні витрати, щоб реалізувати просте вікно?

Шаблони дизайну ніколи не мали на меті догматично слідувати.


2
Правильно. Просте вікно про поле має бути простим, але що, якщо вам доведеться відображати таку інформацію, як версія, ліцензування, запущений процес, назва компанії тощо. Це вся інформація, яка десь живе. У стандартних формах ви можете просто зв’язати всю цю інформацію і закінчити з нею. MVVM каже, що вам слід створити для нього модель перегляду, яка є зайвою.
ATL_DEV

1

Оскільки сам шаблон MVVM чудовий. Але бібліотека управління WPF, що постачається з підтримкою прив'язки даних NET 4.0, дуже обмежена, вона набагато краща, ніж WinForm, але все ж цього недостатньо для прив'язуваного MVVM, я б сказав, що потужність становить близько 30% від того, що потрібно для прив'язуваного MVVM.
Зв'язуваний MVVM: це інтерфейс, де ViewModel з'єднаний з View лише за допомогою прив'язки даних.
Шаблон MVVM стосується представлення об'єкта ViewState, він не описує, як ви підтримуєте синхронізацію між View і ViewModel, у WPF це прив'язка даних, але це може бути що завгодно. І насправді ви можете використовувати шаблон MVVM в будь-якому наборі інтерфейсів користувача, який підтримує події \ зворотні виклики, ви можете використовувати його в чистому WinAPI в WinForms (я це зробив, і це не набагато більше роботи з подіями \ зворотними викликами), і ви навіть можете використовувати його в тексті Консоль, як переписати Doton's Norton Commander, використовуючи шаблон MVVM.

Коротше кажучи: MVVM не безглуздо, це чудово. Бібліотека керування NET 4.0 WPF є сміттям.

Ось простий доказ концепції ViewModel, яку ви не можете прив’язати до даних чисто MVVM за допомогою WPF.

public class PersonsViewModel
{
    public IList<Person> PersonList;
    public IList<ColumnDescription> TableColumns;
    public IList<Person> SelectedPersons;
    public Person ActivePerson;
    public ColumnDescription SortedColumn;
}

Ви не можете прив’язати дані заголовків стовпців DataGrid WPF, ви не можете прив’язати дані виділених рядків тощо, ви можете зробити це простим кодом, або написати 200 рядків коду злому XAML для цих 5 рядків найпростішого ViewModel. Ви можете лише уявити, як із складними ViewModels все стає гірше.
Тож відповідь проста, якщо ви не пишете програму Hello World, використання прив’язуваного MVVM у WPF безглуздо. Ви витратите більшу частину часу на роздуми про те, щоб зв'язати вас ViewModel. Прив'язка даних приємна, але будьте готові повернутися до 70% часу події.


Ви можете прив'язати це за допомогою перетворювачів до DataGrid.
Cameron MacFarland

@CameronMacFarland: Не всі, деякі властивості доступні лише для читання та неможливо підключити, деякі просто не існують, і є лише події, які повідомляють про зміну стану.
Алекс Бурцев

Зізнаюся, у мене немає великого досвіду використання WPF DataGrid. Я схильний уникати цього, оскільки це негарно і більше не підходить для WPF. Сказавши, що комбінація перетворювачів та AttachedProperties для обробки подій повинна дати вам те, що вам потрібно.
Cameron MacFarland

1
Алекс, у вас виникають проблеми з дизайном DataGrid, а не з MVVM. Просто невірно стверджувати, що "Прив'язка даних приємна, але будьте готові повернутися до 70% часу події". Я написав кілька об'єктивно величезних програм WPF, в яких в UI взагалі немає обробників подій - за винятком обробника подій, який сітка даних (Telerik) потребує для ініціалізації.
Роберт Росней,

3
Я думаю, що ви могли б досягти більше успіху, якби замість того, щоб прийняти позицію: "Це погано розроблено і не працює", ви спробували: "Чому це працює для інших людей, а не для мене?" Ви можете виявити, що причиною того, що важко зробити, є те, що ви ще не знаєте, як це робити.
Роберт Росней,

0

Ні, це не безглуздо, але складно обернути голову, хоча сам малюнок смішно простий. Є маса дезінформації та різні групи, які борються за правильний шлях. Я думаю, що з WPF та Silverlight вам слід використовувати MVVM, інакше ви перестанете кодувати та намагатись вирішити проблеми за новою моделлю, “старою” методологією виграшних форм, яка просто призводить вас до неприємностей. Це більше стосується Silverlight, оскільки все повинно бути асинхронним (хакі навколо цього можливі, але вам слід просто вибрати іншу платформу).

Я пропоную прочитати цю статтю Спрощення WPF TreeView за допомогою ретельного використання шаблону ViewModel, щоб побачити, як MVVM може бути добре впроваджено, і дозволити вам змінити менталітет ваших виграшних форм на новий спосіб мислення в MVVM. Коротше кажучи, коли ви хочете щось зробити, застосуйте логіку до ViewModel спочатку, а не до View. Ви хочете вибрати елемент? Змінити значок? Не переглядайте елементи інтерфейсу, просто оновіть властивості моделей і нехай прив'язка даних робить дрібницю.


-1

Я бачив ту саму проблему з багатьма реалізаціями MVVM, коли справа стосується (модальних) діалогів. Коли я дивлюся на учасників Шаблону MVVM, тоді у мене виникає відчуття, що чогось не вистачає для створення цілісного додатка.

  • Вид містить певні елементи управління GUI і визначає зовнішній вигляд призначеного для користувача інтерфейсу.
  • ViewModel представляє стан та поведінку презентації.
  • Модель може бути бізнес-об'єктом з рівня домену або службою, яка надає необхідні дані.

Але відсутнє:

  • Хто створює ViewModels?
  • Хто відповідає за робочий процес програми?
  • Хто посередник між ViewModels, коли їм потрібно спілкуватися між собою?

Мій підхід полягає в тому, щоб представити контролер (Use-Case), який відповідає за відсутні місця. Як це працює, можна побачити в зразках програм WPF Application Framework (WAF) .


Шаблон "Посередник", реалізований Джошем Смітом, вирішив усі мої проблеми спілкування в моделі перегляду. Messenger.NotifyColleagues надав спосіб отримати абсолютно незалежні моделі поглядів, які знали, як реагувати на глобальні події (якщо їм це було байдуже), не маючи жодної моделі перегляду, яка знає одна про одну. Вже кілька разів ми врятували наш бекон.
JasonD
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.