:: Обсуждаем проекты
Статья
h.i.a.
Автор

Сообщений: 4002
Откуда: Мурманск/Спб/Мск
Дата регистрации: 18.11.2005
Идеи написать несколько статей по VFP посещают меня уже давно. 15 лет стучания по клавишам (кому-то из ветеранов эта цифра наверняка покажется смешной) дают свои результаты. Есть определенный объем накопленных знаний, иногда возникает желание им безвозмездно поделиться.
Вкратце опишу содержание первой статьи. Она будет посвящена реализации технологии "подписки на изменение состояния объектов". Первый уровень подписчиков (назовем его локальный) - объекты, находящиеся внутри окна. Второй уровень (глобальный) - сами окна. Функцию рассылки уведомления об изменении состояния выполняет "Служба оповещения".
Сразу отвечу на вопрос: зачем все это надо? Рассматриваемая технология решает проблему жесткой связи объектов. Отправитель уведомления полностью изолирован от получателей. Он "не знает" их названия, количества и месторасположения.
Хочется услышать мнение сообщества о ценности (или бесполезности) предлагаемого материала.



Исправлено 1 раз(а). Последнее : h.i.a., 01.07.07 14:28
Ratings: 0 negative/0 positive
Re: Статья
Владимир Максимов

Сообщений: 14095
Откуда: Москва
Дата регистрации: 02.09.2000
Больше статей, хороших и разных! (почти цитата

После определенного количества лет "стучания по клавишам", практически любая статья выливается не столько в тонкости описания той или иной реализации на конкретном языке, сколько общей идеологии (стратегии и тактики) решения описанной проблемы.

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

Хотя, конечно, и собственно способ реализации на FoxPro описать не помешает ;)

Правда, я не понял, о чем собственно статья будет. Но думаю из самой статьи что-нибудь пойму.
Ratings: 0 negative/0 positive
Re: Статья
DIMM@

Сообщений: 522
Откуда: Витебск
Дата регистрации: 21.03.2006
Конечно писать!
Кстати не об этом ли приблизительно идет речь? www.javaportal.ru
Ratings: 0 negative/0 positive
Re: Статья
h.i.a.
Автор

Сообщений: 4002
Откуда: Мурманск/Спб/Мск
Дата регистрации: 18.11.2005
DIMM@
Конечно писать!
Спасибо за поддержку.
DIMM@
Кстати не об этом ли приблизительно идет речь? www.javaportal.ru
Нет, немного не об этом. Служба оповещения в отличие от Посредника не содержит ссылок ни на отправителя, ни на получателя сообщения. Основная его задача - оповещение подписчиков об изменении данных. Например, при изменении наименования товара в справочнике новое наименование автоматически обновляется во всех открытых окнах, подписанных на прием уведомления об изменении справочника товаров. Какие окна будут открыты пользователем в момент изменения предугадать невозможно.



Исправлено 1 раз(а). Последнее : h.i.a., 04.07.07 15:13
Ratings: 0 negative/0 positive
Re: Статья
Dag

Сообщений: 1156
Дата регистрации: 08.02.2006
Жду с огромным интересом. Хорошо бы сразу подкрепить теорию работающими классами.
Ratings: 0 negative/0 positive
Re: Статья
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
Hi h.i.a.!

А какая реализация используется (в общих чертах)?
Фоксовое позднее связывание?


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Статья
h.i.a.
Автор

Сообщений: 4002
Откуда: Мурманск/Спб/Мск
Дата регистрации: 18.11.2005
Igor Korolyov
Hi h.i.a.!
А какая реализация используется (в общих чертах)?
Фоксовое позднее связывание?
Цикл по формам/объектам и проверка наличия у объекта подписки на конкретное "уведомление". Объект может быть подписан на любое количество уведомлений.
Ratings: 0 negative/0 positive
Re: Статья
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
Hi Михаил!

Ой не, это чрезвычайно неэффективное решение
BindEvents() значительно лучше


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Статья
h.i.a.
Автор

Сообщений: 4002
Откуда: Мурманск/Спб/Мск
Дата регистрации: 18.11.2005
Здравствуй Игорь.
Чем же оно "чрезвычайно неэффективно"? Перебрать в цикле 10 открытых окон или 20 контроллов? Поверь, проблем со скоростью при таком подходе нет.
Связка bindevent предполагает обязательное наличие отправителя и получателя. Допустим, пользователь открыл 10 окон, открыл форму редактирования какого-нибудь справочника и изменил наименование. Форма редактирования не может "знать", сколько окон используют данный справочник. Возможно все 10 окон, а возможно и не одно. Также, как и "окна-подписчики" не знают, открыта или нет "форма-отправитель". К тому же, одно окно может отправлять оповещения нескольких типов, также как и "окна-подписчики" могут подписаться на несколько оповещений.
Я считаю, что bindevent очень удобная и полезная функция, но в данном случае ее использование неуместно.
Ratings: 0 negative/0 positive
Re: Статья
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
Hi Михаил!

Ну если формы простые то может быть, а если они сложные? Если там пользовательские визуальные классы применяются? Тогда надо не 20 контролов обходить (при этом возможно довольно глубоко вложенных) а гораздо больше - при этом определять их тип...
А подписка не обязательно должна быть "напрямую" - вполне можно через специальный класс-посредник сделать. В примитивнейшем случае даже просто в goApp завести "событие" которое и дёргать при любом изменении - т.е. тот кто хочет подписаться - всегда знает на что ему подписываться, а тот кто публикует - всегда знает что дёргать...

BindEvents это просто способ настроить кучу "мягких" связей между объектами. Он не диктует кто должен событие публиковать.
Просто в других языках может и не оказаться возможности "обойти все открытые формы"


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Статья
h.i.a.
Автор

Сообщений: 4002
Откуда: Мурманск/Спб/Мск
Дата регистрации: 18.11.2005
Здравствуй Игорь.
Igor Korolyov
Ну если формы простые то может быть, а если они сложные? Если там пользовательские визуальные классы применяются? Тогда надо не 20 контролов обходить (при этом возможно довольно глубоко вложенных) а гораздо больше - при этом определять их тип...
Насколько сложные? В реал-тайм системах с сотнями событий в секунду перебор мог бы стать "узким" местом... Какое количество объектов должно быть на форме, чтобы их обход вызвал заметную задержку? Миллион ? Боюсь, узнать не удастся, фокспро не переживет инициализации такой формы
Обработка вложенности объектов (контейнеры и страничные блоки) в решении реализована, а классы по-умолчанию должны быть "пользовательскими" (решение не исключает использование базовых контроллов, но уведомления они получать не смогут). Тип объекта значения не имеет.
Igor Korolyov
А подписка не обязательно должна быть "напрямую" - вполне можно через специальный класс-посредник сделать.
Служба оповещения является промежуточным классом, но реализация отличается от паттерна "Посредник", упомянутого DIMM@
Igor Korolyov
Просто в других языках может и не оказаться возможности "обойти все открытые формы"
Если стандартной возможности обхода не предусмотрено, можно создать коллекцию форм, в init/destroy соответственно добавлять/удалять элементы. Если и этой возможности в "другом" языке не окажется, стоит задуматься нужен ли такой язык
Ratings: 0 negative/0 positive
Re: Статья
Prudivus

Сообщений: 4283
Откуда: Кишинев
Дата регистрации: 14.12.2006
У меня подобный функционал реализован через пару методов в базовом классе форм: getMessage(msg_type, msg_body) и sendMessage(msg_type, msg_body, form_name, form_class). По мере надобности форма рассылает сообщения (об изменении каких-то своих свойств, данных и т.п.), одной форме, группе форм (классу) или всем открытым формам сразу. Принимающая форма анализирует сообщение и реагирует на него. Типичные сценарии:
- изменение данных таблицы Т1 -> обновить курсор, связанный с Т1;
- выбор из справочника (без его закрытия, множественный последовательный) -> добавить строку;
- перемещение формы на экране (для "привязанных" и/или дочерних форм) -> переместиться вместе с главной формой.

Делать ли для подобных задач (именно в фоксе) посредника - вопрос весьма спорный.
Ratings: 0 negative/0 positive
Re: Статья
Dag

Сообщений: 1156
Дата регистрации: 08.02.2006
На это можно посмотреть?
Ratings: 0 negative/0 positive
Re: Статья
Prudivus

Сообщений: 4283
Откуда: Кишинев
Дата регистрации: 14.12.2006
Dag
На это можно посмотреть?
К кому вопрос?
Ratings: 0 negative/0 positive
Re: Статья
h.i.a.
Автор

Сообщений: 4002
Откуда: Мурманск/Спб/Мск
Дата регистрации: 18.11.2005
Prudivus
Делать ли для подобных задач (именно в фоксе) посредника - вопрос весьма спорный.
Он у Вас уже сделан, так как разорвана связь между отправителем и получателем посредством интерфейса sendMessage/getMessage. В отличие от моей реализации у Вас все "прикручено" к классу окна.
Я стараюсь придерживаться подхода Мартина Фаулера: лучше определить несколько "маленьких" классов, хорошо выполняющих свои "маленькие" задачи, чем один универсальный. Спорить из-за этого нюанса не вижу смысла, оба подхода имеют право на существование, суть от этого не меняется


------------------
Ratings: 0 negative/0 positive
Re: Статья
Dag

Сообщений: 1156
Дата регистрации: 08.02.2006
Prudivus
К кому вопрос?

К Вам.
Ratings: 0 negative/0 positive
Re: Статья
Prudivus

Сообщений: 4283
Откуда: Кишинев
Дата регистрации: 14.12.2006
h.i.a.
Prudivus
Делать ли для подобных задач (именно в фоксе) посредника - вопрос весьма спорный.
Он у Вас уже сделан, так как разорвана связь между отправителем и получателем посредством интерфейса sendMessage/getMessage.
Фактически да, хотя можно было бы сделать еще класс диспетчера сообщений, как предлагал Игорь Королев.

Цитата:
На это можно посмотреть?
Код довольно простой. В базовом классе моих форм определены два метода:
* sendMessage: отправка сообщений другим формам
lparameters msg_type, msg_body, form_name, form_class
local i
if pcount() < 1 or empty(m.msg_type)
return
endif
if pcount() < 2 or empty(m.msg_body)
return
endif
if pcount() < 3 or empty(m.form_name)
m.form_name = ""
else
m.form_name = upper(m.form_name)
endif
if pcount() < 4 or empty(m.form_class)
m.form_class = ""
else
m.form_class = upper(m.form_class)
endif
with _screen
for m.i = 1 to .formcount && цикл по всем формам...
* ...кроме себя! и только для тех, кто может принимать сообщения
if .forms(m.i) <> thisform &&and;
pemstatus(.forms(m.i), "getmessage", 3) = "Method"
if upper(.forms(m.i).name) = m.form_name;
and upper(.forms(m.i).class) = m.form_class && проверка условий отбора
* посылка сообщений
TRY && возможна ошибка
.forms(m.i).GetMessage(m.msg_type, m.msg_body)
CATCH
* нет такого метода или что-то еще...
ENDTRY
endif
endif
endfor
endwith

* getMessage: получение сообщения
lparameters msg_type, msg_body
* базовые сообщения
DO CASE
case m.msg_type = "DATACHANGE"
IF USED(m.msg_body);
AND CURSORGETPROP("SourceType", m.msg_body) <= 2;
AND NOT "?"$CURSORGETPROP("SQL", m.msg_body)
IF thisform.editing
IF CURSORGETPROP("Buffering", m.msg_body) < 5
=REQUERY(m.msg_body)
ELSE
IF GETNEXTMODIFIED(0, m.msg_body)=0
=REQUERY(m.msg_body)
ENDIF
ENDIF
ELSE
=REQUERY(m.msg_body)
ENDIF
ENDIF
case m.msg_type = "TYPE"
wait window nowait m.msg_type
case m.msg_type = "NAME"
wait window nowait m.msg_body + thisform.name
ENDCASE

Пример посылки сообщения об изменении в данных:
thisform.SendMessage("DATACHANGE", thisform.alias) && послать сообщения всем формам

Формы, в которых необходимо расширить базовый функционал, могут переопределять/дополнять метод getMessage, например:
lparameters msg_type, msg_body
* принять сообщение...
DO CASE
CASE m.msg_type="MOVE" && перемещение
thisform.Left = thisformset.docs.Left
thisform.Top = MIN(thisformset.docs.Top + thisformset.docs.Height +;
SYSMETRIC(4)*1.5 + SYSMETRIC(9),;
_screen.Height - thisform.Height)
CASE m.msg_type="ADDPRODUCT" && добавление товарной строки...
* инициализируем количество
LOCAL кол
DO FORM getvalue WITH 1.000,;
"Введите количество" TO m.кол
IF NOT ISEMPNLL(m.кол)
INSERT INTO doclines (id, id_doc, товар, количество);
VALUES (newid(), docs.id_doc, m.msg_body, m.кол)
thisformset.add_docline(m.msg_body)
SELECT doclines
thisform.editing = .t.
thisform.refresh
ENDIF
CASE m.msg_type="PICKPRODUCT" && инициализация товарной строки...
thisformset.add_docline(m.msg_body)
CASE m.msg_type="DATACHANGE"
IF m.msg_body = "DOCLINES"
thisform.requery
ENDIF
ENDCASE

Например, нужно обработать события перемещения главной формы формсета. Метод moved главной формы:
thisformset.doclines.getmessage("MOVE")
thisformset.master_docs.getmessage("MOVE")
thisformset.slave_docs.getmessage("MOVE")
thisformset.docs_log.getmessage("MOVE")
Вот примерно так.
Ratings: 0 negative/0 positive
Re: Статья
Dag

Сообщений: 1156
Дата регистрации: 08.02.2006
Prudivus

Спасибо. Интересное решение.
Ratings: 0 negative/0 positive
Re: Статья
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
Hi Вадим!

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

PUBLIC goApp
goApp = CREATEOBJECT("App")
DEFINE CLASS App AS Custom
PROCEDURE table1Changed
ENDPROC
PROCEDURE list1Moved
LPARAMETERS tnLeft, tnTop, tnWidth, tnHeight
ENDPROC
PROCEDURE GenericMessage
LPARAMETERS tcName, toData
* Не очень хорошо, но если хочется "всегда один приёмник" то сойдёт
ENDPROC
ENDDEFINE

Ну и далее всё просто. Тот кто хочет "оповестить" делает
goApp.list1Moved(This.Left, This.Top, This.Width, This.Height)
или лучше
RAISEEVENT(goApp, "list1Moved", This.Left, This.Top, This.Width, This.Height)
тот кому "интересно" делает
BINDEVENT(goApp, "list1Moved", This, "parentListMoved")
Ну и соответственно в самом методе parentListMoved принимает параметры и реагирует на произошедшее "событие"...
В качестве "событий" можно использовать не только методы goApp, а например свойства динамически добавленные к _SCREEN (если не нужны параметры)...
Все знают про goApp, но никто не знает о подписчиках и генераторах событий (они никак не связаны - хотя если надо, то одним из параметров генератор может передать ссылку на самого себя).

Вот эту схему я понимаю А обходы _SCREEN.Forms а в общем случае ещё и рекурсивный обход вложенных в форму контейнеров - это есть некрасиво. Ессно что для VFP7 и ранее это не годится. AFAIK подобная схема может применяться в C#. И не нужно никаких "форм" или их коллекций для работы.


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Статья
Prudivus

Сообщений: 4283
Откуда: Кишинев
Дата регистрации: 14.12.2006
Согласен, эта схема красивее. Мой вариант писался еще для VFP6. Хотя, конечно, и для VFP6 можно было бы реализовать подобную схему, но "чуть большей кровью".

Изначально ставилась задача отобразить изменение данных в другой, заранее неизвестной открытой форме. Эта проблема появилась после перевода БД на MSSQL и, соответственно, использования Remote View. С родными фоксовыми таблицами все рефрешилось автоматом и проблемы не было. Потом оказалось что система сообщений может применяться и для других целей... Привязывать контролы разных форм друг к другу - пока с такими потребностями не сталкивался.



Исправлено 1 раз(а). Последнее : Prudivus, 25.07.07 19:39
Ratings: 0 negative/0 positive


Извините, только зарегистрированные пользователи могут оставлять сообщения в этом форуме.

On-line: 1 (Гостей: 1)

© 2000-2024 Fox Club 
Яндекс.Метрика