Свободный интерфейс в Delphi

31

Каковы плюсы и минусы использования свободного интерфейса в Delphi?

Свободные интерфейсы, как предполагается, повышают читаемость, но я немного скептически отношусь к одному длинному LOC, который содержит много прикованных методов.

Есть ли проблемы с компилятором? Существуют ли какие-либо проблемы отладки?
Есть ли проблемы с обработкой/ошибкой?

Свободные интерфейсы используются, например, TStringBuilder, THTMLWriter и TGpFluentXMLBuilder.


Обновлено:
Дэвид Хеффернан задал вопросы, о которых я беспокоился. Мне была дана эта мысль, и общая проблема заключается в различии между "явно указывая, как это делается", и "позволяя компилятору решить, как это сделать".

AFAICS, нет документации о том, как обработчики цепочки фактически обрабатываются компилятором, ни какая-либо спецификация того, как компилятор должен обрабатывать цепные методы.

В в этой статье мы можем прочитать о том, как компилятор добавляет два дополнительных параметра var к параметрам, объявленным как функции, и что стандартный вызов соглашение помещает три параметра в регистр и следующие в стек. "Свободный функциональный метод" с 2 параметрами будет использовать стек, тогда как "обычный метод процедуры" с 2 параметрами использует регистр.

Мы также знаем, что компилятор делает некоторую магию для оптимизации двоичного файла (например, строка как результат функции, порядок оценки, ref для локального proc), но иногда с удивительными побочными эффектами для программиста.

Так что тот факт, что управление памятью/стеком/регистром более сложный, и тот факт, что компилятор может сделать какую-то магию с непреднамеренными побочными эффектами, довольно вонючий для меня. Отсюда вопрос.

После того, как я прочитал ответы (очень хорошие), моя забота сильно сократилась, но мои предпочтения все те же:)

  • 2
    Что я сделал из изучения примера Delphi в этой статье - это желание прочитать книгу под названием «Хватит думать на Java» :-)
  • 2
    Некоторым людям это нравится. Некоторые люди этого не делают. Как вы думаете?
Показать ещё 9 комментариев
Теги:
fluent-interface

5 ответов

19
Лучший ответ

Проблемы с компилятором:

Если вы используете интерфейсы (а не объекты), каждый вызов в цепочке приведет к избыточным ресурсам ссылок, даже если тот же самый интерфейс возвращается все время, компилятор не знает об этом. Таким образом, вы создадите более крупный код с более сложным стеком.

Отладка:

Цепочка вызова рассматривается как одна команда, вы не можете наступать или останавливаться на промежуточных шагах. Вы также не можете оценивать состояние на промежуточных этапах. Единственным способом отладки промежуточных шагов является отслеживание в представлении asm. Стол вызовов в отладчике также будет неясным, если одни и те же методы выполняются несколько раз в свободной цепочке.

Проблемы времени выполнения:

При использовании интерфейсов для цепочки (а не объектов) вам приходится оплачивать служебные данные подсчета ссылок, а также более сложный фрейм исключений. Вы не можете попробовать... окончательно конструируете в цепочке, поэтому не гарантируете закрытия того, что было открыто в свободной цепочке f.i.

Проблемы с отладки/ошибки:

Исключения и их трассировка стека будут видеть цепочку как одну команду, поэтому, если вы потерпели крах в .DoSomething, а в цепочке вызовов есть несколько вызовов .DoSomething, вы не будете знать, что вызвало проблему.

Проблемы с форматированием кода:

AFAICT ни один из существующих форматировщиков кода не будет правильно размещать свободную цепочку вызовов, поэтому это может быть только ручное форматирование, которое может поддерживать свободную цепочку вызовов. Если запускается автоматический форматировщик, он обычно превращает цепочку в беспорядок чтения.

  • 2
    Вы можете сделать шаг за шагом, если вы - в конце КАЖДОЙ процедуры - выходите с помощью F11. Это очень много времени, поэтому не рекомендуется.
23

Все просто пишут о негативных проблемах, поэтому давайте подчеркнем некоторые положительные моменты. Errr, единственная положительная проблема - меньше (в некоторых случаях намного меньше).

Я написал GpFluentXMLBuilder только потому, что мне не нравится печатать тонны кода при создании XML-документов. Ничего больше и не меньше.

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

EDIT: точка для представления "краткость и удобочитаемость".

Я отлаживал старый код и наткнулся на это:

fdsUnreportedMessages.Add(CreateFluentXml
  .UTF8
  .AddChild('LogEntry')
    .AddChild('Time', Now)
    .AddSibling('Severity', msg.MsgID)
    .AddSibling('Message', msg.MsgData.AsString)
  .AsString);

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

var
  xmlData: IXMLNode;
  xmlDoc : IXMLDocument;
  xmlKey : IXMLNode;
  xmlRoot: IXMLNode;

  xmlDoc := CreateXMLDoc;
  xmlDoc.AppendChild(xmlDoc.CreateProcessingInstruction('xml', 
    'version="1.0" encoding="UTF-8"'));
  xmlRoot := xmlDoc.CreateElement('LogEntry');
  xmlDoc.AppendChild(xmlRoot);
  xmlKey := xmlDoc.CreateElement('Time');
  xmlDoc.AppendChild(xmlKey);
  xmlData := xmlDoc.CreateTextNode(FormatDateTime(
    'yyyy-mm-dd"T"hh":"mm":"ss.zzz', Now));
  xmlKey.AppendChild(xmlData);
  xmlKey := xmlDoc.CreateElement('Severity');
  xmlDoc.AppendChild(xmlKey);
  xmlData := xmlDoc.CreateTextNode(IntToStr(msg.MsgID));
  xmlKey.AppendChild(xmlData);
  xmlKey := xmlDoc.CreateElement('Message');
  xmlDoc.AppendChild(xmlKey);
  xmlData := xmlDoc.CreateTextNode(msg.MsgData.AsString);
  xmlKey.AppendChild(xmlData);
  fdsUnreportedMessages.Add(xmlKey.XML);

Мне понадобится довольно много времени (и чашка кофе), чтобы понять, что он делает.

EDIT2:

Эрик Грейндж сделал замечательную точку зрения в комментариях. В действительности, можно использовать некоторую оболочку XML, а не DOM напрямую. Например, используя OmniXMLUtils из пакета OmniXML, код будет выглядеть следующим образом:

var
  xmlDoc: IXMLDocument;
  xmlLog: IXMLNode;

  xmlDoc := CreateXMLDoc;
  xmlDoc.AppendChild(xmlDoc.CreateProcessingInstruction(
    'xml', 'version="1.0" encoding="UTF-8"'));
  xmlLog := EnsureNode(xmlDoc, 'LogEntry');
  SetNodeTextDateTime(xmlLog, 'Time', Now);
  SetNodeTextInt(xmlLog, 'Severity', msg.MsgID);
  SetNodeText(xmlLog, 'Message', msg.MsgData.AsString);
  fdsUnreportedMessages.Add(XMLSaveToString(xmlDoc));

Тем не менее, я предпочитаю свободную версию. [И я никогда не использую форматировщики кода.]

  • 0
    +1: не могу согласиться больше!
  • 2
    @gabr, не голосуйте за ваш ответ, потому что технически он правильный (намного меньше печатать). Тем не менее, вы можете получить гораздо меньше печатать, используя плохо (с MyQuery, MyButton, MyTreeView, MyOtherThing do ...). Это в основном делает код чрезвычайно сложным для отладки и поддержки, даже если вы сохраняете много нажатий клавиш. Я думаю, что беглые интерфейсы могут закончиться с теми же проблемами.
Показать ещё 11 комментариев
7

Are there any compiler issues?

Нет.

Are there any debugging issues?

Да.. Поскольку все вызовы с цепными вызовами рассматриваются как одно выражение, даже если вы пишете их на нескольких строках, как в приведенном вами примере Википедии, это проблема при отладке, поскольку вы не можете один шаг через них.

Are there any runtime/error handling issues?

Отредактировано: Здесь приложение тестовой консоли, которое я написал, чтобы проверить фактические накладные расходы Runtime на использование Свободных интерфейсов. я назначено 6 свойств для каждой итерации (фактически одинаковые 2 значения 3 раза каждый). Выводы заключаются в следующем:

  • С интерфейсами: увеличение времени выполнения на 70% зависит от количества установленных свойств. Установив только два свойства, накладные расходы были меньше.
  • С объектами: Использование плавных интерфейсов было быстрее
  • Не проверял записи. Он не может хорошо работать с записями!

Я лично не против этих "плавных интерфейсов". Никогда не слышал об имени раньше, но я использовал их, особенно в коде, который заполняет список из кода. (В некоторой степени похожий пример XML, который вы опубликовали). Я не думаю, что их трудно читать, особенно если кто-то знаком с таким типом кодирования, и имена методов имеют смысл. Что касается одной длинной строки кода, посмотрите на пример в Википедии: вам не нужно помещать все это в одну строку кода.

Я четко помню, как их использовать с Turbo Pascal для инициализации экранов, что, вероятно, поэтому я не возражаю против них, а также иногда использую их. К сожалению, Google не справляется со мной, я не могу найти код для старого кода TP.

  • 0
    +1 интересно прочитать, что они даже быстрее (при реализации с объектами), чем классический построчный код, tx для запуска тестов
3

Я бы поставил под вопрос преимущества использования "плавных интерфейсов".

Из того, что я вижу, нужно позволить вам избежать объявления переменной. Таким образом, та же самая польза от страшного предложения With приносит другой набор проблем (см. Другие ответы)

Честно говоря, я никогда не понимал мотивацию использовать выражение С, и я не понимаю мотивации использовать и свободные интерфейсы. Я имею в виду, трудно определить переменную? Все это mumbo jumbo просто, чтобы позволить лень.

Я бы сказал, что вместо повышения удобочитаемости это, на первый взгляд, кажется, что нужно печатать/читать меньше, это фактически запутывает его.

Итак, еще раз, я спрашиваю, почему вы хотите использовать бесплатные интерфейсы в первую очередь?

Он был придуман Мартином Фаулером, так что это должно быть круто? Нет, я не покупаю его.

  • 6
    «Все это фетиш, просто чтобы позволить лень». - Просто хочу отметить, что то же самое было сказано о языке ассемблера, 3GL и IDE, дизайнерах форм, генеративном программировании, сборке мусора, инструментах автоматического рефакторинга и ...
2

Это своего рода запись с однократной записью-записью, которую нелегко понять без прохождения документации по всем задействованным методам. Также такая нотация несовместима с свойствами Delphi и С# - если вам нужно установить свойства, вам нужно откат к использованию общие обозначения, поскольку вы не можете связать присвоения свойств.

  • 0
    This is a kind of write-once-read-never notation that is not easy to understand without going through documentation for all involved methods. Это преувеличение. Это вполне читабельно при использовании в правильных ситуациях.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню