Fluent Interfaces-Цепочка Методов

цепочка методов-единственный известный мне способ создания плавных интерфейсов.

вот пример в C#:

John john = new JohnBuilder()
    .AddSmartCode("c#")
    .WithfluentInterface("Please")
    .ButHow("Dunno");

Assert.IsNotNull(john);

  [Test]
    public void Should_Assign_Due_Date_With_7DayTermsVia_Invoice_Builder()
    {
        DateTime now = DateTime.Now;

        IInvoice invoice = new InvoiceBuilder()
            .IssuedOn(now)
            .WithInvoiceNumber(40)
            .WithPaymentTerms(PaymentTerms.SevenDays)
            .Generate();

        Assert.IsTrue(invoice.DateDue == now.AddDays(7));
    }

Итак, как другие создают плавные интерфейсы. Как вы его создаете? Какой язык / платформа / технология необходимы?

9 ответов


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

например, в C#:

public class JohnBuilder
{
    public JohnBuilder AddSmartCode(string s)
    {
        // do something
        return this;
    }

    public JohnBuilder WithfluentInterface(string s)
    {
        // do something
        return this;
    }

    public JohnBuilder ButHow(string s)
    {
        // do something
        return this;
    }
}

использование:

John = new JohnBuilder()
    .AddSmartCode("c#")
    .WithfluentInterface("Please")
    .ButHow("Dunno");

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

в современных языках OO, таких как C#, VB.NET и Java, цепочка методов-это один из способов, которым это достигается, но это не единственный метод - два других-это заводские классы и именованные параметры.

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

Способ Сцепления

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

вам не нужно обязательно возвращать тот же объект, на котором был вызван метод, то есть вам не всегда нужно " возвращать этот.";

один полезный метод проектирования-создать внутренний класс-я всегда суффиксирую их с "выражением" - который предоставляет fluent API, позволяя настраивать другой класс.

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

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

Заводские Классы

иногда вы хотите создать ряд связанных объектов-примеры включают API критериев NHibernate, Rhino.Издевается над ограничениями ожидания и новым синтаксисом NUnit 2.4.

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

например, в NUnit 2.4 вы можете написать:

Assert.That( result, Is.EqualTo(4));

класс "Is" является статическим классом, полным заводских методов, которые создают ограничения для оценки NUnit.

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

Assert.That( result, Is.EqualTo(4.0).Within(0.01));

(заранее прошу прощения - мой синтаксис может быть выключен.)

имени Параметры

в языках, которые их поддерживают (включая Smalltalk и C# 4.0), именованные параметры предоставляют способ включить дополнительный "синтаксис" в вызов метода, улучшая читаемость.

рассмотрим гипотетический метод Save (), который принимает имя файла и разрешения для применения к файлу после сохранения:

myDocument.Save("sampleFile.txt", FilePermissions.ReadOnly);

с именованными параметрами, этот метод может выглядеть так:

myDocument.Save(file:"SampleFile.txt", permissions:FilePermissions.ReadOnly);

или, более складно:

myDocument.Save(toFile:"SampleFile.txt", withPermissions:FilePermissions.ReadOnly);

AFAIK, термин fluent interface не определяет конкретную технологию или структуру, а скорее шаблон дизайна. Википедия имеет обширный пример свободных интерфейсов в C.

в простом методе сеттера вы не возвращаете void но this. Таким образом, вы можете связать все утверждения на этом объекте, которые ведут себя так. Вот краткий пример, основанный на вашем исходном вопросе:

public class JohnBuilder
{
    private IList<string> languages = new List<string>();
    private IList<string> fluentInterfaces = new List<string>();
    private string butHow = string.Empty;

    public JohnBuilder AddSmartCode(string language)
    {
        this.languages.Add(language);
        return this;
    }

    public JohnBuilder WithFluentInterface(string fluentInterface)
    {
        this.fluentInterfaces.Add(fluentInterface);
        return this;
    }

    public JohnBuilder ButHow(string butHow)
    {
        this.butHow = butHow;
        return this;
    }
}

public static class MyProgram
{
    public static void Main(string[] args)
    {
        JohnBuilder johnBuilder = new JohnBuilder().AddSmartCode("c#").WithFluentInterface("Please").ButHow("Dunno");
    }
}

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

проверьте это по адресу:

рекомендации по плавному дизайну интерфейса в C# part 1

У меня есть раздел о цепочке X вложенности, которая может быть вам интересна.

в следующих сообщениях я буду говорить об этом более глубоким образом.

лучшие с уважением,

Андре Вианна


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

Я нашел эту технику проще всего выполнить с помощью интерфейсов:

public interface IFoo
{
    IFoo SetBar(string s);
    IFoo DoStuff();
    IFoo SetColor(Color c);
}

таким образом, любой конкретный класс, реализующий интерфейс, получает fluent возможности цепочки методов. Чистки рядов.. Я написал выше код в C# 1.1

вы обнаружите, что эта техника разбросана по всему API jQuery


на ум приходит пара вещей, которые возможны в .Net 3.5 / C# 3.0:

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

  2. вы можете использовать инициализацию объекта для имитации fluent, но это работает только во время создания экземпляра и будет работать только для методов с одним аргументом (где свойство является только сеттером). Это кажется мне hackish, но там это есть.

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


вот как я построил свои так называемые fluent интерфейсы или мой единственный forary в него

Tokenizer<Bid> tkn = new Tokenizer<Bid>();
tkn.Add(Token.LambdaToken<Bid>("<YourFullName>", b => Util.CurrentUser.FullName))
    .Add(Token.LambdaToken<Bid>("<WalkthroughDate>",
          b => b.WalkThroughDate.ToShortDateString()))
    .Add(Token.LambdaToken<Bid>("<ContactFullName>", b => b.Contact.FullName))
    .Cache("Bid")
    .SetPattern(@"<\w+>");

в моем примере требуется .net 3.5, но это только причина моей лямбды. Как отметил Брэд, вы можете сделать это в любой версии .сеть. Хотя я думаю, что лямбда делает более интересные возможности, такие как это.

======

некоторые другие хорошие примеры-API критериев nHibernate, есть также свободное расширение nhibernate для настройки nhibernate, но Я никогда не использовал его


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

http://withasmiletomeltathousandhearts.wordpress.com/2009/02/16/fluent-interfaces-constraints-at-compile-time/

методы расширения-это круто :)


динамическое ключевое слово в C# 4.0 позволит писать динамические строители стилей. Взгляните на следующее статьи о конструкции объекта JSON.