Fluent interface и Delphi

Текучий интерфейс (Fluent interface) — совсем молодая методика, даже скорее паттерн проектирования, получивший популярность и широкое распространение среди кодеров Java, C#, PHP.
В большинстве методик «хорошего кода» лежит разряжение текста кода (висячие строки, пробелы, отступы, etc) и это очень здорово, но иногда это превращается в сущий ад. Пробежка скролом по коду, запоминание цепочки вызова метода, вечная дилемма между длинным методом и читабельностью, etc.
Но решение есть — Текучий интерфейс! Теперь и на Delphi!

Так что же такое текучий интерфейс?

Если вкратце — это упрощение множественных вызовов методов одного объекта, с помощью цепочки методов возвращающих вызвавший его объект (т.е. самого себя). Звучит сложно, а выглядит очень легко и просто:
CreateFluentBinaryWriter(Stream)
  .WriteString('active')
  .WriteByte(130);

Реализация

Интерфейс выглядит элементарно — так:
IFluentBinaryWriter = interface
  function WriteByte(Value: Byte): IFluentBinaryWriter; 
  function WriteString(Value: string): IFluentBinaryWriter; 
end;

Вспомогательная функция создания класса, реализующего интерфейс, выглядит очень просто — так:
function CreateFluentBinaryWriter(AStream: TStream): IFluentBinaryWriter;
begin
  Result := TFluentBinaryWriter.Create(AStream);
end;

Далее все функции текучего интерфейса должны возвращать экземпляр класса вызвавшего эту функцию.

Как например функция WriteString записывает строку в поток (TBytesStream), и возвращает экземпляр класса вызвавшего эту функцию
function TFluentBinaryWriter.WriteString(Value: string): IFluentBinaryWriter;
begin 
  fBinaryWriter.Write(Value); // TBinaryWriter
  Result := Self; 
end;

Вот и всё! Собственно на этом можно было закончить, но есть одна очень интересная реализация!

Вариант два — посложнее

CreateFluentStringWriter(Stream)
  ['Привет ']
  ['Мир']
  ['!'];

Здесь мы пойдем на небольшую хитрость. Объявим свойство Attrib
property Attrib[Value: string]: IFluentStringWriter read AddString; default;

и в функции AddString мы запишем запрашиваемую строку в поток и вернём в качестве результата экземпляр класса вызвавшего эту функцию
function TFluentValueWriter.AddString(Value: string): IFluentValueWriter;
begin
  fBinaryWriter.Write(Value); // TBinaryWriter
  Result := Self;
end;

PS

Использование текучего интерфейса весьма широко, и вкупе с анонимными процедурами можно достичь очень компактного и самодокументируемого кода!

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 17

    –1
    Чем не устраивает with?
      –3
      with — дурной тон, две лишних строки, хрупкость записи (удаление метода из класса под with и его присутствие в методе), а из своего опыта отмечу with значительно затрудняет отладку
        +2
        А с таким вариантом построковая отладка вообще не пройдет. Так как такую запись дебагер будет расценивать как одну строку. Плюс все те же недостатки как и у with
          0
          Если быть честным и то и то для дебага зло. Но для данного метода есть выход — добавление метода Breakpoint и внутри int 3. T.e. получится где то так
          CreateIntf
            .Add(1)
            .Breakpoint()
            .Add(2);
          

          Или же добавление промежуточных результатов в лог
        +1
        Только не текучий, а «беглый», «лёгкий», «поверхностный», «не отяжелённый»
          0
          ru.wikipedia.org/wiki/Fluent_interface (Текучий интерфейс), плюс пару статей на хабре с тем же переводом.
            +3
            А .net reflection переводят как «отражение» (хотя его смысл — рефлексия), и чего теперь?

            В Вики написано, говорите? Сейчас поправим

            Кстати, обратите внимание на секцию «Огрехи перевода»: ru.wikipedia.org/wiki/Обсуждение:Fluent_interface
              0
              На хабре тож поправите? А итератор? Всегда итератор иногда перечислитель, у программистов всё не как у людей )
              С огрехами согласен, но вариантов нет, поймите )
                +2
                Скажите, вы правда не чувствуете семантической разницы между («fluent» — «текучий») и («итератор» — «перечислитель»), раз привели вторую пару в качестве примера?
                Вам знакомо понятие омонимов?
                  0
                  Или скорее даже «летающий» (по аналогии с русским понятием об интерфейсе, когда «всё летает», и по аналогии с английским «fluent english» — «связный, легко употребляемый, быстрый и при этом совершенный»).
              +1
              хотя его смысл — рефлексия

              Если честно, никогда не понимал, почему reflection в этом контексте переводят рефлексией. Имхо, «отражение» гораздо более точно передаёт суть этого явления — программа «видит» некое представление, отражение себя. Кстати, именно поэтому в Scala в новой reflection-библиотеке основные объекты, с помощью которых reflection и производится, называются зеркалами (mirror) — ClassMirror, MethodMirror и прочие.
        0
        CreateFluentStringWriter(Stream) ['Привет '] ['Мир'] ['!'];

        Выглядит, здорово! Но читать такой код очень неприятно.

        Попытаюсь в мягкой форме сказать почему это плохо.
        Во-первых getter для свойства изменяет состояние объекта — это ужасная практика. Это подстава.
        Во-вторых, первый вариант читается проще.

        Но выглядит прикольно!
        p.s. А если заменить тип параметра на variant, то можно писать туда вообще всё что угодно.
        p.p.s. В RTL библиотеке Delphi тоже есть примеры Fluent interface — например тот же TStringBuilder
          0
          > Во-первых getter для свойства изменяет состояние объекта — это ужасная практика. Это подстава.
          Сам fluent интерфейс это подстава, но мы же эмулируем синтаксический сахар, а какими путями — не важно. Blackbox, зато работает.
            +1
            > Сам fluent интерфейс это подстава
            Почему?

            Например, чем плоха такая конструкция?
            CreateFluentBinaryWriter(Stream) .WriteString('active') .WriteByte(130);
          0
          «fluent interface» — это не что иное, как инфиксная запись выражений. Её с незапамятных времён использовали. Назвали так странно из-за того, что в некоторых языках (Delphi. C#) при описании используется ключевое слово «interface». И вообще, почти ни один язык не создан для определения инфиксных операторов (C++ — только перегрузка имеющихся; в Haskel что-то есть). Из-за этого — точки и скобочки.

          Only users with full accounts can post comments. Log in, please.