Interfaces Fluidas-Encadenamiento de Métodos


El encadenamiento de métodos es la única forma que conozco de construir interfaces fluidas.

Aquí hay un ejemplo en 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));
    }

Entonces, ¿cómo crean otros interfaces fluidas? ¿Cómo se crea? ¿Qué idioma / plataforma / tecnología se necesita?

Author: Cᴏʀʏ, 2008-11-16

9 answers

Puede crear una interfaz fluida en cualquier versión de.NET o cualquier otro lenguaje orientado a Objetos. Todo lo que necesita hacer es crear un objeto cuyos métodos siempre devuelvan el objeto en sí.

Por ejemplo en 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;
    }
}

Uso:

John = new JohnBuilder()
    .AddSmartCode("c#")
    .WithfluentInterface("Please")
    .ButHow("Dunno");
 48
Author: Chris Pietschmann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-16 04:32:36

La idea central detrás de la construcción de una interfaz fluida es una de legibilidad: alguien que lea el código debería ser capaz de entender lo que se está logrando sin tener que profundizar en la implementación para aclarar los detalles.

En lenguajes OO modernos como C#, VB.NET y Java, método de encadenamiento es una forma de que esto se logra, pero no es la única técnica - otros dos son clases de fábrica y parámetros con nombre.

Tenga en cuenta también que estas técnicas no son mutuamente excluyentes - el objetivo es maximizar la capacidad de lectura del código, no la pureza del enfoque.

Método De Encadenamiento

La idea clave detrás del encadenamiento de métodos es nunca tener un método que devuelva void, sino siempre devolver algún objeto, o más a menudo, alguna interfaz, que permita realizar más llamadas.

No necesita necesariamente devolver el mismo objeto en el que se llamó al método, es decir, no siempre necesita "devolver esto;".

Una técnica de diseño útil es crear una clase interna - siempre los sufijo con "Expression" - que exponga la API fluent, permitiendo la configuración de otra clase.

Esto tiene dos ventajas: mantiene la API fluida en un solo lugar, aislada de la funcionalidad principal de la clase, y (porque es una clase interna) puede jugar con las entrañas de la clase principal de maneras que otras clases no pueden.

Es posible que desee utilizar una serie de interfaces, para controlar qué métodos están disponibles para el desarrollador en un momento dado en el tiempo.

Clases De Fábrica

A veces desea crear una serie de objetos relacionados; por ejemplo, la API de criterios de NHibernate, Rhino.Se burla de las restricciones de expectativa y de la nueva sintaxis de NUnit 2.4.

En ambos casos, tiene los objetos reales que está almacenando, pero para hacerlos más fáciles de crear, hay clases de fábrica que proporcionan métodos estáticos para fabricar las instancias que necesita.

Por ejemplo, en NUnit 2.4 puedes escribir:

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

La clase "Is" es una clase estática llena de métodos de fábrica que crean restricciones para la evaluación por NUnit.

De hecho, para permitir errores de redondeo y otras imprecisiones de números de coma flotante, puede especificar una precisión para la prueba:

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

(Disculpas anticipadas - mi sintaxis puede estar apagada.)

Parámetros con nombre

En los lenguajes que los soportan (incluyendo Smalltalk y C # 4.0) los parámetros con nombre proporcionan una forma de incluir "sintaxis" adicional en una llamada a un método, mejorando la legibilidad.

Considere un método Save () hipotético que toma un nombre de archivo y permisos para aplicar al archivo después de guardar:

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

Con parámetros nombrados, este método podría verse así:

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

O, más fluidamente:

myDocument.Save(toFile:"SampleFile.txt", withPermissions:FilePermissions.ReadOnly);
 49
Author: Bevan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-16 06:32:38

AFAIK, el término fluent interface no especifica una tecnología o marco específico, sino más bien un patrón de diseño. Wikipedia tiene un extenso ejemplo de interfaces fluidas en C {.

En un método setter simple, no se devuelve void sino this. De esa manera, puedes encadenar todas las declaraciones en ese objeto que se comportan así. Aquí hay un ejemplo rápido basado en su pregunta original:

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");
    }
}
 16
Author: hangy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-16 02:28:42

Hace algún tiempo tuve las mismas dudas que tú tienes ahora. He investigado un poco y ahora estoy escribiendo una serie de entradas de blog sobre técnicas de diseño de una interfaz fluida.

Compruébalo en:

Directrices para el diseño fluido de la interfaz en C # parte 1

Tengo una sección sobre Encadenar Anidamiento X que puede ser interesante para usted.

En los siguientes posts hablaré de ello de una manera más profunda.

Saludos cordiales,

André Vianna

 5
Author: Andre Vianna,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-25 19:52:27

La interfaz Fluent se logra en la programación orientada a objetos devolviendo siempre de sus métodos la misma interfaz que contiene el método. En consecuencia, puede lograr este efecto en java, javascript y sus otros lenguajes orientados a objetos favoritos, independientemente de la versión.

He encontrado esta técnica más fácil de lograr a través del uso de interfaces:

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

De esta manera, cualquier clase concreta que implemente la interfaz, obtiene el método fluent chaining capacidad. FWIW.. Escribí el código anterior en C# 1.1

Usted encontrará esta técnica llena a lo largo de la API de jQuery

 3
Author: Jeff Fritz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-16 02:42:10

Un par de cosas vienen a la mente que son posibles en. Net 3.5/ C # 3.0:

  1. Si un objeto no implementa una interfaz fluida, puedes usar Métodos de extensión para encadenar tus llamadas.

  2. Es posible que pueda usar la inicialización de objetos para simular fluent, pero esto solo funciona en tiempo de instanciación y solo funcionaría para métodos de argumento único (donde la propiedad es solo un setter). Esto me parece hackish, pero el allí él ser.

Personalmente, no veo nada malo con el uso de encadenamiento de funciones si está implementando un objeto builder. Si el objeto builder tiene métodos de encadenamiento, mantiene limpio el objeto que está creando. Sólo una idea.

 1
Author: Jason Jackson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-16 02:30:18

Así es como he construido mis llamadas interfaces fluentes o mi único forario en él

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+>");

Mi ejemplo requerida .net 3.5, pero eso es sólo a causa de mi lambda. Como Brad señaló usted puede hacer esto en cualquier versión de .net. Aunque creo lambda para hacer más posibilidades interesantes como este.

======

Algunos otros buenos ejemplos son la API de criterios de NHibernate, también hay una extensión de nhibernate fluida para configurar nhibernate, pero nunca la he usado

 1
Author: JoshBerke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-16 02:33:41

Encontré una manera de hacer encadenamiento de métodos polimórficos en las interfaces fluent con seguridad de tipos forzada en tiempo de compilación utilizando constructores genéricos y métodos de extensión genéricos.

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

Los métodos de extensión son geniales:)

 1
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-16 14:29:01

La palabra clave dinámica en C# 4.0 hará posible escribir constructores de estilo dinámicos. Echa un vistazo al siguiente artículo sobre la construcción de objetos JSON.

 0
Author: Dan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-15 01:13:12