Свободные интерфейсы и неплотные абстракции - PullRequest
13 голосов
/ 11 января 2009

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

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

Спасибо

Ответы [ 8 ]

26 голосов
/ 11 января 2009

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

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

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

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

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

18 голосов
/ 11 января 2009

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

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

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

9 голосов
/ 11 января 2009

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

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
 );
5 голосов
/ 11 января 2009

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

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, даже если это будет действительный результат.

3 голосов
/ 11 января 2009

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

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

1 голос
/ 07 июля 2009

Нил Форд прекрасно объясняет и приводит примеры интерфейса Fluent в своей книге «Продуктивный программист».

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

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);
1 голос
/ 12 января 2009

Спасибо, ребята.

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

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

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

1 голос
/ 11 января 2009

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

Например, в NValidate я сделал это для упрощения проверки параметров:

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

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

...