Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Потрепаться";
Текущий архив: 2005.01.23;
Скачать: [xml.tar.bz2];

Вниз

Обособление интерфейсной части в программе. Способы.   Найти похожие ветки 

 
Aldor_   (2005-01-03 20:38) [0]

Имеем чисто интерфейсный модуль, который о решаемой задаче мало что "знает". Например, в нем описана форма с функциями Get<Value> (считать то, что ввел юзер в поля) и Set<Value> (вывести что-то там-то и так-то). При этом интерфейсный модуль имеет доступ к engine-модулю, процедуры которого вызывает (например, DoJob :).

 Engine-модуль тоже "поставляет" информацию интерфейсному модулю (например о том, какие возможности для выбора юзер должен иметь, чтобы интерфейс эти возможности должным образом обеспечил). Также engine-модуль, как уже было сказано, вызывает процедуры из интерфейсного модуля.
 
 Отсюда проблема: оба модуля "видят" друг друга, не удается обеспечить какое-либо однообразие в том, "кто кого должен вызывать". Нет закономерности, по которой человек, читающий код будет знать, то ли engine-модуль берет значение из интерфейса через Form.GetValue, то ли интерфейс вызывает процедуру engine-модуля SetValue.

 А отсюда вопрос: как вы обеспечиваете независимость интерфейса и задачи?


 
Alex Konshin ©   (2005-01-03 21:24) [1]

Обычно в таких ситуациях разделение естественно.
Тот "engine"-модуль не должен имет зависимости от интерфейсного модуля. Если же обработка некого события в engine требует выполнения каких-то действий в интерфейсе (перерисовки?), то эти действия должны быть в методах интерфейсного модуля, а вызываться они будут как обработчики событий. То есть, например, в engine описываешь событие OnЧто-тоChanged с нужными параметрами и вставляешь вызов его обработчика в нужных местах. Интерфейсный модуль просто вышает свои обработчики.
Этот прием позволяет избежать зависимостей во многих ситуациях взаимодействия какого-то объекта и формы. Если же развязать их все-таки не удается (что странно), то можно использовать второй прием - описание абстрактных классов или интерфейсов в третьем модуле и включение его в uses обоих модулей.


 
Alex Konshin ©   (2005-01-03 21:33) [2]

Да, кстати, иногда приходится передавать события через windows-сообщения. Особенно если они ассинхронные.

Полезно еще ввести блокировку обновления интерфейса (обычно достаточно счеткика и try-finally), чтобы избежать рекурсивных событий. Например, мы можем обновить состояние некого контрола, и это действие может спровоцировать другое событие.


 
Vaitek ©   (2005-01-03 22:11) [3]

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


 
Aldor_   (2005-01-03 22:40) [4]

>  Alex Konshin ©
 Спасибо, объяснение классное. Пара вопросов:

передавать события через windows-сообщения

 ?? А как можно передать сообщение в engine-модуль, если не создавать доп. поток? Или может мне просто выспаться недо? %)

Полезно еще ввести блокировку обновления интерфейса (обычно достаточно счеткика и try-finally)
 Так ведь для этого нужно, чтобы engine имел доступ к интерфейсу. Или опять дело в высыпании? И причем тут счетчик?


 
Alex Konshin ©   (2005-01-03 23:09) [5]

передавать события через windows-сообщения

?? А как можно передать сообщение в engine-модуль, если не создавать доп. поток? Или может мне просто выспаться недо? %)

А зачем engine получать сообщения? Интерфейс может спокойно вызывать его методы. Интерфейс зависит от engine, но не наоборот.
Хотя, если твой engine вертится в отдельном потоке, то может там и придется делать какие-то ухищрения, но для интерфейса все должно быть пофиг - engine должен выглядеть как черный ящик, у которого наружу торчат только методы и eventы. Вместо delphi eventов может быть какой-то иной callback механизм. Как там устроено внутри - личное дело того самого engine.

Полезно еще ввести блокировку обновления интерфейса (обычно достаточно счеткика и try-finally)
Так ведь для этого нужно, чтобы engine имел доступ к интерфейсу. Или опять дело в высыпании? И причем тут счетчик?



if LocCounter>0 then DeferChanges;
Inc(LockCounter);
try
 DoChanges;
finally
 Dec(LocCounter);
end;

Понятно, что в каждом случае вместо DeferChanged и DoChanges будут разные действия. В вырожденной ситуации DeferChanges может, например, быть пустой, т.е. изменения просто игнорируются. Тут же, как раз-таки для реализации DeferChanges может быть полезны windows сообщения: просто кидаем свое сообщение себе же, а в обработчике будет все тот же try-finaly и DoChanges. Хитрость тут в том, что мы откладываем обработку каких-то событий, чтобы не породить рекурсию вызовов. Правда, можно нарваться на рекурсию посылки сообщений, но вот тут и помогут счетчики вложенности.

Это все может быть тяжело для понимания, но советую все-таки разобраться. Универсального решения все равно нет, но имея в арсенале несколько таких приемов можно выкрутится из любой такой путаницы. Рекомендую посмотреть, как работают DoLayout,DeferLayout,InvalidateLayout и т.п. в реализации гридов.
Там по сути все эти способы используются.


 
Alex Konshin ©   (2005-01-03 23:16) [6]

Да, уж... столько опечаток...

if LockCounter>0 then DeferChanges
else
 begin
   Inc(LockCounter);
   try
     DoChanges;
   finally
     Dec(LockCounter);
     CommitChanges; // ??? optional
   end;
 end;

Понятно, это всего лишь общая схема.


 
iZEN ©   (2005-01-04 20:10) [7]

Я такую ситуацию разруливаю следующим образом:
1) Объявляю интерфейс View с методом refresh(Object source) - модуль 1;
2) Имплементирую View в Form - модуль 2;
3) Создаю DataModel, которая "знает" только о View и ни о чём больше не догадывается (метод setView(View view)) - модуль 3;
4) Создаю Controller, который имеет представление о Form, DataModel и событиях, которые могут происходить в системе (например, ему известно, что пользователь нажал на Form) - модуль 4;
5) Пишу в Controller процедуру инстанцирования экземпляров DataModel, Form, назначаю для DataModel конкретную форму (dataModel.setView(form)), описываю ответные реакции на действия пользователя в форме, собираю всё вместе и фсё.

Приблизительно так:
http://izen.dev.juga.ru/notebook/prototypes/j2me/midp1/secondmidlet.html



Страницы: 1 вся ветка

Форум: "Потрепаться";
Текущий архив: 2005.01.23;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.48 MB
Время: 0.039 c
1-1105187147
Unknown
2005-01-08 15:25
2005.01.23
Несколько различных вопросов


14-1104250139
Чеширский_Кот
2004-12-28 19:08
2005.01.23
AOL


14-1104623168
Германн
2005-01-02 02:46
2005.01.23
Новый Год - прекрасная пора!


1-1105226563
reaper
2005-01-09 02:22
2005.01.23
WebBrowser и ShellListView в одном окне


9-1096986377
ninja
2004-10-05 18:26
2005.01.23
смотрите, изометрия :)





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский