MVC при нестандартном интерфейсе пользователя
От: Slicer [Mirkwood] Россия https://ru.linkedin.com/in/maksim-gumerov-039a701b
Дата: 27.06.04 14:26
Оценка:
Всем привет!
Вот такая возникла проблема, не знаю, есть ли тут универсальное решение или придется идти на компромисс.

Как я понимаю, в MVC у нас вид осведомлен о модели, а контроллер (в общем случае) — о них обоих.
Теперь представим изменение модели во времени. Соотвественно этим изменениям должен изменяться и вид. И вот тут возникают два варианта: либо вид знает о всех возможных элементарных изменениях модели и получает о них уведомления, либо вид получает просто уведомление о том, что модель изменилась, и в ответ он себя обновляет. Первый вариант вроде как уродлив, только сама модель вправе иметь знания о направлениях своих изменений, ну, может, они еще как-то касаются контроллера (и то вряд ли), но уж никак не вида. Остается второй вариант. Пока никакого криминала.

Далее, хочется разбить вид на подвиды — виды отдельных компонентов модели, и каждому из подвидов сопоставить свою часть модели и свой контроллер. Например, если в модели у нас объекты, которые на экране должны быть как-то сложно представлены (сложная отрисовка, или обилие элементов управления) или которым свойственна сложная обработка действий пользователя, то впихивать всю эту логику в одну только тройку MVC очень неудобно.
Как надежно организовать обновление в общем виде, при этом не скатываясь к первому варианту (знанию вида о возможных изменениях)? Мне приходит на ум только одно — уничтожать все мини-виды и затем воссоздавать их по модели, причем вместе с их контроллерами.
Почему с контроллерами? Да потому, что в общем случае контроллеры тоже могут, как и виды, содержать собственные поля, не входящие в модель, — например, счетчик нажатий на кнопку.

Это все прекрасно, если для начала реакции на действие пользователя (нажатие мышки, клавы) достаточно знания того мини-вида, в котором это действие проведено. А если программа должна обладать памятью? Т.е., например, сопрягать объект, на котором кликнули в прошлый раз, и тот, на котором кликнули сейчас? Ведь кликнутый ранее вид уже был, возможно, удален и снова воссоздан — по другому адресу, со сброшенным содержимым...

Итак, вопросы.
1) Как же в условиях изменчивой модели обеспечить фиксацию мини-видов?
2) Как позволить "жить" котроллерам, имеющим внутреннее состояние?
3) Может, я перестраховываюсь, и учет в виде всех вариантов изменения модели — не такое и плохое решение?

Slicer.
Специалист — это варвар, невежество которого не всесторонне :)
Re: MVC при нестандартном интерфейсе пользователя
От: A.J. Россия CintaNotes
Дата: 28.06.04 07:34
Оценка: 13 (2)
Здравствуйте, Slicer [Mirkwood], Вы писали:

SM> Итак, вопросы.

SM> 1) Как же в условиях изменчивой модели обеспечить фиксацию мини-видов?
SM> 2) Как позволить "жить" котроллерам, имеющим внутреннее состояние?
SM> 3) Может, я перестраховываюсь, и учет в виде всех вариантов изменения модели — не такое и плохое решение?


Хм, ну ты же сам пишешь — вид знает о модели. Ведь он ее отображает. В том числе он знает и о том, какие в ней возможны (значимые для отображения) изменения. Если он не будет об этом знать, то как он будет ее представлять-то?

Я обычно поступаю следующим образом: определяю для модели набор событий, которые могут интересовать вид. Как конкретно эти события реализуются — неважно, как правило для этого используется шаблон observer(publish-subscribe). При такой схеме элементарно просто создавать "мини-виды", которые будут реагировать только на конкретное подмножество событий модели.

Виды и контроллеры тогда совсем не нужно заново создавать при каждом изменении модели — это, по-моему, изврат.

Все вышесказанное, разумеется, сугубо ИМХО.

Удачи
Re[2]: MVC при нестандартном интерфейсе пользователя
От: Slicer [Mirkwood] Россия https://ru.linkedin.com/in/maksim-gumerov-039a701b
Дата: 28.06.04 09:22
Оценка:
Я вот и не знаю, как правильнее с точки зрения расширяемости:
Например, появилась возможность изменять узлы дерева — а мы ранее руководствовались лишь сообщениями о включении узла и исключении узла? Придется изменять код вида. А если вид строит себя с нуля, он это учтет автоматически.

Slicer
Специалист — это варвар, невежество которого не всесторонне :)
Re[3]: MVC при нестандартном интерфейсе пользователя
От: AndreyFedotov Россия  
Дата: 28.06.04 09:36
Оценка: 1 (1)
Здравствуйте, Slicer [Mirkwood], Вы писали:

SM>Я вот и не знаю, как правильнее с точки зрения расширяемости:

SM> Почему ИЛИ? Вид может, получив извещение об изменении модели — анализировать, что из нужной ему информации изменилось (а возможно, что и ничего), и в зависимости от этого — перерисовывать себя или даже перестраивать.
SM>Например, появилась возможность изменять узлы дерева — а мы ранее руководствовались лишь сообщениями о включении узла и исключении узла? Придется изменять код вида. А если вид строит себя с нуля, он это учтет автоматически.
Если модель изменяется — особенно если изменяется её интерфейс — то это неизбежно повлечёт изменение кода тех представлений (видов), которые зависят от этих изменений.
Лучшее, чего можно достичь — это того, что бы при изменении интерфейса и кода модели — менялся код только тех представлений, которые непосредсвенно от этих изменений зависят и не менялся код всех остальных.
SM>Slicer
Re[4]: MVC при нестандартном интерфейсе пользователя
От: Slicer [Mirkwood] Россия https://ru.linkedin.com/in/maksim-gumerov-039a701b
Дата: 28.06.04 09:51
Оценка:
AF> Почему ИЛИ? Вид может, получив извещение об изменении модели — анализировать, что из нужной ему информации изменилось (а возможно, что и ничего), и в зависимости от этого — перерисовывать себя или даже перестраивать.
Ну это и был первый вариант, по сути. Естественно, не все время сканировать изменения Но определять, что именно изменилось, в этом случае будет сам вид.

SM>>Например, появилась возможность изменять узлы дерева — а мы ранее руководствовались лишь сообщениями о включении узла и исключении узла? Придется изменять код вида. А если вид строит себя с нуля, он это учтет автоматически.

AF> Если модель изменяется — особенно если изменяется её интерфейс — то это неизбежно повлечёт изменение кода тех представлений (видов), которые зависят от этих изменений.
Вроде бы, не обязательно: изменения ведь разные бывают. Я говорю лишь о появлении новых направлений изменения (содержимого) модели. В приведенном примере новое такое направление (возможность изменения узлов дерева) привело к изменению интерфейса модели (добавился доступ на запись к узлам), но для вида, отображающего всех детей данного узла, если он будет воссоздавать себя "с нуля" (т.е. анализируя не изменения, а конечный результат), это изменение интерфейса модели не приведет к изменению кода вида.

Slicer
Специалист — это варвар, невежество которого не всесторонне :)
Re[5]: MVC при нестандартном интерфейсе пользователя
От: A.J. Россия CintaNotes
Дата: 28.06.04 10:30
Оценка: 10 (1)
Здравствуйте, Slicer [Mirkwood], Вы писали:

SM>Вроде бы, не обязательно: изменения ведь разные бывают. Я говорю лишь о появлении новых направлений изменения (содержимого) модели. В приведенном примере новое такое направление (возможность изменения узлов дерева) привело к изменению интерфейса модели (добавился доступ на запись к узлам), но для вида, отображающего всех детей данного узла, если он будет воссоздавать себя "с нуля" (т.е. анализируя не изменения, а конечный результат), это изменение интерфейса модели не приведет к изменению кода вида.


Вообще, если у тебя простой вид, тогда в данном конкретном случае можно было бы определить для модели просто событие NodeChanged (или что у тебя там), и передавать узел, начиная с которого надо все перерисовать.
Но я бы так делать не стал — модель не должна плясать от потребностей вида. ИМХО лучше все-таки постараться покрыть всевозможные интересные любому клиенту модели (виду разумеется в том числе) события.

Пример: как это сделано в Java Swing:
Компонент JTree состоит из вида (TreeUI),контроллера (собственно JTree, который всех троих и связывает воедино), и модели дерева, реализующей интерфейс TreeModel. Этот интерфейс позволяет компоненту опрашивать структуру данных, которую он должен отображать в виде дерева. В нем определены методы типа "получить корень дерева", "получить все дочерние узлы такого-то узла", "определить, есть ли у узла дочерние узлы" и т.п. — всего 8 методов.

Класс DefaultTreeModel, модель дерева "по умолчанию", реализует TreeModel и определяет для модели следующие события:


Т.о. модель оповещает всех подписавшихся об этих событиях (среди которых могут быть не только виды, но и другие классы). Легко видеть, что при такой схеме событий предусматриваются фактически любые изменения древовидной структуры данных.

При этом, если мне нужно отобразить свою структуру данных в виде дерева, я просто пишу свой TreeModel, и подставляю его вместо стандартного — все, дерево готово, без всякого дублирования данных. В своей модели я могу определить свои события, или просто унаследовать от DefaultTreeModel, и тогда я могу пользоваться теми, что определены в DefaultTreeModel — просто по мере необходимости вызывать fireTreeModesChanged и т.п.
После этого можно навешивать обработчики на контроллер дерева, чтобы оно стало интерактивным.
Re[6]: MVC при нестандартном интерфейсе пользователя
От: Slicer [Mirkwood] Россия https://ru.linkedin.com/in/maksim-gumerov-039a701b
Дата: 28.06.04 12:35
Оценка:
Ну это все понятно, это лишь описание того, как реализовать второй вариант. А мне интересны доводы в пользу первого и второго. Что гибче-то?
Ведь, по идее, трудно предусмотреть все возможные события. Следует исходить из того, что классы развиваются. И события появятся новые. И тогда сиди и переписывай код всех видов.

Slicer
Специалист — это варвар, невежество которого не всесторонне :)
Re[7]: MVC при нестандартном интерфейсе пользователя
От: A.J. Россия CintaNotes
Дата: 28.06.04 13:15
Оценка:
Здравствуйте, Slicer [Mirkwood], Вы писали:

SM>Ну это все понятно, это лишь описание того, как реализовать второй вариант. А мне интересны доводы в пользу первого и второго. Что гибче-то?

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

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

А если нет, значит, надо события делать более общего характера, чтобы новые события были просто разновидностью старых. Множество событий модели должно быть полным. В предельном случае объявляется событие somethingChanged которое посылается при любом изменении модели — но тут мы приходим к твоему второму варианту. Тут есть еще одна возможность: как-то вместе с этим событием передавать еще информацию о том, _что_конкретно_ изменилось.
Вид тогда на основе этой информации будет обновлять те мини-виды, которых эти изменения затрагивают.

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


Если ты уж так хочешь сделать виды независимыми от модели — определи тот минимум информации, который тебе нужен для отображения модели, запиши его в виде интерфейса и пусть модель его реализует (или сделай отдельный класс-посредник, который будет интерпретировать модель для отображения во всех видах. Тогда можешь изменять модель как хочешь, видам на это будет начхать — им будут в любом случае давать только ту информацию, которую они в состоянии отобразить. И если модель структурно изменится, надо будет изменить только один класс-посредник.)

Сложно? Да. Но ты ведь хочешь получить универсальность, гибкость и независимость видов. А за это, как правило, приходится платить новыми абстракциями.

Re[8]: MVC при нестандартном интерфейсе пользователя
От: Slicer [Mirkwood] Россия https://ru.linkedin.com/in/maksim-gumerov-039a701b
Дата: 28.06.04 13:24
Оценка:
AJ>Если ты уж так хочешь сделать виды независимыми от модели — определи тот минимум информации, который тебе нужен для отображения модели, запиши его в виде интерфейса и пусть модель его реализует (или сделай отдельный класс-посредник, который будет интерпретировать модель для отображения во всех видах.
Ну и как ты предлагаешь это сделать?
Проблема в том, что для отображения модели нужно не так много информации, как для отслеживания изменения данных в этой модели. Создать инвариантный интерфейс чтения информации легко, а вот отслеживать изменения сложнее.

Slicer
Специалист — это варвар, невежество которого не всесторонне :)
Re[7]: MVC при нестандартном интерфейсе пользователя
От: A.J. Россия CintaNotes
Дата: 28.06.04 13:27
Оценка:
Здравствуйте, Slicer [Mirkwood], Вы писали:

SM>Ну это все понятно, это лишь описание того, как реализовать второй вариант. А мне интересны доводы в пользу первого и второго. Что гибче-то?


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

На примере дерева — не реагировать на NodeChanged, если удаленный узел все равно не видно, но реагировать на NodeInserted и NodeRemoved если у тебя там, скажем, где-то считается и показывается суммарное кол-во узлов.
Re[8]: MVC при нестандартном интерфейсе пользователя
От: A.J. Россия CintaNotes
Дата: 28.06.04 13:43
Оценка: 6 (1)
Здравствуйте, A.J., Вы писали:

AJ>На примере дерева — не реагировать на NodeChanged, если удаленный узел все равно не видно


тут следует читать: "..если измененный (переименованный) узел все равно не видно..."

SM>Ну и как ты предлагаешь это сделать?

SM>Проблема в том, что для отображения модели нужно не так много информации, как для отслеживания изменения данных в этой модели. Создать инвариантный интерфейс чтения информации легко, а вот отслеживать изменения сложнее.

Ну, прежде всего: виды у тебя отслеживают изменение только того аспекта модели, который имеет какое-то влияние на ее отображение в этих видах. Все остальное их не касается. Это, думаю, очевидно.
Ты хочешь, чтобы виды у тебя не менялись. То есть, их постоянно будет интересовать в модели только какой-то определенный аспект. Когда информация, относящаяся к этому аспекту меняется, ты хочешь чтобы виды обновились — причем только та их часть, которая отвечает за отображение изменившегося элемента(ов).
Следовательно, для отслеживания этих изменений тебе нужно знать, какие элементы изменились. Это реализуется с помощью того же инвариантного интерфейса чтения информации с возможностью навигации по модели.
Дальше. Как эту информацию передавать. Если тебя интересует только простое обновление,
то информация о том, где в модели что обновилось, передается вместе в событием somethingChanged
в виде атрибутов опеределенного тобой объекта-события.

Пример (немного утрированный): Пусть модель — это текст, состоящий из n строк.
Далее имеем: общий вид — отвечает за обработку событий от модели и прорисовку мини-видов
Мини-виды: каждый отвечает за отрисовку одной строки.

Итак, пусть изменилась строка 3 .
Модель: посылает общему виду сообщение textChanged c параметром "lineNumber=3".
Общий вид: проверяет, находится ли мини-вид, отображающий строку 3, на экране.
Если да, то посылает мини-виду команду перерисоваться. Мини-вид запрашивает у модели
содержимое строки 3 и перерисовывается.
Если строки 3 на экране все равно нет, общий вид ничего не делает.
Re[8]: MVC при нестандартном интерфейсе пользователя
От: Slicer [Mirkwood] Россия https://ru.linkedin.com/in/maksim-gumerov-039a701b
Дата: 28.06.04 14:37
Оценка:
А адаптабельнее вроде как наоборот вариант с одним событием.. Ладно,идея ясна.

Может кто предложить серебряную пулю? Или только эти 2 способа просматриваются?

Slicer
Специалист — это варвар, невежество которого не всесторонне :)
Re[9]: MVC при нестандартном интерфейсе пользователя
От: Slicer [Mirkwood] Россия https://ru.linkedin.com/in/maksim-gumerov-039a701b
Дата: 28.06.04 14:49
Оценка:
Спасибо конечно Как реализовать вариант с множеством событий, я и сам догадался
А вот с ответом на изначальный вопрос похуже получается. Значит, все же компромисс.
Все равно спасибо за желание помочь.

AJ>Пример (немного утрированный): Пусть модель — это текст, состоящий из n строк.

AJ>Далее имеем: общий вид — отвечает за обработку событий от модели и прорисовку мини-видов
AJ>Мини-виды: каждый отвечает за отрисовку одной строки.
AJ>Итак, пусть изменилась строка 3 .
AJ>Модель: посылает общему виду сообщение textChanged c параметром "lineNumber=3".
AJ>Общий вид: проверяет, находится ли мини-вид, отображающий строку 3, на экране.
AJ>Если да, то посылает мини-виду команду перерисоваться. Мини-вид запрашивает у модели
AJ>содержимое строки 3 и перерисовывается.
AJ>Если строки 3 на экране все равно нет, общий вид ничего не делает.
Если на то пошло, то имхо лучше передавать все сообщения, кроме сообщений о добавлении/удалении обозреваемых элементов, минивидам этих элементов. Так хотя бы в некоторых случаях можно будет ограничиться переписыванием минивидов (которая при таком подходе, скорее всего, неизбежна при появлении новых событий) без переписывания главного вида.

Slicer
Специалист — это варвар, невежество которого не всесторонне :)
Re[10]: MVC при нестандартном интерфейсе пользователя
От: Slicer [Mirkwood] Россия https://ru.linkedin.com/in/maksim-gumerov-039a701b
Дата: 28.06.04 14:52
Оценка:
Уточнение...
SM>... передавать все сообщения, кроме сообщений о добавлении/удалении обозреваемых элементов, минивидам этих элементов.
Ну в крайнем случае, если ссылка на элемент как-то закодирована в теле сообщения, — ретранслировать всем минивидам. Пусть те интерпретируют и тот, к кому относится, обработает.

Slicer
Специалист — это варвар, невежество которого не всесторонне :)
Re[11]: MVC при нестандартном интерфейсе пользователя
От: A.J. Россия CintaNotes
Дата: 28.06.04 15:11
Оценка:
Здравствуйте, Slicer [Mirkwood], Вы писали:

SM>Уточнение...

SM>>... передавать все сообщения, кроме сообщений о добавлении/удалении обозреваемых элементов, минивидам этих элементов.
SM>Ну в крайнем случае, если ссылка на элемент как-то закодирована в теле сообщения, — ретранслировать всем минивидам. Пусть те интерпретируют и тот, к кому относится, обработает.

Можно и так, конечно.. ты подумай, что будет в твоем случае эффективнее
Так сообщение приходит только тому, кому надо (кстати, я подразумевал что общий вид не имеет собственного представления и служит только для того, чтобы координировать отрисовку минивидов.)
А так оно будет приходить всем. А если этих минивидов — тысячи?? С другой стороны, иногда вид-координатор и правда может быть излишним, когда мини-видов всего несколько и можно без проблем подписать каждый на "рассылку" событий от модели.

SM>Спасибо конечно Как реализовать вариант с множеством событий, я и сам догадался

SM>А вот с ответом на изначальный вопрос похуже получается. Значит, все же компромисс.

В любом случае вариант в постоянным удалением-созданием видов —


SM>Все равно спасибо за желание помочь.


не за что
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.