Motto

В тихом саду здравомыслия
Пусть на вас постоянно падают
кокосовые орехи пробужденности.
Чогьям Трунгпа РИНПОЧЕ


Версия для мобильного


суббота, 22 мая 2010 г.

Текучка 12: Велосипед для езды по минному полю. Или почему не стоит вешать обработчики Application.On...

Это история о том, как один программист, начал изобретать групповой делегат (multicast delegate) для обработки события Application.OnMessage, не зная о том, что подобный класс включён в стандартную поставку Delphi.

Одному программисту понадобилось сделать так, чтобы одна из форм обрабатывала событие Application.OnMessage. Программист не раз встречал подобный код у себя в библиотеках. Обычно это делалось так:

  • в секции юнита initialization, запоминался старый обработчик события
  • затем вешался свой обработчик события, который после выполнения своих операций вызывал запомненный старый обработчик
  • а восстанавливалась цепочка, как правило, в секции finalization

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

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

Ведь если предположить, что будет открыто 2 таких формы. Например, так:

  • сначала создаётся Форма А, которая запоминает оригинальный обработчик события, и вешает свой обработчик
  • потом создаётся Форма Б и запоминает обработчик события Формы А, и также вешает свой обработчик
  • а потом Форма А уничтожается, и восстанавливает оригинальный обработчик, затирая обработчик, установленный Формой Б
  • Форма Б перестаёт обрабатывать эти сообщения
  • и, что самое ужасное, при уничтожении Форма Б пытается восстановить запомненный обработчик (который принадлежал Форме А). Таким образом, Форма Б может спокойно затереть оригинальный обработчик, заменив его обработчиком несуществующей (уже) Формы А.

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

И тут, программист вдруг вспомнил, что в Delphi есть стандартный компонент TApplicationEvents. И подумал: "А что будет если я брошу на форму несколько таких компонентов?". Подумал - и бросил. Более того, даже присвоил им разные обработчики. Все обработчики отработали как надо. Хммм... - подумал программист и полез смотреть исходники этого компонента. И оказалось, что этот компонент, не вешает свои обработчики непосредственно на события объекта Application. А вместо этого, в initialization секции юнита AppEvents, создаёт внутренний объект MultiCaster, который и обрабатывает события Apllication. А при создании каждого объекта TApplicationEvents добавляет себя в очередь обработчиков с помощью метода TMultiCaster.AddAppEvent. "Дык, ёлы-палы!" - подумал программист и переписал код так, чтобы тот использовал TApplicationEvents, удалив нафиг велосипед собственного изобретения.

Программистом в этой истории был я. Модальной формой была вовсе не модальная форма, форма переключавшаяся в режим дизайнера с помощью DreamVCL, и перестававшая из-за этого реагировать на внешние события. А обработчик OnMessage здесь использовался для обработки горячих клавиш. А времени на это было угрохано больше чем следовало бы.

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


А спонсоры у этого поста такие:
  • Этот ресурс мне очень понравился. Это сайт курсов онлайн валют. Можно легко найти наиболее выгодный для себя обменник.
  • Сколько стоит сделать санитарную книжку в России? (p.s. вот уж не думал, что это платная услуга ;)

2 комментария:

  1. вы абсолютно правы с моралью...
    на самом деле в исходниках модулей стандартной поставки можно найти гораздо больше чем ожидает типовой программист-делфист... ИМХО по этой причине (незнанию) многие и считают его не серьезным языком...

    ОтветитьУдалить
  2. Это верно. Нужны чрезвычайно веские причины для того, чтобы делать свой велосипед. К сожалению, многие по неопытности предпочитают именно плодить кривые велосипеды, а не искать промышленные решения. С готовыми решениями то нужно разбираться, это утомительно, а лавров особых не даст - подумаешь, воспользовался. Другое дело, создать свое, гениальное!

    Поувольнял бы. Извините, накипело.

    ОтветитьУдалить

Постоянные читатели