MVVM та схема обслуговування


14

Я будую додаток WPF за допомогою шаблону MVVM. Зараз мої viewmodels викликають сервісний шар для отримання моделей (як це не стосується viewmodel) та перетворення їх у viewmodels. Я використовую інжектор конструктора, щоб передати необхідну послугу в перегляд.

Це легко перевіряється і добре працює для моделей перегляду з невеликими залежностями, але як тільки я намагаюся створити viewModels для складних моделей, у мене є конструктор, що в нього вводиться багато служб (один для отримання кожної залежності та список усіх доступних значень наприклад, для прив’язки до itemsSource). Мені цікаво, як обробляти кілька подібних служб, і все ще є перегляд моделей, який я можу легко виконати.

Я думаю про кілька рішень:

  1. Створення сервісу одиночного типу (IServices), що містить усі доступні сервіси як інтерфейси. Приклад: Services.Current.XXXService.Retrieve (), Services.Current.YYYService.Retrieve (). Таким чином, у мене немає величезного конструктора з тоннами параметрів послуг.

  2. Створення фасаду для служб, які використовує viewModel, і передача цього об'єкта в ctor моєї viewmodel. Але тоді мені доведеться створити фасад для кожного з моїх складних моделей перегляду, і це може бути трохи більше ...

Як ви вважаєте, що це "правильний" спосіб втілення подібного роду архітектури?


Я думаю, що "правильний" спосіб це зробити - це створити окремий шар, який викликає служби та робить все необхідне, щоб створити ViewModel. Ваші ViewModels не повинні нести відповідальність за створення себе.
Емі Бланкенсіп

@AmyBlankenship: Моделі перегляду не повинні (або навіть обов'язково вміти) створювати себе, але неминуче іноді нестимуть відповідальність за створення інших моделей перегляду . Тут є величезна допомога IoC-контейнер з автоматичною заводською підтримкою.
Aaronaught

"Будуть іноді" і "повинні" - це дві різні тварини;)
Емі Бланкенсіп

@AmyBlankenship: Ви припускаєте, що моделі перегляду не повинні створювати інші моделі перегляду? Це жорстка таблетка, ковтати. Я можу зрозуміти, що моделі перегляду не повинні використовуватись newдля створення інших моделей перегляду, але придумати щось таке просто, як додаток MDI, коли натискання кнопки або меню «новий документ» додасть нову вкладку або відкриє нове вікно. Оболонка / провідник повинні вміти створювати нові екземпляри чогось , навіть якщо це приховано за одним або кількома шарами непрямості.
Aaronaught

Ну, безумовно, він повинен мати можливість вимагати, щоб Погляд був зроблений десь. Але зробити це самостійно? Не в моєму світі :). Але знову ж таки, у світі, в якому я живу, ми називаємо ВМ «Модель презентації».
Емі Бланкенсіп

Відповіді:


22

Насправді обидва ці рішення погані.

Створення сервісу одиночного типу (IServices), що містить усі доступні сервіси як інтерфейси. Приклад: Services.Current.XXXService.Retrieve (), Services.Current.YYYService.Retrieve (). Таким чином, у мене немає величезного конструктора з тоннами параметрів послуг.

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

Створення фасаду для служб, які використовує viewModel, і передача цього об'єкта в ctor моєї viewmodel. Але тоді мені доведеться створити фасад для кожного з моїх складних моделей перегляду, і це може бути трохи більше ...

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

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

Подумайте, як би ви провели тестування однієї з цих моделей перегляду. Наскільки великим буде ваш код налаштування? Скільки речей потрібно ініціалізувати, щоб воно працювало?

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

Насправді вам потрібно зробити рефакторинг моделей перегляду, щоб вони мали менше залежностей . Потім, якщо вам потрібно мати сукупний "екран", ви створюєте іншу модель подання для об'єднання менших моделей перегляду. Ця модель сукупного вигляду не повинна робити багато сама по собі, тому вона, в свою чергу, також досить проста для розуміння та тестування.

Якщо ви зробили це правильно, це повинно бути очевидним лише з огляду на код, оскільки у вас будуть короткі, стислі, конкретні та перевірені моделі.


Так, це я, мабуть, в кінцевому підсумку робити! Дуже дякую, сер.
альфа-альфа

Ну, я вже припускав, що він уже пробував це, але не вдалося. @ alfa-alfa
Euphoric

@Euphoric: Як вам "не досягти успіху" в цьому? Як сказав Йода: "Робити чи не робити", немає спроб.
Aaronaught

@Aaronaught Наприклад, йому справді потрібні всі дані в одній програмі перегляду. Можливо, у нього є сітка, і різні стовпці надходять від різних служб. Ви не можете цього зробити зі складом.
Ейфорія

@Euphoric: насправді ви можете вирішити це за допомогою композиції, але це можна зробити під рівнем моделі перегляду. Це просто питання створення правильних абстракцій. У такому випадку вам потрібна лише одна служба для обробки початкового запиту для отримання списку ідентифікаторів та послідовності / списку / масиву "збагачувачів", які коментують власну інформацію. Зробіть саму сітку власною моделлю перегляду, і ви вирішили проблему ефективно двома залежностями, і це надзвичайно просто перевірити.
Aaronaught

1

Я міг би написати про це книгу ... насправді я є;)

По-перше, не існує універсально "правильного" способу робити речі. Ви повинні враховувати інші фактори.

Можливо, ваші послуги занадто дрібнозернисті. Обгортання Служб фасадами, які надають інтерфейс, який використовує конкретний Viewmodel або навіть кластер пов'язаних ViewModels, може бути кращим рішенням.

Ще простіше було б обернути сервіси в єдиний Фасад, який використовують усі переглядачі. Звичайно, це потенційно може бути дуже великим інтерфейсом з великою кількістю непотрібних функцій для середнього сценарію. Але я б сказав, що це не відрізняється від маршрутизатора повідомлень, який обробляє кожне повідомлення у вашій системі.

Насправді те, що я бачив у багатьох архітектурах, в кінцевому підсумку еволюціонує, - це шина повідомлень, побудована навколо чогось подібного до шаблону агрегатора подій. Тестування там легко, оскільки ваш тестовий клас просто реєструє слухача з EA і запускає відповідну подію у відповідь. Але це попередній сценарій, на який потрібен час, щоб вирости. Я кажу, починайте з об'єднувального фасаду та йдіть звідти.


Масовий фасад обслуговування сильно відрізняється від брокера повідомлень. Це майже на протилежному кінці спектру залежності. Відмітною ознакою цієї архітектури є те, що відправник нічого не знає про приймач, і приймачів може бути багато (pub / sub або multicast). Можливо, ви плутаєте це з "видаленням" у стилі RPC, що просто піддає традиційній службі через віддалений протокол, і відправник все ще пов'язаний з отриманням, як фізично (адреса кінцевої точки), так і логічно (значення повернення).
Aaronaught

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

Так, але фасад є об'єктом Бога . Він має всі залежності, які мають Моделі перегляду, ймовірно, більше, тому що їх поділять декілька. Насправді ви усунули переваги сипучих з’єднань, над якими ви так наполегливо працювали, адже зараз, коли щось торкається мега-фасаду, ви не маєте поняття, від якої функціональності це насправді залежить. Написання одиничного тесту для класу, який використовує фасад. Ви створюєте макет для фасаду. Тепер, якими методами ви знущаєтесь? Як виглядає ваш код налаштування?
Aaronaught

Це дуже відрізняється від брокера повідомлень, оскільки брокер також нічого не знає про реалізацію обробників повідомлень . Він використовує IoC під кришкою. Фасад знає все про одержувачів, тому що він повинен переадресовувати дзвінки до них. Шина має нульову муфту; фасад має нецензурно високу еферентну муфту. Практично все, що ви зміните, де завгодно, вплине і на фасад.
Aaronaught

Я думаю, що частина плутанини тут - і я бачу це досить багато - саме те, що означає залежність . Якщо у вас є клас, який залежить від іншого класу, але він називає 4 методи цього класу, він має 4 залежності, а не 1. Якщо все це за фасадом не змінюється кількість залежностей, це просто ускладнює їх розуміння .
Aaronaught

0

Чому б не поєднати обидва?

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

Або ви можете використовувати введення властивостей замість конструктора. Але тоді вам потрібно переконатися, що ті отримують ін'єкцію належним чином.


Це було б кращою відповіддю, якби ви подали приклад у псевдо-C #.
Роберт Харві
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.