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:
Если объект не реализует интерфейс fluent, вы можете использовать методы расширения для цепочки вызовов.
вы можете использовать инициализацию объекта для имитации 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 интерфейсах с безопасностью типов, применяемой во время компиляции с использованием общих построителей и общих методов расширения.
методы расширения-это круто :)
динамическое ключевое слово в C# 4.0 позволит писать динамические строители стилей. Взгляните на следующее статьи о конструкции объекта JSON.