2009-02-25 9 views
27

Hace poco vi un webcast sobre how to create a fluent DSL y tengo que admitir que no entiendo las razones por las cuales se usaría dicho enfoque (al menos para el ejemplo dado).¿Cuál es el punto de las DSL/interfaces fluidas?

La transmisión por Internet presenta una imagen de cambio de tamaño de clase, que le permite especificar una imagen de entrada, cambia su tamaño y guardarlo en un archivo de salida utilizando la siguiente sintaxis (usando C#):

Sizer sizer = new Sizer(); 
sizer.FromImage(inputImage) 
    .ToLocation(outputImage) 
    .ReduceByPercent(50) 
    .OutputImageFormat(ImageFormat.Jpeg) 
    .Save(); 

Pongo 't entender cómo esto es mejor que un método 'convencional' que tiene algunos parámetros:

sizer.ResizeImage(inputImage, outputImage, 0.5, ImageFormat.Jpeg); 

desde el punto de vista de la facilidad de uso, esto parece mucho más fácil de usar, ya que claramente le dice lo que el método espera como entrada. Por el contrario, con la interfaz fluida, nada le impide omitiendo/olvidar un parámetro/método de llamada, por ejemplo:

sizer.ToLocation(outputImage).Save(); 

Así que a mis preguntas:

1 - ¿Hay alguna manera para mejorar la usabilidad de una interfaz fluida (es decir, decirle al usuario lo que se espera que haga)?

2 - ¿Este enfoque de interfaz fluida es solo un reemplazo de los parámetros del método nombrado no existente en C#? ¿Los parámetros nombrados dejarán obsoletas las interfaces fluidas, p. Algo similar ofertas de Objective-C:

sizer.Resize(from:input, to:output, resizeBy:0.5, ..) 

3 - son interfaces fluidas sobre-utilizados simplemente porque son populares en la actualidad?

4 - ¿O fue solo un mal ejemplo el elegido para la transmisión por Internet? En ese caso, dígame cuáles son las ventajas de tal enfoque, dónde tiene sentido usarlo.

BTW: Sé lo fácil que es hacer jquery, así que no estoy buscando comentarios sobre ese u otros ejemplos existentes.

Estoy más buscando algunos comentarios (generales) que me ayuden a comprender (por ejemplo) cuándo implementar una interfaz fluida (en lugar de una biblioteca de clases clásica) y qué tener en cuenta al implementar una.

+0

Pregunta interesante –

+1

+1 También creo que actualmente están sobreactuadas. –

Respuesta

13

2 - ¿Es este enfoque interfaz fluida sólo un reemplazo para la no existente denominado parámetros del método en C#? Los parámetros nombrados harán que las interfaces con fluencia sean obsoletas, p. algo objetivo-C similar ofrece:

Bueno, sí y no. La interfaz fluida le da una mayor cantidad de flexibilidad. Algo que no podría lograrse con parametros con nombre es:

sizer.FromImage(i) 
.ReduceByPercent(x) 
.Pixalize() 
.ReduceByPercent(x) 
.OutputImageFormat(ImageFormat.Jpeg) 
.ToLocation(o) 
.Save(); 

El FromImage, ToLocation y OutputImageFormat en la interfaz de fluido, oler un poco a mí. En cambio, habría hecho algo en esta línea, que creo que es mucho más claro.

new Sizer("bob.jpeg") 
.ReduceByPercent(x) 
.Pixalize() 
.ReduceByPercent(x) 
.Save("file.jpeg",ImageFormat.Jpeg); 

Las interfaces fluidas tienen los mismos problemas que muchas técnicas de programación, pueden ser mal usadas, usadas en exceso o subutilizadas. Creo que cuando esta técnica se usa de manera efectiva puede crear un modelo de programación más rico y conciso. Incluso StringBuilder lo admite.

var sb = new StringBuilder(); 
sb.AppendLine("Hello") 
.AppendLine("World"); 
2

Es una forma de implementar cosas.

Para objetos que no hacen más que manipular el mismo elemento una y otra vez, no hay nada realmente incorrecto en él. Considere los flujos de C++: son lo último en esta interfaz. Cada operación devuelve la secuencia de nuevo, por lo que puede encadenar otra operación de transmisión.

Si está haciendo LINQ y haciendo la manipulación de un objeto una y otra vez, esto tiene sentido.

Sin embargo, en su diseño, debe tener cuidado. ¿Cuál debería ser el comportamiento si quiere desviarse a la mitad?(Es decir,

var obj1 = object.Shrink(0.50); // obj1 is now 50% of obj2 
var obj2 = object.Shrink(0.75); // is ojb2 now 75% of ojb1 or is it 75% of the original? 

Si se obj2 75% del objeto original, entonces eso significa que usted está haciendo una copia completa del objeto cada vez (y tiene sus ventajas en muchos casos, al igual que si usted está tratando para hacer dos instancias de la misma cosa, pero ligeramente diferente).

Si los métodos simplemente manipulan el objeto original, este tipo de sintaxis es un tanto falso. Esas son manipulaciones en el objeto en lugar de manipulaciones para crear un objeto cambiado

No todas las clases funcionan así, ni tiene sentido hacer este tipo de diseño. Por ejemplo, este estilo de el diseño tendría poca o ninguna utilidad en el diseño de un controlador de hardware o en el núcleo de una aplicación GUI. Siempre que el diseño implique nada más que la manipulación de algunos datos, este patrón no es malo.

5

considerar:

sizer.ResizeImage(inputImage, outputImage, 0.5, ImageFormat.Jpeg); 

Lo que si se ha utilizado menos claros los nombres de variables:

sizer.ResizeImage(i, o, x, ImageFormat.Jpeg); 

Imagínese que usted ha impreso el código a cabo. Es más difícil inferir cuáles son estos argumentos, ya que no tienes acceso a la firma del método.

Con la interfaz de fluido, esto es más claro:

sizer.FromImage(i) 
.ToLocation(o) 
.ReduceByPercent(x) 
.OutputImageFormat(ImageFormat.Jpeg) 
.Save(); 

Además, el orden de los métodos no es importante. Esto es equivalente:

sizer.FromImage(i) 
.ReduceByPercent(x) 
.OutputImageFormat(ImageFormat.Jpeg) 
.ToLocation(o) 
.Save(); 

Además, tal vez usted podría tener valores por defecto para el formato de imagen de salida, y la reducción, por lo que esto podría convertirse en:

sizer.FromImage(i) 
.ToLocation(o) 
.Save(); 

esto requeriría sobrecarga de constructores para lograr el mismo efecto.

+0

Por lo tanto, una de las ventajas de una interfaz fluida es que garantiza un código legible (mientras que una interfaz convencional podría ser más sobre la usabilidad de la interfaz/api). – M4N

8

Yo diría que las interfaces fluidas son un poco exageradas y creo que ha escogido solo un ejemplo.

Encuentro las interfaces fluidas particularmente fuertes cuando construyes un modelo complejo con él. Con el modelo me refiero, por ejemplo. una relación compleja de objetos instanciados. La interfaz fluida es entonces una manera de guiar al desarrollador para construir correctamente instancias del modelo semántico. Dicha interfaz fluida es entonces una excelente manera de separar la mecánica y las relaciones de un modelo de la "gramática" que utiliza para construir el modelo, esencialmente protegiendo los detalles del usuario final y reduciendo los verbos disponibles tal vez solo los relevantes en un escenario particular.

Su ejemplo parece un poco exagerado.

Últimamente he hecho una interfaz fluida en la parte superior del SplitterContainer de Windows Forms. Podría decirse que el modelo semántico de una jerarquía de controles es algo complejo para construir correctamente. Al proporcionar una API pequeña y fluida, un desarrollador ahora puede expresar de manera declarativa cómo debería funcionar su SplitterContainer. Uso va como

var s = new SplitBoxSetup(); 
s.AddVerticalSplit() 
.PanelOne().PlaceControl(()=> new Label()) 
.PanelTwo() 
.AddHorizontalSplit() 
.PanelOne().PlaceControl(()=> new Label()) 
.PanelTwo().PlaceControl(()=> new Panel()); 
form.Controls.Add(s.TopControl); 

ahora he reducido la compleja mecánica de la jerarquía de control a un par de verbos que son relevantes para el tema en cuestión.

Esperanza esto ayuda

2

Debe leer Domain Driven Design por Eric Evans para conseguir algo de idea de por qué se considera una buena opción de diseño DSL.

El libro está lleno de buenos ejemplos, recomendaciones de mejores prácticas y patrones de diseño. Muy recomendable.

1

Es posible usar una variación en una interfaz Fluent para aplicar ciertas combinaciones de parámetros opcionales (por ejemplo, requerir que al menos un parámetro de un grupo esté presente, y requerir que si se especifica un cierto parámetro, otro parámetro debe ser omitido). Por ejemplo, uno podría proporcionar una funcionalidad similar a Enumerable.Range, pero con una sintaxis como IntRange.From (5) .Upto (19) o IntRange.From (5) .LessThan (10) .Stepby (2) o IntRange (3) .Count (19) .StepBy (17). La aplicación en tiempo de compilación de requisitos de parámetros excesivamente complejos puede requerir la definición de un número molesto de estructuras o clases de valores intermedios, pero el enfoque puede en algunos casos resultar útil en casos más simples.

0

En relación con la sugerencia de s con respecto a la flexibilidad de una interfaz fluida cuando se añade una nueva operación:

Si necesitábamos para añadir una nueva operación, como Pixalize() y, a continuación, en el '@sam-saffron método con múltiples escenario de parámetros, esto requeriría un nuevo parámetro para ser agregado a la firma del método. Esto puede requerir una modificación en cada invocación de este método a través de la base de código para agregar un valor para este nuevo parámetro (a menos que el idioma en uso permita un parámetro opcional).

Por lo tanto, un posible beneficio de una interfaz fluida es limitar el impacto de los cambios futuros.