En muchos aspectos, me gusta mucho la idea de interfaces fluidas, pero con todas las características modernas de C # (inicializadores, lambdas, parámetros con nombre) me encuentro pensando, "¿vale la pena?" Y "¿Es este el patrón correcto para ¿utilizar?". ¿Podría alguien darme, si no una práctica aceptada, al menos su propia experiencia o matriz de decisión sobre cuándo usar el patrón Fluido?
Conclusión:
Algunas buenas reglas generales de las respuestas hasta ahora:
- Las interfaces fluidas ayudan enormemente cuando tiene más acciones que los configuradores, ya que las llamadas se benefician más del traspaso de contexto.
- Las interfaces fluidas deben considerarse como una capa sobre la parte superior de una API, no el único medio de uso.
- Las características modernas, como lambdas, inicializadores y parámetros con nombre, pueden funcionar de la mano para hacer que una interfaz fluida sea aún más amigable.
Aquí hay un ejemplo de lo que quiero decir con las características modernas que lo hacen sentir menos necesario. Tomemos, por ejemplo, una interfaz fluida (quizás un mal ejemplo) que me permite crear un empleado como:
Employees.CreateNew().WithFirstName("Peter")
.WithLastName("Gibbons")
.WithManager()
.WithFirstName("Bill")
.WithLastName("Lumbergh")
.WithTitle("Manager")
.WithDepartment("Y2K");
Podría escribirse fácilmente con inicializadores como:
Employees.Add(new Employee()
{
FirstName = "Peter",
LastName = "Gibbons",
Manager = new Employee()
{
FirstName = "Bill",
LastName = "Lumbergh",
Title = "Manager",
Department = "Y2K"
}
});
También podría haber utilizado parámetros con nombre en los constructores en este ejemplo.
Respuestas:
Escribir una interfaz fluida (he incursionado en ello) requiere más esfuerzo, pero tiene una recompensa porque si lo haces bien, la intención del código de usuario resultante es más obvia. Es esencialmente una forma de lenguaje específico de dominio.
En otras palabras, si su código se lee mucho más de lo que está escrito (¿y qué código no lo es?), Entonces debería considerar crear una interfaz fluida.
Las interfaces fluidas tienen más que ver con el contexto, y son mucho más que simples formas de configurar objetos. Como puede ver en el enlace de arriba, utilicé una API con fluidez para lograr:
objectA.
intellisense le da muchas pistas. En mi caso anterior,plm.Led.
le da todas las opciones para controlar el LED incorporado, yplm.Network.
le da las cosas que puede hacer con la interfaz de red.plm.Network.X10.
Le da el subconjunto de acciones de red para dispositivos X10. No obtendrá esto con inicializadores de constructor (a menos que desee tener que construir un objeto para cada tipo diferente de acción, lo que no es idiomático).Una cosa que normalmente hago es:
No veo cómo puedes hacer algo así sin una interfaz fluida.
Edición 2 : También puede realizar mejoras de legibilidad realmente interesantes, como:
fuente
Scott Hanselman habla sobre esto en el Episodio 260 de su podcast Hanselminutes con Jonathan Carter. Explican que una interfaz fluida es más como una interfaz de usuario en una API. No debe proporcionar una interfaz fluida como único punto de acceso, sino proporcionarla como una especie de interfaz de usuario de código encima de la "interfaz API normal".
Jonathan Carter también habla un poco sobre el diseño de API en su blog .
fuente
Las interfaces fluidas son características muy poderosas para proporcionar dentro del contexto de su código, cuando se pone en práctica con el razonamiento "correcto".
Si su objetivo es simplemente crear cadenas de código de una línea masivas como una especie de pseudo-caja negra, entonces probablemente esté ladrando el árbol equivocado. Si, por otro lado, lo está utilizando para agregar valor a su interfaz API al proporcionar un medio para encadenar llamadas de método y mejorar la legibilidad del código, entonces con mucha buena planificación y esfuerzo, creo que el esfuerzo vale la pena.
Evitaría seguir lo que parece convertirse en un "patrón" común al crear interfaces fluidas, donde nombras todos tus métodos fluidos "con" -algo, ya que roba una interfaz API potencialmente buena de su contexto y, por lo tanto, de su valor intrínseco. .
La clave es pensar en la sintaxis fluida como una implementación específica de un lenguaje específico de dominio. Como un muy buen ejemplo de lo que estoy hablando, eche un vistazo a StoryQ, que emplea la fluidez como un medio para expresar un DSL de una manera muy valiosa y flexible.
fuente
position.withX(5)
Versusposition.getDistanceToOrigin()
Nota inicial: estoy en desacuerdo con una suposición en la pregunta, y extraigo mis conclusiones específicas (al final de esta publicación). Debido a que esto probablemente no sea una respuesta completa que abarque, estoy marcando esto como CW.
En mi opinión, estas dos versiones deberían significar y hacer cosas diferentes.
A diferencia de la versión no fluida, la versión fluida oculta el hecho de que lo nuevo
Employee
también seAdd
edita en la colecciónEmployees
; solo sugiere que un nuevo objeto esCreate
d.El significado de
….WithX(…)
es ambiguo, especialmente para las personas que provienen de F #, que tiene unawith
palabra clave para las expresiones de objeto : podrían interpretarobj.WithX(x)
como un nuevo objeto derivado delobj
que es idéntico aobj
excepción de suX
propiedad, cuyo valor esx
. Por otro lado, con la segunda versión, está claro que no se crean objetos derivados y que todas las propiedades se especifican para el objeto original.Esto
….With…
tiene otro significado: cambiar el "foco" de la inicialización de la propiedad a un objeto diferente. El hecho de que su API fluida tenga dos significados diferentesWith
hace que sea difícil interpretar correctamente lo que está sucediendo ... y tal vez es por eso que usó la sangría en su ejemplo para demostrar el significado deseado de ese código. Sería más claro así:Conclusiones: "Ocultar" una función de lenguaje suficientemente simple
new T { X = x }
, con una API fluida (Ts.CreateNew().WithX(x)
) obviamente se puede hacer, pero:Se debe tener cuidado de que los lectores del código fluido resultante todavía entiendan exactamente lo que hace. Es decir, la API fluida debe ser transparente en significado y sin ambigüedades. El diseño de una API de este tipo puede ser más difícil de lo previsto (es posible que deba probarse para facilitar su uso y aceptación), y / o ...
diseñarlo podría ser más trabajo del necesario: en este ejemplo, la API fluida agrega muy poca "comodidad del usuario" sobre la API subyacente (una función de lenguaje). Se podría decir que una API fluida debería hacer que la característica subyacente de API / lenguaje sea "más fácil de usar"; es decir, debería ahorrarle al programador una cantidad considerable de esfuerzo. Si es solo otra forma de escribir lo mismo, probablemente no valga la pena, porque no hace que la vida del programador sea más fácil, sino que solo hace que el trabajo del diseñador sea más difícil (vea la conclusión # 1 arriba).
Ambos puntos anteriores suponen silenciosamente que la API fluida es una capa sobre una API existente o una función de idioma. Esta suposición puede ser otra buena guía: una API fluida puede ser una forma adicional de hacer algo, no la única. Es decir, podría ser una buena idea ofrecer una API fluida como una opción "opt-in".
fuente
Me gusta el estilo fluido, expresa la intención muy claramente. Con el ejemplo de inicializador de objetos que tiene después, debe tener definidores de propiedad pública para usar esa sintaxis, no con el estilo fluido. Dicho esto, con tu ejemplo no ganas mucho sobre los setters públicos porque casi has optado por un estilo de método set / get java-esque.
Lo que me lleva al segundo punto, no estoy seguro de si usaría el estilo fluido de la manera que lo ha hecho, con muchos establecedores de propiedades, probablemente usaría la segunda versión para eso, lo encuentro mejor cuando tienen muchos verbos para encadenar, o al menos muchas cosas en lugar de configuraciones.
fuente
No estaba familiarizado con el término interfaz fluida , pero me recuerda un par de API que he usado, incluido LINQ .
Personalmente, no veo cómo las características modernas de C # evitarían la utilidad de este enfoque. Prefiero decir que van de la mano. Por ejemplo, es aún más fácil lograr dicha interfaz mediante el uso de métodos de extensión .
Quizás aclare su respuesta con un ejemplo concreto de cómo se puede reemplazar una interfaz fluida utilizando una de las características modernas que mencionó.
fuente