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

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

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

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

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


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

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

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

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

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

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

Ответ 1

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

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

Отладка:

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

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

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

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

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

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

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

Ответ 2

Все просто пишут о негативных проблемах, поэтому давайте подчеркнем некоторые положительные моменты. 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));

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

Ответ 3

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.

Ответ 4

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

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

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

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

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

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

Ответ 5

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