tag:blogger.com,1999:blog-3208941668630248550.post8849016115935848795..comments2024-03-13T10:43:06.615+02:00Comments on 18-ть лет с Delphi: О fluent interface'ахAlex W. Lulinhttp://www.blogger.com/profile/08400475846894229767noreply@blogger.comBlogger11125tag:blogger.com,1999:blog-3208941668630248550.post-29579198155521328112013-09-07T01:41:42.892+03:002013-09-07T01:41:42.892+03:00Проблемы с TInterfacedObject - да.Проблемы с TInterfacedObject - да.Alex W. Lulinhttps://www.blogger.com/profile/08400475846894229767noreply@blogger.comtag:blogger.com,1999:blog-3208941668630248550.post-83202675403567957762013-09-07T01:40:54.319+03:002013-09-07T01:40:54.319+03:00А я вам скажу в чём проблема. Для глобальных перем...А я вам скажу в чём проблема. Для глобальных переменных уровня programm есть проблемы с compiler-magic и Release. Это как раз - проверено.Alex W. Lulinhttps://www.blogger.com/profile/08400475846894229767noreply@blogger.comtag:blogger.com,1999:blog-3208941668630248550.post-17624442107120936672013-09-07T00:19:43.440+03:002013-09-07T00:19:43.440+03:00А такой подход - мне нравится. Спасибо!А такой подход - мне нравится. Спасибо!Alex W. Lulinhttps://www.blogger.com/profile/08400475846894229767noreply@blogger.comtag:blogger.com,1999:blog-3208941668630248550.post-57613841712227850512013-09-06T02:59:20.403+03:002013-09-06T02:59:20.403+03:00NameRec:
Пример где это может оказаться востребов...NameRec:<br /><br />Пример где это может оказаться востребованным.<br />--<br />Ряд СУБД (Oracle, Postgres, SQLite, Red Database (клон Firebird)), поддерживают полнотекстовый поиск, но при сходных возможностях, язык запросов в них заметно отличается. <br />Предположим, мы хотим:<br />1. Обеспечить независимое от СУБД хранение полнотекстовых запросов<br />2. Предоставить пользователю возможность делать полнотекстовые запросы интерактивно и хотим его избавить от набора магических строчек вида «сверхпроводимость -"вики" -"яндекс" +"левитация" +"видео"», поскольку все эти плюсы-минусы и кавычки не интуитивны для неподготовленного пользователя.<br />Нам потребуется создать интерфейс пользователя для формирования полнотекстового запроса. Вероятно, центральным элементом интерфейса будет древовидное представление запроса, поскольку в нём возможны скобки логические условия (AND, OR...).<br /><br />Ввиду [1], вероятно, потребуется разработать свой диалект синтаксиса для полнотекстовых запросов, который будет при исполнении транслироваться в строку для полнотекстового запроса к используемой СУБД.<br />Ввиду [2] удобным способом описания языка будет набор соответствующих объектов.<br />Таким образом, для сохранения полнотекстового запроса, сформированного пользователем с использованием интерактивных средств будет сериализация построенной совокупности объектов.<br /><br />Выше была преамбула. Теперь — амбула :-)<br />Как быть, если мы хотим построить запрос полнотекстового поиска в коде? Разумеется, отвлекаясь от специфики СУБД, в контексте которой он будет исполняться.<br />Очевидно, нам следует прибегнуть к нашему диалекту языка полнотекстовых запросов. По-простому — построить совокупность объектов.<br />Как это сделать проще всего? - Что если попробовать fluent-технику?<br />{code}<br />_ // сверхпроводимость -"вики" -"яндекс" +"левитация" +"видео"<br />_ full_text_req := NewFullTextRequest()<br />_ _ .AND_Bracket([<br />_ _ _ FullText_Words(['сверхпроводимость']),<br />_ _ _ FullText_Minus(['вики', 'яндекс']),<br />_ _ _ FullText_Plus(['левитация', 'видео'])<br />_ _ ]);<br />{/code}<br />Что же здесь общего с DSL? - Кое-что есть :-)<br />Например, проверку синтаксиса нашего «диалекта» языка запросов мы возлагаем на компилятор Delphi.<br />Так, NewFullTextRequest возвращает интерфейс IFullTextRequest, содержащий методы AND_Bracket и OR_Bracket – скобки для условий поиска, элементы которых объединены условиями, соответственно, AND или OR. <br />Подобно этому, AND/OR_Bracket принимают в качестве параметра массив элементов типа IFullTextCondition, которые умеют создавать функции FullText_Words/Minus/Plus, которые, в свою очередь, принимают на вход массивы строк, посредством которых описываются условия. <br />Далее, если потребуется расширить наш язык новыми понятиями, мы можем следовать парадигме: определить место в существующей структуре, в которой востребованы новые понятия и поддержать соответствующие fluent-методы.<br />Например: новый вид условий XOR можно поддержать «рядом» с AND/OR_Bracket.<br /><br />Отвечу на один вопрос из множества, которые могут появиться.<br />[Q] Зачем «городить огород» с описанием диалекта посредством объектов? Почему не парсить строчку, оговорив предварительно её формат? Т.е. сформулировав диалект.<br />[A] Например, по следующим причинам:<br />1. При изменениях в языке, запросы, описанные в коде в виде строчек приведут к ошибкам при выполнении, а записанные во fluent-технике к ошибкам компиляции.<br />2. Работать с объектами проще, чем со строками, поскольку пропадает потребность в разборе строк. А обрабатывать запрос, сформулированный в нашем диалекте придётся перед его выполнением средствами используемой СУБД.<br />3. Объекты, вероятно, всё равно потребуются ввиду [2] технических требований. Поэтому, строки можно рассматривать как формат сериализации этих объектов, но если из технических соображений сделать объекты потомками TComponent, «из коробки» станет доступна и сериализация.<br /><br />Ладно, здесь я, пожалуй, остановлюсь. Поскольку у меня нет уверенности в том, что развитие этой темы здесь кому-либо может оказаться интересным...Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-3208941668630248550.post-39120239221663796992013-09-06T02:58:53.194+03:002013-09-06T02:58:53.194+03:00NameRec:
«Но мне лично - СИЛЬНО интереснее - UML,...NameRec:<br /><br />«Но мне лично - СИЛЬНО интереснее - UML, DSL и TDD. Почему-то... <br />Ну или "хардкорные" обёртки над локальными функциями... <br />А "синтаксический сахар" - далеко не всегда интересен....»<br />-- Александр, а Вы не находите, что fluent-техника, в сущности, DSL и есть? Она выглядит как DSL и правила работы с ней напоминают правила работы с DSL.<br />Только DSL в контексте существующего языка.<br />Вероятно, это не просто увидеть, поскольку обычно DSL *реализуется* средствами другого языка.<br /><br />fluent-техника - не синтаксический сахар в классическом понимании (http://ru.wikipedia.org/wiki/Синтаксический_сахар), поскольку не требует расширения синтаксиса языка программирования, в чём я вижу достоинство.<br /><br />Fluent-техника действительно очень похожа на DSL как по реализации, так и по применению.<br />Поверхностно проверить моё утверждение можно относительно просто.<br />Выше я говорил, что вижу ограниченную область применения этой техники в Delphi и обозначил описание SQL - как пример задачи, где fluent-техника может принести пользу.<br />Учитывая, что SQL, в сущности, является DSL, можно посмотреть, получится ли использовать fluent-технику для описания объектами других известных DSL. Таких как XML, XPath, regexp, JSON и, не поверите, даже UML.<br />Понятное дело, что генерация UML-диаграммы в программном коде выглядит, по меньшей мере, странно. Но технически это возможно.<br />Для описания же XML, XPath, regexp и т.п. вполне можно найти достойные применения.<br />Что делает это возможным? Наличие у языка описания, что ограничивает возможное число вариантов в контексте построения fluent-цепочки.<br /><br />Разумеется, никого не призываю "выкидывать свои DSL" и "прикручивать" вместо них текучие интерфейсы.<br />Просто предлагаю под другим углом посмотреть на технику, которая (есть у меня такое ощущение) может оказаться полезной там, где требуется объектное описание *существующего* DSL.Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-3208941668630248550.post-45993472858216335022013-09-06T02:54:48.572+03:002013-09-06T02:54:48.572+03:00NameRec:
«Я вот "по-стариковски" даже Н...NameRec:<br /><br />«Я вот "по-стариковски" даже НЕ ДУМАЛ, что это такая ЖИВОТРЕПЕЩУЩАЯ тема.»<br />-- В комментариях - обсуждение возможной ошибки Delphi при работе с интерфейсами в контексте потомков TInterfacedObject.<br />IMHO - тема, как минимум, заслуживающая внимания.Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-3208941668630248550.post-66390775228610876552013-09-06T00:30:07.391+03:002013-09-06T00:30:07.391+03:00Я вот "по-стариковски" даже НЕ ДУМАЛ, чт...Я вот "по-стариковски" даже НЕ ДУМАЛ, что это такая ЖИВОТРЕПЕЩУЩАЯ тема.<br /><br />Я просто "по ходу жизни" откомментировал.<br /><br />А тут ТАКАЯ жизнь!<br /><br />Но мне лично - СИЛЬНО интереснее - UML, DSL и TDD. Почему-то...<br /><br />Ну или "хардкорные" обёртки над локальными функциями...<br /><br />А "синтаксический сахар" - далеко не всегда интересен....Alex W. Lulinhttps://www.blogger.com/profile/08400475846894229767noreply@blogger.comtag:blogger.com,1999:blog-3208941668630248550.post-92053946775758126442013-09-05T22:40:12.938+03:002013-09-05T22:40:12.938+03:00Delphi 7, XE3, XE4Delphi 7, XE3, XE4Alex W. Lulinhttps://www.blogger.com/profile/08400475846894229767noreply@blogger.comtag:blogger.com,1999:blog-3208941668630248550.post-42475777903369873512013-09-05T22:36:33.906+03:002013-09-05T22:36:33.906+03:00У меня - нет проблем с _AddRef/_Release. У меня ес...У меня - нет проблем с _AddRef/_Release. У меня есть тесты для DUnit.<br /><br />Но я НЕ ИСПОЛЬЗУЮ TInterfacedObject. Я уже писал об этом. Может быть проблема в этом.<br /><br />Хотя - вряд ли.Alex W. Lulinhttps://www.blogger.com/profile/08400475846894229767noreply@blogger.comtag:blogger.com,1999:blog-3208941668630248550.post-48504782943333394342013-09-05T19:09:55.196+03:002013-09-05T19:09:55.196+03:00NameRec:
«Использование интерфейсов в подобных вы...NameRec:<br /><br />«Использование интерфейсов в подобных выражениях ведет к утечке памяти в Delphi XE2 и ранее, в более поздних не смотрел.»<br />-- Не подтверждаю.<br />Попробуйте запустить такой вариант:<br />{code}<br />{$apptype console}<br /><br />program fit;<br />uses<br /> SysUtils;<br /><br />type<br /> ITFI = interface(IUnknown)<br /> function fi: ITFI;<br /> procedure Test(Prompt: String='');<br /> end;<br /><br /> TTFI = class(TInterfacedObject, ITFI)<br /> public<br /> destructor Destroy; override;<br /> function fi: ITFI;<br /> procedure Test(Prompt: String='');<br /> end;<br /><br />{ TTFI }<br /><br />destructor TTFI.Destroy;<br />begin<br /> Test('Destroy');<br /> inherited;<br />end;<br /> <br />function TTFI.fi: ITFI;<br />begin<br /> Result := Self;<br /> Test('fi');<br />end;<br /><br />procedure TTFI.Test(Prompt: String='');<br />begin<br /> if Prompt <> '' then<br /> Prompt := '[' + Prompt + '] ';<br /> WriteLn(Prompt, Self.RefCount);<br />end;<br /><br />procedure Execute;<br />var<br /> f, f1: ITFI;<br />begin<br /> WriteLn('begin Execute');<br /> f := ttfi.create;<br /> f.Test('after ttfi.create');<br /> f := f.fi;<br /> f.Test('after f.fi');<br /> f := f.fi.fi.fi.fi;<br /> f.Test('after f := f.fi.fi.fi.fi');<br /> Move(f, f1, SizeOf(f));<br /> f := nil;<br /> f1.Test('after f := nil');<br /> WriteLn('end Execute');<br />end;<br /><br />begin<br /> WriteLn('begin program');<br /> try<br /> WriteLn('before call Execute');<br /> Execute;<br /> WriteLn('after call Execute');<br /> except<br /> on E: Exception do<br /> Writeln(E.ClassName, ': ', E.Message);<br /> end;<br /> WriteLn('end program');<br />end.<br />{/code}<br />По меньшей мере, в Delphi 2007 память освобождается корректно.<br />Вызов TTFI.Destroy происходит после завершения процедуры Execute.<br />Кстати, как Вы определили, что утечка памяти имеет место?Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-3208941668630248550.post-45704780505059016042013-09-05T09:58:00.716+03:002013-09-05T09:58:00.716+03:00Использование интерфейсов в подобных выражениях ве...Использование интерфейсов в подобных выражениях ведет к утечке памяти в Delphi XE2 и ранее, в более поздних не смотрел.<br />К утечке приводит тот факт, что при вызове функций цепочкой не вызывается _release. <br />_AddRef вызывается в момент присвоения значения псевдопеременной Result, а _Release - в момент присвоения значения той переменной, в которую мы положим результат вызова функции. Если же этот результат мы не присваиваем переменной, то _Кудуфыу не будет вызван вовсе.<br /><br />Полный код примера.<br />program fit;<br />//сделана для ответа на этот пост<br />//http://18delphi.blogspot.ru/2013/09/fluent-interface.html<br />{$APPTYPE CONSOLE}<br />{$R *.res}<br />uses<br /> System.SysUtils;<br />type<br /> ITFI=interface(IUnknown)<br /> function fi:ITFI;<br /> end;<br /> TTFI=class(TInterfacedObject,ITFI)<br /> function fi:ITFI;<br /> end;<br />{ TTFI }<br />function TTFI.fi: ITFI;<br />begin<br /> Writeln('Before: ',self.FRefCount);<br /> Result := Self;<br /> Writeln('After: ',self.FRefCount);<br />end;<br />var<br /> f:ITFI;<br />begin<br /> try<br /> f:=ttfi.create;<br /> //f.FRefCount=1<br /> f:=f.fi;<br /> //f.FRefCount=2<br /> f:=f.fi.fi.fi.fi;<br /> //f.FRefCount=6<br /> f:=nil;<br /> //единственный вызов f._release Интересно, что же в счетчике ссылок?<br /> readln;//чтоб с экрана вывод не пропадал<br /> except<br /> on E: Exception do<br /> Writeln(E.ClassName, ': ', E.Message);<br /> end;<br />end.Anonymousnoreply@blogger.com