На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Друзья, соблюдайте, пожалуйста, правила форума и данного раздела:
Данный раздел не предназначен для вопросов и обсуждений, он содержит FAQ-заготовки для разных языков программирования. Любой желающий может разместить здесь свою статью. Вопросы же задавайте в тематических разделах!
• Если ваша статья может быть перенесена в FAQ соответствующего раздела, при условии, что она будет оформлена в соответствии с Требованиями к оформлению статей.
• Чтобы остальным было проще понять, указывайте в описании темы (подзаголовке) название языка в [квадратных скобках]!
Модераторы: Модераторы
  
> Обработчики событий без объекта , [Delphi]
    Все обработчики событий OnXXX имеют тип метода объекта (procedure / function of object). Однако не всегда есть объект, куда можно запихнуть эти обработчики. Что же делать? Заводить объект (и значит, писать Create и Free) только из-за этого?
    Выход появился с введением в D2009 есть с применением методов класса. Они совместимы с методами объекта, а значит, пригодны для использования в качестве обработчиков.
    ExpandedWrap disabled
      type
        TEventHandler = class
          class procedure ThreadTerminate(Sender: TObject);
        end;
       
      class procedure TEventHandler.ThreadTerminate(Sender: TObject);
      begin
        ...
      end;
       
      ...
       
      SomeThread.OnTerminate := TEventHandler.ThreadTerminate;


    Добавлено
    Таким образом можно собрать все обработчики в логическую кучку и избежать необходимости создания отдельного объекта. Только надо не забыть не использовать в методах Self - для class методов он указывает непосредственно на сам класс, а не на экземпляр класса (объект), что в нашем случае практически бесполезно.
    Кроме того, доступ из этих методов будет только к другим class методам и полям.

    См. также шаманский метод leo в следующем посте.
    Сообщение отредактировано: Fr0sT -
      Цитата Fr0sT @
      Выход появился с введением в D2009 методов класса.

      ?!
      Во-первых, методы класса существовали с незапамятных времен. Во-вторых, если в методе не используется Self, то создавать реальный объект вызовом Create совершенно не обязательно - можно юзать nil или через реальную переменную или просто приведением типа:
      MyThread.OnTerminate:=TEventHandler(nil).ThreadTerminate;
        Цитата leo @
        Во-первых, методы класса существовали с незапамятных времен

        Хм, да, действительно, даже в D7 есть. Значит, это я только в 2009-ых до них добрался))
        Цитата leo @
        MyThread.OnTerminate:=TEventHandler(nil).ThreadTerminate;

        О, вот это тоже крутой метод, не знал!
          Цитата Fr0sT @
          Хм, да, действительно, даже в D7 есть

          Даже в D3 есть ;), ведь у TObject практически все методы - классовые
            Лучше все-таки завести объект и назначить обработчиком обычный instance-метод, чем связываться с грязными приемчиками. Управление временем жизни такого объекта не такая уж и проблема, тем более что это управление может быть не ручным, а автоматическим. Да и вообще, я считаю, что если в некой системе существует объект, генерирующий события, то странно что в этой системе нет места объекту, который по логике должен их обрабатывать - это уже не ООП, а какой-то гибрид.
              --Ins--, ну, мой метод вовсе не грязен :) Кто ж виноват, что нет возможности просто и без запарок назначить обработчик, не засовывая его в класс.
              Цитата --Ins-- @
              если в некой системе существует объект, генерирующий события, то странно что в этой системе нет места объекту, который по логике должен их обрабатывать - это уже не ООП, а какой-то гибрид

              Во-первых, ну и что? Гибрид так гибрид. Глобальные переменные и самостоятельные функции тоже уже не ООП, и кого это волнует.
              Во-вторых, объекта может не быть чисто по структурным соображениям: к примеру, если есть сокет, у которого по OnConnect посылается запрос на сервер - то засовывать обработчик OnConnect в форму совершенно нелепо. Создавать экземпляр класса, который служит исключительно хранилищем обработчиков? Зачем плодить сущности, если можно обойтись без них.
              Сообщение отредактировано: Fr0sT -
                Цитата Fr0sT @
                --Ins--, ну, мой метод вовсе не грязен


                Ну по сравнению с методом leo в целом да :)

                Цитата Fr0sT @
                Во-первых, ну и что? Гибрид так гибрид. Глобальные переменные и самостоятельные функции тоже уже не ООП, и кого это волнует.


                Для функции расчета синуса/косинуса делать ее методом какого-либо класса действительно смысла нет. Но в случае отправитель-получатель (события) - так как-то сам собой объект напрашивается. Я имею в виду не в коде объект, а в абстрактной модели, описывающей поведение твоей системы. И вовсе необязательно это должна быть форма, а скорее даже наоборот - если источник события не ее собственные компоненты - то это скорее всего моветон. Для чего делать именно так (отвечая на вопрос "ну и что?") - для проведения декомпозиции, выработки хорошего стиля, грамотного подхода - всего того, что позволит максимально гибко решать в том числе и более сложные задачи проектирования, расширяемые и защищенные от багов и простые и понятные при описании принципов, в этих решениях реализованных.

                Цитата Fr0sT @
                Во-вторых, объекта может не быть чисто по структурным соображениям: к примеру, если есть сокет, у которого по OnConnect посылается запрос на сервер - то засовывать обработчик OnConnect в форму совершенно нелепо.


                В форму - совершенно нелепо. А в какой-нибудь объект-менеджер соединений - так это называется не разведение сущностей, а декомпозиция ;)
                Сообщение отредактировано: --Ins-- -
                  Это всё дело вкуса и каждого конкретного случая. Главное - чтоб была возможность выбора ;) В моём случае оказалось, что городить огород ради пары обработчиков, использующих только Sender - нецелесообразно, вот я и пошел другим путем.

                  Добавлено
                  Цитата --Ins-- @
                  В форму - совершенно нелепо. А в какой-нибудь объект-менеджер соединений

                  Это лепо, если сокетов много, а обработчики будут еще и на кучу других событий. Тогда уже не зазорно и класс-наследник сделать, либо действительно менеджер коннектов с внутренними методами и обработчиками. Однако в более простых случаях легче прилепить парочку автономных обработчиков и не создавать Hello world на основе паттернов
                  Сообщение отредактировано: Fr0sT -
                    Цитата Fr0sT @
                    Это всё дело вкуса и каждого конкретного случая.


                    Скорее конкретного случая, чем вкуса. Для проектика который пишется за 2-3 дня и больше к его коду ты никогда не вернешься - так может и незачем действительно "разводить огород" - тут на размышление "а как бы лучше сделать" потратишь больше времени, чем если просто взять и сделать не задумываясь как-нибудь. Но в более сложном случае - лучше потратить чуть больше времени на декомпозицию, реализовать все в коде строго в соответствии с получившейся в результате декомпозиции моделью, и потом экономить множество времени уже на стадии сопровождения и расширения кода. И вкус тут ни причем

                    Добавлено
                    PS: Кстати, лично мне более интересен случай как наоборот - там где требуется процедура/функция использовать метод. А то всякие TList.Sort в этом случае портят малину <_<
                    Сообщение отредактировано: --Ins-- -
                      Цитата --Ins-- @
                      Цитата Fr0sT @
                      --Ins--, ну, мой метод вовсе не грязен :)

                      Ну по сравнению с методом leo в целом да :)


                      А мой-то чем грязен, только тем, что непонятен "непосвященным"? ;) Грязный этот тот, который либо использует недокументированные возможности, либо может привести к непредсказуемым ошибкам из-за отключения проверки типов, передачи какого-то мусора и т.п. В данном же случае вполне "законно" вместо Self передается nil, что вполне допустимо, если этот параметр в обработчике не используется, а если используется, то получим вполне предсказуемый AccessViоlation ;)

                      Цитата Fr0sT @
                      О, вот это тоже крутой метод, не знал!

                      Для кого "крутой", а для кого "грязный" :) Была уже как-то подобная тема (может и не одна), там же (вроде как) рассматривался и вариант использования в качестве обработчика TNotifyEvent и обычной процедуры (о, боже, что на это скажет --Ins-- ;) )
                      ExpandedWrap disabled
                        type
                          TNotifyProc = procedure(ProcParam:pointer; Sender:TObject);register;
                         
                        function NotifyProcAsEvent(Proc:TNotifyProc; ProcParam:pointer = nil):TNotifyEvent;
                        begin
                          with TMethod(Result) do
                          begin
                            Code:=@Proc;
                            Data:=ProcParam;
                          end;
                        end;
                         
                        //--- example ---
                        procedure ThreadTerminate(ProcParam:pointer; Sender:TObject);
                        begin
                          ... //можно использовать ProcParam, если в него было что-то передано
                        end;
                         
                        MyThread.OnTerminate:=NotifyProcAsEvent(ThreadTerminate, nil); //можно что-то передать через ProcParam

                      Плюсы: не нужно объявлять класс (и тем самым засорять код лишней VMT ;) ), можно передать любой доп.параметр, можно использовать вложенную\локальную функцию при условии, что компилятор разрешит это сделать (D3 вообще не разрешал, D7 разрешает, если ф-я не обращается к локальным переменным и параметрам вышестоящей функции).
                        Цитата leo @
                        А мой-то чем грязен, только тем, что непонятен "непосвященным"? Грязный этот тот, который либо использует недокументированные возможности, либо может привести к непредсказуемым ошибкам из-за отключения проверки типов, передачи какого-то мусора и т.п. В данном же случае вполне "законно" вместо Self передается nil, что вполне допустимо, если этот параметр в обработчике не используется, а если используется, то получим вполне предсказуемый AccessViоlation


                        Нет, он грязен тем, что работает только благодаря некоторым особенностям "подкапотного" механизма, и приведет к багу при первом же явном или неявном (вызов виртуального/динамического метода например) обращении к Self. Нельзя вызывать методы для невалидных ссылок, даже несмотря на то, что очень хочется и кажется что можно.

                        Цитата leo @
                        о, боже, что на это скажет --Ins--


                        Инсу интереснее и кажется более оправданным случай, когда наоборот ;)

                        Добавлено
                        Цитата leo @
                        и тем самым засорять код лишней VMT


                        ОМГ, тоже мне проблема в 21-м веке :wall:
                        Сообщение отредактировано: --Ins-- -
                          Метод leo более опасен, т.к. можно ненароком забыться и обратиться к полю объекта. Для классовых же методов компилятор не даст "выстрелить себе в ногу".
                          Кроме того, обкатал этот метод в своих приложениях - выяснилась ещё одна приятная особенность: если добавить классовых переменных, можно на халяву получить синглтон :)

                          Добавлено
                          А эту мешанину простая функция/метод/ссылка заменили бы уж одними ссылками (reference). Для простеньких методов удобно, когда код внутри другой подпрограммы, а если хочется выделять, то ради бога - вызывай внутри reference хоть процедуру, хоть метод
                            :offtop:
                            Цитата --Ins-- @
                            Нет, он грязен тем, что ...

                            Не надо обобщать, т.к. в данном случае речь идет об особом случае: и способ с классовым методом и способ с nil-указателем НЕ предназначены для обращения к полям объекта и тем более к виртуальным методам. Это просто "затычка" для редких ситуаций, когда обработка производится не в методе класса, а в обычной процедуре и соот-но "некуда пристроить" оработчик. А накосячить из-за собственной забывчивости или тупости можно где-угодно (например, с теми же нетипированными var-параметрами или переполнением буфера), и приводить это может действительно к трудновыявляемым багам, а не к явному AV как в случае с nil-указателем. К тому же против забывчивости есть хорошее средство - не лениться писать комментарии, особенно при наличии каких-то ограничений в использовании.
                            PS: Разумеется я ни в коей мере не агитирую за использование подобных трюков, но думаю, что в FAQ заметка об их существовании не помешает - разумеется с предупреждением об отказе от ответственности за причиненный ущерб :)

                            Цитата --Ins-- @
                            ОМГ, тоже мне проблема в 21-м веке

                            Проблемы 21 века - это чрезмерное потребление "ресурсов", приводящее к ожирению и мировым кризисам, а также искуственно насаждаемое отупление масс, приучаемых к ЕГЭ и шаблонному мышлению :D

                            Цитата Fr0sT @
                            Для классовых же методов компилятор не даст "выстрелить себе в ногу".

                            Ага, только если мы рассуждаем о забывчивости на грани бездумности, то с таким же успехом можно забыть написать приставку class :D
                              Цитата leo @
                              Это просто "затычка" для редких ситуаций, когда обработка производится не в методе класса, а в обычной процедуре и соот-но "некуда пристроить" оработчик.


                              Частный, общий, редкий, частый - от этого грязность не зависит :) Ты сам дал выше правильное определение грязности

                              Цитата leo @
                              А накосячить из-за собственной забывчивости или тупости можно где-угодно (например, с теми же нетипированными var-параметрами или переполнением буфера), и приводить это может действительно к трудновыявляемым багам, а не к явному AV как в случае с nil-указателем.


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

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


                              У Фаулера это первое правило обнаружение говнокода - если где-то стоит/хочется написать комментарий, без которого поведение кода не очевидно. И я с этим утверждением категорически согласен :)

                              Цитата leo @
                              PS: Разумеется я ни в коей мере не агитирую за использование подобных трюков, но думаю, что в FAQ заметка об их существовании не помешает - разумеется с предупреждением об отказе от ответственности за причиненный ущерб


                              Я думаю что с примерно такой фразы лучше и начинать. А еще лучше - и заканчивать :D Хотя я раньше сам любил такие всяческие грязненькие штучки и хаки - правда скорее из спортивного интереса, нежели из практического :yes:

                              Цитата leo @
                              Проблемы 21 века - это чрезмерное потребление "ресурсов", приводящее к ожирению и мировым кризисам, а также искуственно насаждаемое отупление масс, приучаемых к ЕГЭ и шаблонному мышлению


                              Интересное замечание, но и с ним можно поспорить... Ну, скажем, пожалуй ожирение лучше чем недоедание, а экономические циклы с периодически случающимися кризисами, сопровождающиеся падением доходов населения с 2500 евро до 2000 евро - лучше чем "министерство изобилия увеличивает недельную норму потребление шоколада до 30 грамм"... Я думаю понятно к чему я клоню ;) Проблемы 21-го века они ступенькой выше проблем 20-го, т.е. подразумевают что они уже решены и теперь можно двигаться дальше. Применительно к сабжу - никакой проблемы в современных условиях наличие лишней VMT не создает. В смысле вообще никакой

                              PS: ну не такой уж и оффтоп :D
                              Сообщение отредактировано: --Ins-- -
                              0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                              0 пользователей:


                              Рейтинг@Mail.ru
                              [ Script execution time: 0,0806 ]   [ 16 queries used ]   [ Generated: 19.03.24, 03:04 GMT ]