Свободные интерфейсы - метод цепочки - PullRequest
34 голосов
/ 16 ноября 2008

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

Вот пример на 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));
    }

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

Ответы [ 8 ]

51 голосов
/ 16 ноября 2008

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

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

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

Метод создания цепочек

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

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

Одним из полезных методов проектирования является создание внутреннего класса - я всегда добавляю к ним суффикс «Выражение» - который предоставляет свободный API, позволяющий настраивать другой класс.

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

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

Фабричные классы

Иногда вы хотите создать ряд связанных объектов - примеры включают API Критерии NHibernate, ограничения ожидания Rhino.Mocks и новый синтаксис 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);
49 голосов
/ 16 ноября 2008

Вы можете создать свободный интерфейс в любой версии .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");
16 голосов
/ 16 ноября 2008

AFAIK, термин беглый интерфейс не определяет конкретную технологию или структуру, а скорее шаблон проектирования. В Википедии есть обширный пример свободного интерфейса на 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");
    }
}
5 голосов
/ 05 августа 2010

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

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

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

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

В следующих постах я расскажу об этом более глубоко.

С уважением,

Андре Вианна

3 голосов
/ 16 ноября 2008

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

Мне показалось, что эту технику проще всего реализовать с помощью интерфейсов:

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

Таким образом, любой конкретный класс, который реализует интерфейс, получает возможности быстрого объединения методов. FWIW .. Я написал приведенный выше код в C # 1.1

Вы найдете эту технику замусоренной в API jQuery

1 голос
/ 16 ноября 2008

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

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

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

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

1 голос
/ 16 ноября 2008

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

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, но это единственная причина моей лямбды. Как отметил Брэд, вы можете сделать это в любой версии .net. Хотя я думаю, что лямбда создает более интересные возможности, такие как эта.

======

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

0 голосов
/ 15 сентября 2009

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

...