2009-10-26 11 views
47

¿Cómo se puede crear una API que sea fluida en la naturaleza?creando API que es fluido

¿Esto se usa principalmente con los métodos de extensión?

+0

posible duplicado de [Consejos para escribir interfaces fluidas en C# 3] (http://stackoverflow.com/questions/224730/tips-for-writing-fluent-interfaces-in-c-sharp-3) – nawfal

Respuesta

43

This article lo explican mucho mejor de lo que podría hacerlo.

EDITAR, no se puede exprimir esto en un comentario ...

hay dos lados en las interfaces, la implementación y el uso. Hay más trabajo por hacer en el lado de la creación, estoy de acuerdo con eso, sin embargo, los principales beneficios se pueden encontrar en el lado del uso de las cosas. De hecho, para mí la principal ventaja de las interfaces fluidas es una API más natural, más fácil de recordar y usar y, por qué no, más estética. Y tal vez, el esfuerzo de tener que exprimir una API de forma fluida puede conducir a API mejor pensada.

Como dice Martin Fowler en the original article about fluent interfaces:

Probablemente lo más importante aviso acerca de este estilo es que la intención es hacer algo a lo largo de las líneas de un interna DomainSpecificLanguage. De hecho, esto es por qué elegimos el término 'fluido' para describirlo, de muchas maneras los dos términos son sinónimos. La API es diseñada principalmente para ser legible y para fluir. El precio de esta fluidez es más esfuerzo, tanto en el pensamiento como en la construcción API en sí. La API simple de de los métodos de adición constructor, setter y es mucho más fácil de escribir . Proceder con una buena fluidez API requiere un buen pensamiento.

Como en la mayoría de los casos las API se crean una sola vez y se usan una y otra vez, el esfuerzo adicional puede valer la pena.

¿Y prolijo? Estoy totalmente de verbosidad si sirve la legibilidad de un programa.

+0

I don Realmente entiendo cómo esto genera una API fluida, y en realidad creo que este patrón de diseño es muy detallado. – badbod99

+0

ok ... por lo que coincide con la definición de wikipedia http://en.wikipedia.org/wiki/Fluent_interface Todavía creo que es horrible de implementar. – badbod99

+0

Verbose no sirve para la legibilidad del programa en este caso, sirve para la legibilidad de la aplicación del cliente. Su API aún necesita ser mejorada y mantenida. – badbod99

0

No y sí. Lo básico es una buena interfaz o interfaces para los tipos que desea que se comporten con fluidez. Las bibliotecas con métodos de extensión pueden extender este comportamiento y devolver la interfaz. Los métodos de extensión brindan a otros la posibilidad de ampliar su API con más métodos.

Un buen diseño fluido puede ser difícil y requiere un período de prueba y error bastante largo para ajustar por completo los componentes básicos. Solo una API fluida para configuración o configuración no es tan difícil.

Aprender a construir una API con fluidez hace una al mirar las API existentes. Compare el FluentNHibernate con las API de .NET fluidas o las interfaces fluidas de ICriteria. Muchas API de configuración también están diseñadas "con fluidez".

6

BESO: Keep it simple stupid.

El diseño fluido es un principio de diseño estético utilizado en toda la API. Tu metodología que usas en tu API puede cambiar ligeramente, pero en general es mejor mantenerse constante.

Aunque puede pensar 'todos pueden usar esta API, porque utiliza todos los diferentes tipos de metodología'.La verdad es que el usuario comenzaría a sentirse perdido porque está cambiando constantemente la estructura/estructura de datos de la API a un nuevo principio de diseño o convención de nomenclatura.

Si desea cambiar a medio camino a un principio de diseño diferente, por ejemplo ... Conversión de códigos de error a manejo de excepciones debido a una mayor potencia de comando. Sería una locura y normalmente tendría mucho dolor en la cola. Es mejor mantener el rumbo y agregar la funcionalidad que sus clientes pueden utilizar y vender que hacer que vuelvan a escribir y volver a descubrir todos sus problemas.

Siguiendo con lo anterior, puede ver que hay más en el trabajo de escribir una API Fluent que la del ojo. Hay que tomar decisiones psicológicas y estéticas antes de comenzar a escribir una y, aun así, la sensación, necesidad y deseo de ajustarse a las demandas de los clientes y mantenerse constante es lo más difícil de todos.

6

¿Qué es un API fluida

Wikipedia ellas define aquí http://en.wikipedia.org/wiki/Fluent_interface

¿Por qué no utilizar una interfaz fluida

Yo sugeriría no implementar una interfaz fluida tradicional, ya que aumenta la cantidad de código que necesita para escribir, complica su código y simplemente está agregando un texto repetitivo innecesario.

Otra opción, ¡no hacer nada!

No implemente nada. No proporcione constructores "fáciles" para establecer propiedades y no proporcione una interfaz inteligente para ayudar a su cliente. Permita que el cliente establezca las propiedades como lo haría normalmente. En .Net C# o VB esto podría ser tan simple como usar object initializers.

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

Así que no necesita crear ninguna interfaz inteligente en su código, y esto es muy legible.

Si tiene conjuntos muy complejos propiedades que se deben establecer o establecer en un orden determinado, utilice un objeto de configuración separado y páselo a la clase a través de una propiedad separada.

CarConfig conf = new CarConfig { Color = Color.Yellow, Fabric = Fabric.Leather }; 
Car myCar = new Car { Config = conf }; 
+11

Las ventajas de una API fluida (mejor legibilidad y una forma mucho más intuitiva de trabajar con su API) son mucho más importantes que tener un código un poco más complicado. SI crea una API, se trata del usuario que accederá a su API. Si puede complacerlo con un estilo fluido, me tomaría todo el esfuerzo para hacer que trabajar con la API sea lo más fácil posible, incluso si eso significaría poner mucho más trabajo en la API. –

+3

Crea una interfaz diferente a la interfaz de todos los demás. Eso no ayuda a nadie a entender su API ni a usarla fácilmente. El modelo de objeto de inicialización o propiedad es mucho más estándar y autodescriptivo en OO normal. – badbod99

+0

Los inicializadores de objetos no permiten la creación de objetos inmutables que son útiles para ciertas cosas. – mmmdreg

28

MrBlah,

Aunque puede escribir métodos de extensión en escribir una interfaz fluida, un mejor enfoque es usar el Builder. Estoy en el mismo barco que tú y estoy tratando de descubrir algunas características avanzadas de las interfaces fluidas.

A continuación verás algunos ejemplos de código que he creado en another thread

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

Me gusta, ejemplo muy claro, aunque ¿dónde está la definición de la interfaz? :) – Hace

+0

Utilizando la sintaxis del inicializador de objetos C#, en su lugar, el ejemplo sería más breve (y evitará tener que escribir los métodos 'WithFoo'), y dejar en claro que solo se están estableciendo las propiedades. Creo que el estilo "fluido" solo debe usarse si necesita efectos secundarios. – Dai

1

Escribiendo un API fluida que es complicado, es por eso que he escrito Diezel que es un generador de API Fluent para Java.Genera la API con interfaces (o supuesto) a:

  1. control de la llamada fluya
  2. captura tipos genéricos (como guice uno)

También genera implementaciones.

Es un plugin de maven.

15

Si bien muchas personas citan a Martin Fowler como un destacado exponente en la fluida discusión API, sus primeras afirmaciones de diseño en realidad evolucionan en torno a fluent builder pattern o method chaining. Las API fluidas se pueden desarrollar en internal domain-specific languages. Un artículo que explica cómo una notación BNF de una gramática puede transformarse de forma manual en un "API fluida" se puede ver aquí:

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

Transforma esta gramática:

enter image description here

En esta API Java:

// 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 y C# siendo algo similar, el ejemplo ciertamente se traduce en su caso de uso también. La técnica anterior ha sido muy utilizada en jOOQ, un lenguaje fluido de API/dominio interno específico que modela el lenguaje SQL en Java

7

Esta es una pregunta muy antigua, y esta respuesta probablemente debería ser un comentario en lugar de una respuesta, pero Creo que es un tema que vale la pena seguir hablando, y esta respuesta es demasiado larga para ser un comentario.

El pensamiento original con respecto a la "fluidez" parece haber sido básicamente sobre la adición de potencia y flexibilidad (encadenamiento de métodos, etc.) a los objetos al tiempo que hace que el código sea un poco más fácil de entender.

Por ejemplo

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

es menos "fluidez" que

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

Pero para mí, la tarde no es realmente más potente o flexible o auto-explicativo que

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

.. de hecho, llamaría a esta última versión más fácil de crear, leer y mantener que la anterior, y yo diría que también requiere significativamente menos equipaje entre bastidores. Cuál para mí es importante, por (al menos) dos razones:

1 - El costo asociado con crear y mantener capas de objetos (sin importar quién lo haga) es tan real, relevante e importante como el costo asociado con creando y manteniendo el código que los consume.

2 - La hinchazón de código incrustada en capas de objetos crea tantos (si no más) problemas como la hinchazón de código en el código que consume esos objetos.

El uso de la última versión significa que puede agregar una propiedad (potencialmente útil) a la clase Company simplemente agregando una línea de código muy simple.

Eso no quiere decir que sienta que no hay lugar para el encadenamiento de métodos. Realmente me gusta ser capaz de hacer cosas como (en JavaScript)

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

..where (en el caso hipotético de que estoy imaginando) Dado y Falla hicieron adiciones al objeto original Ajax, y fueron capaces de agregarse sin cambiar ninguno de los códigos de objeto Ajax originales ni ninguno del código existente que hizo uso del objeto Ajax original, y sin crear elementos únicos que fueran excepciones a la organización general del código.

Así que definitivamente he encontrado valor en hacer que un subconjunto de funciones de un objeto devuelva el objeto 'this'. De hecho, cada vez que tengo una función que de otro modo regresaría vacía, considero que esto sea devuelto.

Pero todavía no he encontrado un valor significativo en agregar "interfaces fluidas" (.eg "Establecer") a un objeto, aunque en teoría parece que podría haber un tipo de organización de código similar a un espacio de nombres que podría surgir fuera de la práctica de hacer eso, lo que podría valer la pena. ("Set" puede no ser particularmente valioso, pero "Command", "Query" y "Transfer" podrían, si ayudaron a organizar cosas y facilitar y minimizar el impacto de adiciones y cambios.) Uno de los beneficios potenciales de tal práctica , dependiendo de cómo se haya hecho, podría haber una mejora en el nivel típico de atención del codificador y la atención a los niveles de protección, cuya falta ciertamente ha causado un gran dolor en los volúmenes.

Cuestiones relacionadas