Свободные интерфейсы и неплотные абстракции

Что такое свободный интерфейс? Я не могу найти хорошее определение этого, но все, что я получаю, это примеры длинного кода на языке, с которым я не очень хорошо знаком (например, C++).

Кроме того, что такое дырявая абстракция?

Спасибо

8 ответов

Свободный интерфейс - это API, который позволяет вам писать код, который читается более или менее как обычный английский. Например:

Find.All.Questions(Where.IsAnswered == true);

Цепочка методов обычно используется как часть реализации, но это еще не все. Процитирую Фаулера:

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

Его также часто называют внутренним DSL, поскольку синтаксис напоминает синтаксис DSL, но он реализован внутри языка хоста, а не обрабатывается парсером.

Утечка абстракции - это абстракция, в которой детали базовой реальности часто "просачиваются".

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

Простым примером "утечки" в абстракции может быть обычный тип с плавающей точкой. Кажется, что они представляют общие действительные числа, и вы можете использовать их для выполнения основных вычислений. Но в какой-то момент вы сталкиваетесь со сценарием, в котором 1/3*3!= 1 или 1 + 10^-20 = 1. Это происходит, когда фактические детали реализации просачиваются и абстракция разрывается.

Свободный интерфейс - термин, который придумал Эрик Эванс, и это просто еще одно название для цепочки методов. Мартин Фаулер написал пару статей на эту тему, но примерно это выглядит так:

m_Window = window::with()
    .width(l_Width)
    .height(l_Height)
    .title("default window")
    .left(200)
    .top(200)
.create();

Свободный интерфейс обычно используется для создания именованных параметров в языке, который их не поддерживает (например, Идиома именованных параметров в C++), или в предметно-ориентированных языках, чтобы код читался более свободно.

Я видел, как они используются для всего: от библиотек обработки изображений до библиотек регулярных выражений, библиотек 3D. Другие примеры включают построение древовидных структур, списков или других структур данных. Все, что требует построения сложных объектов (загрузки параметров), может использовать Fluent Interfaces, чтобы сделать его более читабельным. Например, сравните предыдущий пример с вызовом функции CreateWindow:

 ::CreateWindow(
      "Window class", 
      "Window title", 
      dwStyle, X, Y, 
      nWidth, nHeight, 
      hWndPant, hMenu, 
      hInstance, NULL
 );

Вот обычный повседневный интерфейс:

public interface NotFluent
{
  void DoA();
  void DoB();
  void DoC();
}

И вот свободный интерфейс:

public interface Fluent
{
  Fluent DoA();
  Fluent DoB();
  Fluent DoC();
}

Самое очевидное отличие состоит в том, что когда мы возвращаем void, мы возвращаем экземпляр типа интерфейса. Понятно, что возвращаемый интерфейс - это CURRENT INSTANCE, а не новый экземпляр того же типа. Конечно, это не подлежит принудительному исполнению, и в случае неизменяемых объектов (например, строки) это другой экземпляр, но его можно считать только обновленным экземпляром.

Вот примеры их использования:

NotFluent foo = new NotFluentImpl();
foo.DoA();
foo.DoB();
foo.DoC();

Fluent bar = new FluentImpl();
bar.DoA().DoB().DoC();

Обратите внимание, что свободный интерфейс легче использовать при объединении различных вызовов. IRL, ознакомьтесь с методами расширения Linq и с тем, как каждый вызов предназначен для передачи в другой. Ни один из методов не возвращает void, даже если это будет действительный результат.

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

Я впервые столкнулся с беглыми интерфейсами в 1990 году, когда Полицейская Интерфейса Модула-3 (я не придумываю это) потребовала, чтобы все методы инициализации возвращали инициализированный объект. Я полагаю, что это использование предшествует чеканке термина "свободный интерфейс".

Спасибо, парни.

Отличное описание.

Моя мысль о беглых интерфейсах, где они были для удобочитаемости. Я всегда мог прочитать цепочку методов и как один связан с предыдущим / следующим методом.

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

В свободном интерфейсе методы объекта будут возвращать ссылку на объект, так что вы можете связывать вызовы метода вместе.

Например, в NValidate я сделал это, чтобы упростить проверку параметров:

public City GetCity(string zipCode)
{
   zipCode.Assert("zipCode").IsNotNullOrEmpty().HasLength(5,10).Matches("\\d[5]-\\d[4]");
   // Continue processing
}

Я не могу говорить с дырявыми абстракциями.

Нил Форд хорошо объясняет и дает примеры Fluent Interface в своей книге "Продуктивный программист".

Традиционный объект или "боб" с геттерами / сеттерами:

Car car = new CarImpl();
MarketingDescription des = new MarketingDescriptionImpl();
desc.setType("Box");
desc.setSubtype("Insulated");
desc.setAttribute("length", "50.5");
desc.setAttribute("ladder", "yes");
desc.setAttribute("lining type", "cork");
car.setDescription(desc);

Удовлетворить ту же потребность с помощью свободного интерфейса:

Car car = Car.describedAs()
  .box()
  .length(50.5)
  .type(Type.INSULATED)
  .includes(Equipment.LADDER)
  .lining(Lining.CORK);

Вы можете найти хорошее определение и основные понятия свободного интерфейса в этом посте:

Руководство по разработке интерфейса Fluent в C# часть 1

Надеюсь, это поможет.

Другие вопросы по тегам