Чистая архитектура: что такое модель представления?

13

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

введите описание изображения здесь

Это то же самое, что и ViewModel из шаблона проектирования Model-View-ViewModel (MVVM), или это простой объект передачи данных (DTO)?

Если это не простой DTO, как это связано с View? Получает ли представление обновления от него через отношения Observer?

Я предполагаю, что это больше похоже на ViewModel от MVVM, потому что в главе 23 своей книги Роберт Мартин говорит:

Работа [докладчика] состоит в том, чтобы принимать данные из приложения и форматировать их для представления, чтобы представление могло просто переместить их на экран. Например, если приложение хочет, чтобы в поле отображалась дата, оно передает объект Presenter a Date. Затем докладчик отформатирует эти данные в соответствующую строку и поместит их в простую структуру данных, называемую моделью представления, где представление может найти ее.

Это подразумевает, что View каким-то образом связан с ViewModel, а не просто получает его в качестве аргумента функции, например (как в случае с DTO).

Другая причина, по которой я так думаю, заключается в том, что, если вы посмотрите на изображение, Presenter использует модель представления, но не представление. Принимая во внимание, что докладчик использует как выходную границу, так и выходные данные DTO.

Если это не DTO или ViewModel из MVVM, пожалуйста, уточните, что это такое.

Fearnbuster
источник
Я думаю, что ответ «это зависит». Если это веб-приложение, то модель представления - это, по сути, DTO, потому что в конечном итоге она сериализуется в виде строки HTML. В противном случае модель представления - это просто специализированный объект для отображения данных в представлении.
Грег Бургхардт
В MVVM (WPF, приложения Winforms) ViewModelесть обертка для Controller, Presenterи ViewModelв Чистой архитектуре Дяди Боба.
Фабио
@ Грег Бургхардт - Когда ViewModel является специализированной структурой данных, как View уведомляется об изменениях?
Fearnbuster
@Fabio - Если я правильно понимаю, что вы говорите, что в шаблоне MVVM ViewModel эквивалентен всем компонентам, которые находятся в самой левой группе диаграммы? Если это верно для архитектуры дяди Боба, то почему он перечисляет Контроллер и Ведущий отдельно?
Fearnbuster
Я думаю, что он разделил обработчики ввода и вывода для различных объектов / классов. В MVVM это может быть Controller-> ICommandи Presenter-> data-binding mechanism.
Фабио

Ответы:

17

Это то же самое, что и ViewModel из шаблона проектирования Model-View-ViewModel (MVVM)

Нет.

Это было бы так :

введите описание изображения здесь

Это имеет циклы. Дядя Боб тщательно избегал циклов .

Вместо этого у вас есть это:

введите описание изображения здесь

Который, конечно, не имеет циклов. Но это заставляет задуматься о том, как представление узнает об обновлении. Мы вернемся к этому через минуту.

или это простой объект передачи данных (DTO)?

Процитирую Боба с предыдущей страницы:

Вы можете использовать базовые структуры или простые объекты передачи данных, если хотите. Или вы можете упаковать его в hashmap или создать в виде объекта.

Чистая архитектура p207

Так что, конечно, если хотите.

Но я сильно подозреваю, что вас действительно беспокоит это :

введите описание изображения здесь

Это милое небольшое злоупотребление UML противопоставляет направление зависимости исходного кода с направлением потока управления. Здесь можно найти ответ на ваш вопрос.

В отношениях использования:

введите описание изображения здесь введите описание изображения здесь

Поток управления идет в том же направлении, что и зависимость от исходного кода.

В отношениях реализации:

введите описание изображения здесь введите описание изображения здесь

Поток управления обычно идет в направлении, противоположном зависимости исходного кода.

Что означает, что вы действительно смотрите на это:

введите описание изображения здесь

Вы должны быть в состоянии видеть, что поток управления никогда не попадет из Presenter в View.

Как это может быть? Что это означает?

Это означает, что у представления либо есть свой собственный поток (что не так уж необычно), либо (как указывает @Euphoric) поток управления входит в представление из чего-то еще, не изображенного здесь.

Если это тот же поток, то View будет знать, когда View-Model будет готова для чтения. Но если дело обстоит именно так, и представление является графическим интерфейсом, то ему будет трудно перекрашивать экран, когда пользователь перемещает его, ожидая БД.

Если у представления есть свой собственный поток, то у него есть свой собственный поток управления. Это означает, что для реализации этого Представление должно будет опрашивать View-Model, чтобы заметить изменения.

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

Согласно этой диаграмме единственное, что разделяют View и Presenter, это знание View-Model. И это просто структура данных. Так что не ожидайте, что это будет иметь какое-либо поведение.

Это может показаться невозможным, но его можно заставить работать, даже если View-Model сложна. Одно небольшое обновленное поле - это все, что представление должно опрашивать, чтобы обнаружить изменение.

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

Вот немного забавы, иллюстрирующее поток управления:

введите описание изображения здесь

Обратите внимание, что всякий раз, когда вы видите, что поток идет против направлений, которые я определил ранее, вы видите возвращение вызова. Этот трюк не поможет нам добраться до Вью. Ну, если мы сначала не вернемся к тому, что называется контроллером. Или вы можете просто изменить дизайн, чтобы вы могли добраться до вида. Это также исправляет то, что выглядит как начало проблемы с доступом к данным и ее интерфейсом.

Единственное, чему здесь нужно научиться, это то, что Interactor Use Case может в значительной степени вызывать вещи в любом порядке, в котором он хочет, до тех пор, пока он вызывает ведущего.

candied_orange
источник
Большое спасибо за ответ человек, я видел ваши ответы на различные другие вопросы о чистой архитектуре. Вы предлагаете, чтобы представление постоянно проверяло флаг, например, внутри модели представления, чтобы увидеть, были ли какие-либо изменения? Придется ли представлению затем снова отображать всю модель представления или я должен использовать набор вложенных флагов, чтобы указать, какие данные были изменены?
Fearnbuster
Вид не должен опрашивать постоянно. Например, веб 1.0 запрашивает только когда пользователь нажимает перезагрузить. Постоянный опрос - это дизайнерское решение, которое должно учитывать реальные потребности пользователей. Я просто говорю, что это возможно. Смысл поля обновления - сделать обнаружение обновления быстрым. Требуется только если модель представления сложна. Также подумайте о том, что произойдет, если представление будет читать, пока докладчик находится на полпути к обновлению.
candied_orange
Хорошо, большое спасибо за помощь. Если / когда вы следуете этой архитектуре, это метод, который вы обычно используете?
Fearnbuster
1
Я думаю, что есть большая ошибка, что этот ответ группирует зависимости проектирования и зависимости времени выполнения. Два могут быть разными.
Эйфорический
1
@ Euphoric Почему спасибо. Я связываю их вместе, потому что если у вас нет зависимости исходного кода от чего-либо, вы не можете использовать ссылку на него во время выполнения для чего-либо, поскольку вы не понимаете, что это такое. Все, что вы могли бы сделать, это сохранить ссылку, как это делает коллекция. Если это ошибка, я бы хотел это понять.
candied_orange
2

Я нахожу эту проблему слишком запутанной, и потребовалось бы много текста и времени, чтобы правильно объяснить проблему, так как я считаю, что вы неправильно понимаете как Чистую архитектуру Мартина, так и MVVM.

Первое, что нужно отметить, это то, что вы разместили диаграмму не полностью. Он показывает только «бизнес-логику», но в нем отсутствует какой-то «оркестратор», который фактически заставляет детали двигаться в правильном порядке. введите описание изображения здесь

Код оркестратора будет таким же простым, как

string Request(string request) // returns response
{
    Controller.Run(data);
    Presenter.Run();
    return View.Run();
}

Мне кажется, я слышал, как Мартин говорил об этом в одном из своих выступлений о чистой архитектуре.

Еще одна вещь, на которую следует обратить внимание: замечание candied_orange о недостатке циклов неверно. Да, циклический не существует (и не должен) в архитектуре кода. Но циклы между экземплярами времени выполнения являются общими и часто приводят к упрощению проектирования.

Это случай в MVVM. В MVVM View зависит от ViewModel, а ViewModel использует события для уведомления View об его изменениях. Это означает, что при проектировании классов существует зависимость только от классов View до Model, но во время выполнения существует циклическая зависимость между экземплярами View и ViewModel. Из-за этого нет необходимости в оркестраторе, так как ViewModel предоставит View способ выяснить, когда обновлять себя. Вот почему в «уведомлениях» на этой диаграмме используется «волнистая» линия, а не прямая. Это означает, что View наблюдает за изменениями в ViewModel, а не потому, что ViewModel зависит от View.

введите описание изображения здесь

Самая важная вещь, которую вы должны взять из Чистой архитектуры Мартина, это не сам дизайн, а то, как вы справляетесь с зависимостями. Один из критических моментов, которые он подчеркивает в своих выступлениях, заключается в том, что при наличии границы все кодовые зависимости, пересекающие эту границу, пересекают ее в одном направлении. На диаграмме эта граница представлена ​​двойной линией. И есть много инверсий зависимостей через интерфейсы ( InputBoundary, OutputBoundaryи DataAccessInterface) , что фиксирует направление коды зависимости.

В отличие от этого, ViewModelв чистой архитектуре просто DTO без логики. Это становится очевидным по <DS>тегу. И это причина, почему orchestratorэто необходимо, так как Viewне будет знать, когда запустить его логику.

Если бы я «сплющил» диаграмму в то, как она будет выглядеть во время выполнения, она будет выглядеть так:

введите описание изображения здесь

Поэтому во время выполнения зависимости находятся в «неправильном» направлении, но это нормально.

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

Euphoric
источник
Ваш «Оркестратор» не должен звонить ведущему. Интерактор варианта использования делает это.
candied_orange
@candied_orange Правда, это ошибка.
Эйфорическая
Спасибо за ответ, всегда хорошо получить несколько разных мнений. Я проголосовал за ответы обоих ваших парней. Кто-нибудь из вас знает, есть ли у Роберта Мартина кодовая база, в которой он реализовал форму своей Архитектуры? Я посмотрел на его проект FitNess, но я не мог видеть лес за деревьями. Кроме того, я прав в предположении, что, хотя изображение, которое я выложил, является диаграммой, которую дядя Боб всегда использует в своих выступлениях, на самом деле это всего лишь пример того, как МОЖЕТ выглядеть ваша архитектура? Принимая во внимание, что это может выглядеть совсем иначе, пока поток зависимостей корректен?
Fearnbuster
@Fearnbuster К вашему последнему вопросу, да. Направление зависимостей является более важным, чем структура. Я считаю, что «Чистая архитектура», «Луковая архитектура» и «Шестиугольная архитектура» действительно являются реализацией одной и той же идеи «Контролировать зависимости».
Эйфорическая
@Euphoric Честно говоря, я бы сказал, что это, вероятно, так, потому что на другом изображении своей книги (рис. 8.2 главы 8) он показывает архитектуру, которая выглядит по-другому. На этой схеме Контроллер фактически является посредником между Интерактором и Презентатором. Также нет выходной границы для Интерактора; кажется, что Interactor получает запросы через интерфейс, а затем возвращает ответы через тот же интерфейс (я полагаю, что это делается с помощью простого возвращаемого значения функции, так как я не могу представить ни один другой механизм, который бы работал таким образом).
Fearnbuster