Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.116.24.238] |
|
Сообщ.
#1
,
|
|
|
Все обработчики событий OnXXX имеют тип метода объекта (procedure / function of object). Однако не всегда есть объект, куда можно запихнуть эти обработчики. Что же делать? Заводить объект (и значит, писать Create и Free) только из-за этого?
Выход 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 в следующем посте. |
Сообщ.
#2
,
|
|
|
Цитата Fr0sT @ Выход появился с введением в D2009 методов класса. ?! Во-первых, методы класса существовали с незапамятных времен. Во-вторых, если в методе не используется Self, то создавать реальный объект вызовом Create совершенно не обязательно - можно юзать nil или через реальную переменную или просто приведением типа: MyThread.OnTerminate:=TEventHandler(nil).ThreadTerminate; |
Сообщ.
#3
,
|
|
|
Цитата leo @ Во-первых, методы класса существовали с незапамятных времен Хм, да, действительно, даже в D7 есть. Значит, это я только в 2009-ых до них добрался)) Цитата leo @ MyThread.OnTerminate:=TEventHandler(nil).ThreadTerminate; О, вот это тоже крутой метод, не знал! |
Сообщ.
#4
,
|
|
|
Цитата Fr0sT @ Хм, да, действительно, даже в D7 есть Даже в D3 есть , ведь у TObject практически все методы - классовые |
Сообщ.
#5
,
|
|
|
Лучше все-таки завести объект и назначить обработчиком обычный instance-метод, чем связываться с грязными приемчиками. Управление временем жизни такого объекта не такая уж и проблема, тем более что это управление может быть не ручным, а автоматическим. Да и вообще, я считаю, что если в некой системе существует объект, генерирующий события, то странно что в этой системе нет места объекту, который по логике должен их обрабатывать - это уже не ООП, а какой-то гибрид.
|
Сообщ.
#6
,
|
|
|
--Ins--, ну, мой метод вовсе не грязен Кто ж виноват, что нет возможности просто и без запарок назначить обработчик, не засовывая его в класс.
Цитата --Ins-- @ если в некой системе существует объект, генерирующий события, то странно что в этой системе нет места объекту, который по логике должен их обрабатывать - это уже не ООП, а какой-то гибрид Во-первых, ну и что? Гибрид так гибрид. Глобальные переменные и самостоятельные функции тоже уже не ООП, и кого это волнует. Во-вторых, объекта может не быть чисто по структурным соображениям: к примеру, если есть сокет, у которого по OnConnect посылается запрос на сервер - то засовывать обработчик OnConnect в форму совершенно нелепо. Создавать экземпляр класса, который служит исключительно хранилищем обработчиков? Зачем плодить сущности, если можно обойтись без них. |
Сообщ.
#7
,
|
|
|
Цитата Fr0sT @ --Ins--, ну, мой метод вовсе не грязен Ну по сравнению с методом leo в целом да Цитата Fr0sT @ Во-первых, ну и что? Гибрид так гибрид. Глобальные переменные и самостоятельные функции тоже уже не ООП, и кого это волнует. Для функции расчета синуса/косинуса делать ее методом какого-либо класса действительно смысла нет. Но в случае отправитель-получатель (события) - так как-то сам собой объект напрашивается. Я имею в виду не в коде объект, а в абстрактной модели, описывающей поведение твоей системы. И вовсе необязательно это должна быть форма, а скорее даже наоборот - если источник события не ее собственные компоненты - то это скорее всего моветон. Для чего делать именно так (отвечая на вопрос "ну и что?") - для проведения декомпозиции, выработки хорошего стиля, грамотного подхода - всего того, что позволит максимально гибко решать в том числе и более сложные задачи проектирования, расширяемые и защищенные от багов и простые и понятные при описании принципов, в этих решениях реализованных. Цитата Fr0sT @ Во-вторых, объекта может не быть чисто по структурным соображениям: к примеру, если есть сокет, у которого по OnConnect посылается запрос на сервер - то засовывать обработчик OnConnect в форму совершенно нелепо. В форму - совершенно нелепо. А в какой-нибудь объект-менеджер соединений - так это называется не разведение сущностей, а декомпозиция |
Сообщ.
#8
,
|
|
|
Это всё дело вкуса и каждого конкретного случая. Главное - чтоб была возможность выбора В моём случае оказалось, что городить огород ради пары обработчиков, использующих только Sender - нецелесообразно, вот я и пошел другим путем.
Добавлено Цитата --Ins-- @ В форму - совершенно нелепо. А в какой-нибудь объект-менеджер соединений Это лепо, если сокетов много, а обработчики будут еще и на кучу других событий. Тогда уже не зазорно и класс-наследник сделать, либо действительно менеджер коннектов с внутренними методами и обработчиками. Однако в более простых случаях легче прилепить парочку автономных обработчиков и не создавать Hello world на основе паттернов |
Сообщ.
#9
,
|
|
|
Цитата Fr0sT @ Это всё дело вкуса и каждого конкретного случая. Скорее конкретного случая, чем вкуса. Для проектика который пишется за 2-3 дня и больше к его коду ты никогда не вернешься - так может и незачем действительно "разводить огород" - тут на размышление "а как бы лучше сделать" потратишь больше времени, чем если просто взять и сделать не задумываясь как-нибудь. Но в более сложном случае - лучше потратить чуть больше времени на декомпозицию, реализовать все в коде строго в соответствии с получившейся в результате декомпозиции моделью, и потом экономить множество времени уже на стадии сопровождения и расширения кода. И вкус тут ни причем Добавлено PS: Кстати, лично мне более интересен случай как наоборот - там где требуется процедура/функция использовать метод. А то всякие TList.Sort в этом случае портят малину |
Сообщ.
#10
,
|
|
|
Цитата --Ins-- @ Цитата Fr0sT @ --Ins--, ну, мой метод вовсе не грязен Ну по сравнению с методом leo в целом да А мой-то чем грязен, только тем, что непонятен "непосвященным"? Грязный этот тот, который либо использует недокументированные возможности, либо может привести к непредсказуемым ошибкам из-за отключения проверки типов, передачи какого-то мусора и т.п. В данном же случае вполне "законно" вместо Self передается nil, что вполне допустимо, если этот параметр в обработчике не используется, а если используется, то получим вполне предсказуемый AccessViоlation Цитата Fr0sT @ О, вот это тоже крутой метод, не знал! Для кого "крутой", а для кого "грязный" Была уже как-то подобная тема (может и не одна), там же (вроде как) рассматривался и вариант использования в качестве обработчика TNotifyEvent и обычной процедуры (о, боже, что на это скажет --Ins-- ) 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 разрешает, если ф-я не обращается к локальным переменным и параметрам вышестоящей функции). |
Сообщ.
#11
,
|
|
|
Цитата leo @ А мой-то чем грязен, только тем, что непонятен "непосвященным"? Грязный этот тот, который либо использует недокументированные возможности, либо может привести к непредсказуемым ошибкам из-за отключения проверки типов, передачи какого-то мусора и т.п. В данном же случае вполне "законно" вместо Self передается nil, что вполне допустимо, если этот параметр в обработчике не используется, а если используется, то получим вполне предсказуемый AccessViоlation Нет, он грязен тем, что работает только благодаря некоторым особенностям "подкапотного" механизма, и приведет к багу при первом же явном или неявном (вызов виртуального/динамического метода например) обращении к Self. Нельзя вызывать методы для невалидных ссылок, даже несмотря на то, что очень хочется и кажется что можно. Цитата leo @ о, боже, что на это скажет --Ins-- Инсу интереснее и кажется более оправданным случай, когда наоборот Добавлено Цитата leo @ и тем самым засорять код лишней VMT ОМГ, тоже мне проблема в 21-м веке |
Сообщ.
#12
,
|
|
|
Метод leo более опасен, т.к. можно ненароком забыться и обратиться к полю объекта. Для классовых же методов компилятор не даст "выстрелить себе в ногу".
Кроме того, обкатал этот метод в своих приложениях - выяснилась ещё одна приятная особенность: если добавить классовых переменных, можно на халяву получить синглтон Добавлено А эту мешанину простая функция/метод/ссылка заменили бы уж одними ссылками (reference). Для простеньких методов удобно, когда код внутри другой подпрограммы, а если хочется выделять, то ради бога - вызывай внутри reference хоть процедуру, хоть метод |
Сообщ.
#13
,
|
|
|
Цитата --Ins-- @ Нет, он грязен тем, что ... Не надо обобщать, т.к. в данном случае речь идет об особом случае: и способ с классовым методом и способ с nil-указателем НЕ предназначены для обращения к полям объекта и тем более к виртуальным методам. Это просто "затычка" для редких ситуаций, когда обработка производится не в методе класса, а в обычной процедуре и соот-но "некуда пристроить" оработчик. А накосячить из-за собственной забывчивости или тупости можно где-угодно (например, с теми же нетипированными var-параметрами или переполнением буфера), и приводить это может действительно к трудновыявляемым багам, а не к явному AV как в случае с nil-указателем. К тому же против забывчивости есть хорошее средство - не лениться писать комментарии, особенно при наличии каких-то ограничений в использовании. PS: Разумеется я ни в коей мере не агитирую за использование подобных трюков, но думаю, что в FAQ заметка об их существовании не помешает - разумеется с предупреждением об отказе от ответственности за причиненный ущерб Цитата --Ins-- @ ОМГ, тоже мне проблема в 21-м веке Проблемы 21 века - это чрезмерное потребление "ресурсов", приводящее к ожирению и мировым кризисам, а также искуственно насаждаемое отупление масс, приучаемых к ЕГЭ и шаблонному мышлению Цитата Fr0sT @ Для классовых же методов компилятор не даст "выстрелить себе в ногу". Ага, только если мы рассуждаем о забывчивости на грани бездумности, то с таким же успехом можно забыть написать приставку class |
Сообщ.
#14
,
|
|
|
Цитата leo @ Это просто "затычка" для редких ситуаций, когда обработка производится не в методе класса, а в обычной процедуре и соот-но "некуда пристроить" оработчик. Частный, общий, редкий, частый - от этого грязность не зависит Ты сам дал выше правильное определение грязности Цитата leo @ А накосячить из-за собственной забывчивости или тупости можно где-угодно (например, с теми же нетипированными var-параметрами или переполнением буфера), и приводить это может действительно к трудновыявляемым багам, а не к явному AV как в случае с nil-указателем. Если я накосячу из-за того, что попытаюсь сделать что-то нелегальное сам - вина будет моя. Если же я, выполнив совершенно легальную и безобидную операцию (обращение к Self внутри метода), получу косяк - тут извините прокляну того, кто мне такую свинью подложил - и буду прав. Цитата leo @ К тому же против забывчивости есть хорошее средство - не лениться писать комментарии, особенно при наличии каких-то ограничений в использовании. У Фаулера это первое правило обнаружение говнокода - если где-то стоит/хочется написать комментарий, без которого поведение кода не очевидно. И я с этим утверждением категорически согласен Цитата leo @ PS: Разумеется я ни в коей мере не агитирую за использование подобных трюков, но думаю, что в FAQ заметка об их существовании не помешает - разумеется с предупреждением об отказе от ответственности за причиненный ущерб Я думаю что с примерно такой фразы лучше и начинать. А еще лучше - и заканчивать Хотя я раньше сам любил такие всяческие грязненькие штучки и хаки - правда скорее из спортивного интереса, нежели из практического Цитата leo @ Проблемы 21 века - это чрезмерное потребление "ресурсов", приводящее к ожирению и мировым кризисам, а также искуственно насаждаемое отупление масс, приучаемых к ЕГЭ и шаблонному мышлению Интересное замечание, но и с ним можно поспорить... Ну, скажем, пожалуй ожирение лучше чем недоедание, а экономические циклы с периодически случающимися кризисами, сопровождающиеся падением доходов населения с 2500 евро до 2000 евро - лучше чем "министерство изобилия увеличивает недельную норму потребление шоколада до 30 грамм"... Я думаю понятно к чему я клоню Проблемы 21-го века они ступенькой выше проблем 20-го, т.е. подразумевают что они уже решены и теперь можно двигаться дальше. Применительно к сабжу - никакой проблемы в современных условиях наличие лишней VMT не создает. В смысле вообще никакой PS: ну не такой уж и оффтоп |