создание API, который свободно - PullRequest
52 голосов
/ 26 октября 2009

Как можно создать API, свободно владеющий природой?

Использует ли это в основном методы расширения?

Ответы [ 9 ]

44 голосов
/ 26 октября 2009

Эта статья объясняет это намного лучше, чем я когда-либо мог.

РЕДАКТИРОВАТЬ, не могу сжать это в комментарии ...

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

Как говорит Мартин Фаулер в оригинальной статье о плавных интерфейсах :

Наверное, самая важная вещь для обратите внимание на этот стиль является то, что Намерение сделать что-то по линии внутреннего DomainSpecificLanguage. На самом деле это почему мы выбрали термин «свободно» описать это, во многом два Термины являются синонимами. API есть в первую очередь предназначен для чтения и течь. Цена этой беглости больше усилий, как в мышлении, так и в сама конструкция API. простой API конструктора, сеттера и методы сложения намного проще написать. Подходя с хорошим беглым API требует больших усилий.

Поскольку в большинстве случаев API создаются один раз и используются снова и снова, дополнительные усилия могут стоить того.

А многословно? Я за многословие, если оно служит удобочитаемости программы.

35 голосов
/ 25 ноября 2009

MrBlah,

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

Ниже вы увидите пример кода, который я создал в другом потоке

public class Coffee
{
    private bool _cream;
    private int _ounces;

    public static Coffee Make { get { return new Coffee(); } }

    public Coffee WithCream()
    {
        _cream = true;
        return this;
    }
    public Coffee WithOuncesToServe(int ounces)
    {
        _ounces = ounces;
        return this;
    }
}

var myMorningCoffee = Coffee.Make.WithCream().WithOuncesToServe(16);
19 голосов
/ 09 сентября 2013

В то время как многие люди указывают на Мартина Фаулера как на выдающегося специалиста в беглом обсуждении API, его ранние утверждения о дизайне фактически развиваются вокруг беглого шаблона построителя или , связывающего метод . Свободные API-интерфейсы могут быть доработаны до реальных внутренних домен-специфических языков . Статью, которая объясняет, как нотация BNF грамматики можно вручную преобразовать в «плавный API», можно посмотреть здесь:

http://blog.jooq.org/2012/01/05/the-java-fluent-api-designer-crash-course/

Преобразует эту грамматику:

enter image description here

В этот Java API:

// Initial interface, entry point of the DSL
interface Start {
  End singleWord();
  End parameterisedWord(String parameter);
  Intermediate1 word1();
  Intermediate2 word2();
  Intermediate3 word3();
}

// Terminating interface, might also contain methods like execute();
interface End {
  void end();
}

// Intermediate DSL "step" extending the interface that is returned
// by optionalWord(), to make that method "optional"
interface Intermediate1 extends End {
  End optionalWord();
}

// Intermediate DSL "step" providing several choices (similar to Start)
interface Intermediate2 {
  End wordChoiceA();
  End wordChoiceB();
}

// Intermediate interface returning itself on word3(), in order to allow
// for repetitions. Repetitions can be ended any time because this 
// interface extends End
interface Intermediate3 extends End {
  Intermediate3 word3();
}

Java и C # в некоторой степени похожи, пример, безусловно, также соответствует вашему варианту использования. Вышеупомянутая методика широко использовалась в jOOQ , свободном API / внутреннем предметно-ориентированном языке, моделирующем язык SQL в Java

8 голосов
/ 26 сентября 2013

Это очень старый вопрос, и этот ответ, вероятно, должен быть комментарием, а не ответом, но я думаю, что эту тему стоит продолжать обсуждать, и этот ответ слишком длинный, чтобы быть комментарием.

Первоначальное мышление о «беглости», по-видимому, в основном заключалось в добавлении мощности и гибкости (связывание методов и т. Д.) К объектам, в то же время делая код немного более понятным.

Например

Company a = new Company("Calamaz Holding Corp");
Person p = new Person("Clapper", 113, 24, "Frank");
Company c = new Company(a, 'Floridex', p, 1973);

менее "свободно", чем

Company c = new Company().Set
    .Name("Floridex");
    .Manager(
        new Person().Set.FirstName("Frank").LastName("Clapper").Awards(24)
    )
    .YearFounded(1973)
    .ParentCompany(
        new Company().Set.Name("Calamaz Holding Corp")
    )
;

Но, на мой взгляд, последнее на самом деле не более могущественно, гибко или самоочевидно, чем

Company c = new Company(){
   Name = "Floridex",
   Manager = new Person(){ FirstName="Frank", LastName="Clapper", Awards=24 },
   YearFounded = 1973,
   ParentCompany = new Company(){ Name="Calamaz Holding Corp." }
};

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

1 - Стоимость, связанная с созданием и поддержанием слоев объектов (независимо от того, кто это делает), так же реальна, актуальна и важна, как и стоимость, связанная с созданием и обслуживанием кода, который их потребляет.

2 - Раздувание кода, внедренное в слои объектов, создает столько же (если не больше) проблем, что и раздувание кода в коде, который потребляет эти объекты.

Использование последней версии означает, что вы можете добавить (потенциально полезное) свойство в класс Company, просто добавив одну очень простую строку кода.

Это не значит, что я чувствую, что нет места для цепочки методов. Мне очень нравится, что я могу делать что-то вроде (в JavaScript)

var _this = this;
Ajax.Call({
    url: '/service/getproduct',
    parameters: {productId: productId},
)
.Done(
    function(product){
        _this.showProduct(product);
    }
)
.Fail(
    function(error){
        _this.presentError(error);
    }
);

.. где (в гипотетическом случае, который я представляю) Done и Fail были дополнениями к исходному объекту Ajax, и их можно было добавлять без изменения какого-либо исходного кода объекта Ajax или любого существующего кода, который сделал использование исходного объекта Ajax и без создания разовых вещей, которые были исключениями из общей организации кода.

Так что я определенно нашел значение в том, чтобы подмножество функций объекта возвращало объект this. Фактически, всякий раз, когда у меня есть функция, которая в противном случае возвращает void, я считаю, что она должна возвращать это.

Но я еще не нашел существенного значения в добавлении «плавных интерфейсов» (.eg «Set») к объекту, хотя теоретически кажется, что может существовать своего рода организация кода, подобная пространству имен, которая может возникнуть практики делать это, что может быть полезным. («Набор» может быть не особенно ценным, но «Команда», «Запрос» и «Перенос» могут быть полезны, если бы он помог организовать вещи, облегчить и минимизировать влияние дополнений и изменений.) Одно из потенциальных преимуществ такой практики в зависимости от того, как это было сделано, может быть улучшение типичного уровня заботы кодера и внимания к уровням защиты, недостаток которых, безусловно, вызвал большие объемы горя.

6 голосов
/ 26 октября 2009

ПОЦЕЛУЙ: Держи это просто глупо.

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

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

Если вы хотите перейти на полпути к другому принципу проектирования, например .. Преобразование из кодов ошибок в обработку исключений из-за некоторой более высокой мощности командования. Это было бы глупо и, как правило, в хвосте много боли. Лучше остаться в курсе и добавить функциональность, которую ваши клиенты могут использовать и продавать, чем заставлять их переписывать и заново обнаруживать все их проблемы.

Исходя из вышесказанного, вы можете видеть, что при написании Fluent API есть больше, чем кажется на первый взгляд. Существуют психологические и эстетические решения, которые нужно сделать, прежде чем начать писать, и даже тогда самое трудное из всех - чувство, потребность и желание соответствовать требованиям клиентов и оставаться последовательными.

5 голосов
/ 26 октября 2009

Что такое свободный API

Википедия определяет их здесь http://en.wikipedia.org/wiki/Fluent_interface

Почему бы не использовать свободный интерфейс

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

Другой вариант, ничего не делать!

Не реализовывайте ничего. Не предоставляйте «простых» конструкторов для настройки свойств и не предоставляйте умный интерфейс для помощи вашему клиенту. Разрешить клиенту устанавливать свойства так, как обычно. В .Net C # или VB это может быть так же просто, как использовать инициализаторы объектов .

Car myCar = new Car { Name = "Chevrolet Corvette", Color = Color.Yellow };

Так что вам не нужно создавать какой-либо умный интерфейс в вашем коде, и это очень читабельно.

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

CarConfig conf = new CarConfig { Color = Color.Yellow, Fabric = Fabric.Leather };
Car myCar = new Car { Config = conf };
1 голос
/ 18 декабря 2011

Писать свободный API это сложно, поэтому я написал Diezel , который является генератором Fluent API для Java. Он генерирует API с интерфейсами (или курсом) для:

  1. управление потоком вызовов
  2. поймать универсальные типы (как Guice One)

Он также генерирует реализации.

Это плагин Maven.

1 голос
/ 01 февраля 2010

С беглым API:

myCar.SetColor(Color.Blue).SetName("Aston Martin");

Посмотри это видео http://www.viddler.com/explore/dcazzulino/videos/8/

0 голосов
/ 26 октября 2009

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

Хороший беглый дизайн может быть сложным и требует довольно длительного периода проб и ошибок для полной подстройки основных строительных блоков. Простое API для настройки или настройки не так сложно.

Изучение создания свободного API делает один, рассматривая существующие API. Сравните FluentNHibernate с текущими API .NET или текущими интерфейсами ICriteria. Многие API конфигурации также разработаны "свободно".

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...