El encadenamiento de métodos es la práctica de métodos de objetos que devuelven el objeto en sí mismo para que el resultado se llame a otro método. Me gusta esto:
participant.addSchedule(events[1]).addSchedule(events[2]).setStatus('attending').save()
Esto parece ser considerado una buena práctica, ya que produce código legible o una "interfaz fluida". Sin embargo, para mí, en cambio, parece romper la notación de llamada de objeto implícita en la orientación del objeto en sí misma: el código resultante no representa la realización de acciones en el resultado de un método anterior, que es cómo generalmente se espera que funcione el código orientado a objetos:
participant.getSchedule('monday').saveTo('monnday.file')
Esta diferencia se las arregla para crear dos significados diferentes para la notación de punto de "llamar al objeto resultante": en el contexto de encadenamiento, el ejemplo anterior se leería como guardar el objeto participante , a pesar de que el ejemplo tiene la intención de guardar la programación objeto recibido por getSchedule.
Entiendo que la diferencia aquí es si se debe esperar que el método llamado devuelva algo o no (en cuyo caso devolvería el objeto llamado en sí mismo para encadenarlo). Pero estos dos casos no se distinguen de la notación en sí, solo de la semántica de los métodos que se llaman. Cuando no se usa el método de encadenamiento, siempre puedo saber que una llamada de método opera en algo relacionado con el resultado de la llamada anterior; con el encadenamiento, esta suposición se rompe y tengo que procesar semánticamente toda la cadena para comprender cuál es el objeto real llamado realmente es. Por ejemplo:
participant.attend(event).setNotifications('silent').getSocialStream('twitter').postStatus('Joining '+event.name).follow(event.getSocialId('twitter'))
Allí, las dos últimas llamadas al método se refieren al resultado de getSocialStream, mientras que las anteriores se refieren al participante. Tal vez sea una mala práctica escribir cadenas donde el contexto cambia (¿es así?), Pero incluso entonces tendrá que verificar constantemente si las cadenas de puntos que se parecen son en realidad dentro del mismo contexto, o solo funcionan en el resultado .
Para mí, parece que si bien el método de encadenamiento superficial produce código legible, sobrecargar el significado de la notación de puntos solo genera más confusión. Como no me considero un gurú de la programación, supongo que la culpa es mía. Entonces: ¿qué me estoy perdiendo? ¿Entiendo el método de encadenamiento de alguna manera incorrecto? ¿Hay algunos casos en los que el encadenamiento de métodos es especialmente bueno o algunos en los que es especialmente malo?
Nota al margen: Entiendo que esta pregunta podría leerse como una declaración de opinión enmascarada como una pregunta. Sin embargo, no lo es: realmente quiero entender por qué el encadenamiento se considera una buena práctica, y dónde me equivoco al pensar que rompe la notación inherente orientada a objetos.
fuente
.
que ignorara cualquier valor de retorno de mehtod y siempre invocara cualquier método encadenado usando el mismo objeto.Respuestas:
Estoy de acuerdo en que esto es subjetivo. En su mayor parte, evito el encadenamiento de métodos, pero recientemente también encontré un caso en el que era justo lo correcto: tenía un método que aceptaba algo así como 10 parámetros y necesitaba más, pero durante la mayor parte del tiempo solo tenía que especificar un pocos. Con las anulaciones esto se volvió muy engorroso muy rápido. En cambio, opté por el enfoque de encadenamiento:
El método de encadenamiento de métodos era opcional, pero facilitaba la escritura de código (especialmente con IntelliSense). Tenga en cuenta que este es un caso aislado y no es una práctica general en mi código.
El punto es que, en el 99% de los casos, probablemente pueda hacerlo igual o mejor sin encadenar el método. Pero existe el 1% donde este es el mejor enfoque.
fuente
P = MyObject.GetParamsObj().SomeParameter(asdasd).SomeOtherParameter(asdasd); Obj = MyObject.Start(); MyObject.Execute(P);
. Tiene la ventaja de poder reutilizar este objeto de parámetro en otras llamadas, lo cual es una ventajaPizzaBuilder.AddSauce().AddDough().AddTopping()
más referencias aquílist.add(someItem)
, lo leí como "este código se agregasomeItem
allist
objeto". así que cuando leoPizzaBuilder.AddSauce()
, leo esto naturalmente como "este código está agregando salsa alPizzaBuilder
objeto". En otras palabras, veo al orquestador (el que hace la adición) como el método en el que se incrusta el códigolist.add(someItem)
oPizzaBuilder.addSauce()
. Pero creo que el ejemplo de PizzaBuilder es un poco artificial. Tu ejemplo deMyObject.SpecifySomeParameter(asdasd)
trabajos funciona bien para mí.Solo mis 2 centavos;
El encadenamiento de métodos dificulta la depuración: - No puede poner el punto de interrupción en un punto conciso, por lo que puede pausar el programa exactamente donde lo desea - Si uno de estos métodos arroja una excepción y obtiene un número de línea, no tiene idea qué método en la "cadena" causó el problema.
Creo que generalmente es una buena práctica escribir siempre líneas muy cortas y concisas. Cada línea solo debe hacer una llamada al método. Prefiere más líneas a líneas más largas.
EDITAR: el comentario menciona que el método de encadenamiento y salto de línea están separados. Eso es verdad. Sin embargo, dependiendo del depurador, puede o no ser posible colocar un punto de interrupción en el medio de una declaración. Incluso si puede, el uso de líneas separadas con variables intermedias le brinda mucha más flexibilidad y una gran cantidad de valores que puede examinar en la ventana Watch que ayuda al proceso de depuración.
fuente
Personalmente, prefiero encadenar métodos que solo actúan sobre el objeto original, por ejemplo, establecer varias propiedades o llamar a métodos de tipo utilidad.
No lo uso cuando uno o más de los métodos encadenados devolverían cualquier objeto que no sea foo en mi ejemplo. Mientras que sintácticamente puede encadenar cualquier cosa siempre que esté utilizando la API correcta para ese objeto en la cadena, cambiar los objetos en mi humilde opinión hace que las cosas sean menos legibles y puede ser realmente confuso si las API para los diferentes objetos tienen alguna similitud. Si usted hace algo de llamada a un método muy común en el extremo (
.toString()
,.print()
, lo que sea) el objeto que está en última instancia, que actúan sobre? Alguien que lee el código casualmente podría no darse cuenta de que sería un objeto devuelto implícitamente en la cadena en lugar de la referencia original.Encadenar diferentes objetos también puede conducir a errores nulos inesperados. En mis ejemplos, suponiendo que foo es válido, todas las llamadas a métodos son "seguras" (por ejemplo, válidas para foo). En el ejemplo del OP:
... no hay garantía (como desarrollador externo que mira el código) de que getSchedule realmente devuelva un objeto de programación válido y no nulo. Además, la depuración de este estilo de código suele ser mucho más difícil, ya que muchos IDE no evaluarán la llamada al método en el momento de la depuración como un objeto que puede inspeccionar. En mi opinión, cada vez que necesite un objeto para inspeccionar con fines de depuración, prefiero tenerlo en una variable explícita.
fuente
Participant
no tenga un método válidoSchedule
, elgetSchedule
método está diseñado para devolver unMaybe(of Schedule)
tipo y elsaveTo
método está diseñado para aceptar unMaybe
tipo.Martin Fowler tiene una buena discusión aquí:
fuente
En mi opinión, el método de encadenamiento es una novedad. Claro, se ve genial, pero no veo ninguna ventaja real en ello.
Como es:
mejor que:
La excepción podría ser cuando addObject () devuelve un nuevo objeto, en cuyo caso el código desencadenado puede ser un poco más engorroso como:
fuente
Es peligroso porque puede depender de más objetos de los esperados, como entonces su llamada devuelve una instancia de otra clase:
Daré un ejemplo:
foodStore es un objeto que se compone de muchas tiendas de alimentos que posee. foodstore.getLocalStore () devuelve un objeto que contiene información sobre la tienda más cercana al parámetro. getPriceforProduct (cualquier cosa) es un método de ese objeto.
Entonces, cuando llamas a foodStore.getLocalStore (parámetros) .getPriceforProduct (cualquier cosa)
no solo depende de FoodStore como lo hace, sino también de LocalStore.
Si getPriceforProduct (cualquier cosa) cambia alguna vez, debe cambiar no solo FoodStore sino también la clase que llamó al método encadenado.
Siempre debe apuntar a un acoplamiento flojo entre clases.
Dicho esto, personalmente me gusta encadenarlos cuando programo Ruby.
fuente
Muchos utilizan el encadenamiento de métodos como una forma de conveniencia en lugar de tener en cuenta cualquier preocupación de legibilidad. El encadenamiento de métodos es aceptable si implica realizar la misma acción en el mismo objeto, pero solo si realmente mejora la legibilidad, y no solo por escribir menos código.
Desafortunadamente, muchos utilizan el método de encadenamiento según los ejemplos dados en la pregunta. Mientras que pueden todavía resultar legibles, que están causando por desgracia alto acoplamiento entre varias clases, lo que no es deseable.
fuente
Esto parece un poco subjetivo.
El método de encadenamiento no es algo inherentemente malo o bueno.
La legibilidad es lo más importante.
(También considere que tener un gran número de métodos encadenados hará que las cosas sean muy frágiles si algo cambia)
fuente
Beneficios del encadenamiento
, es decir, donde me gusta usarlo
Un beneficio del encadenamiento que no vi mencionado fue la capacidad de usarlo durante el inicio variable, o al pasar un nuevo objeto a un método, no estoy seguro de si es una mala práctica o no.
Sé que este es un ejemplo artificial, pero digamos que tiene las siguientes clases
Y diga que no tiene acceso a la clase base, o diga que los valores predeterminados son dinámicos, basados en el tiempo, etc. los valores de un método:
Pero, ¿no es esto mucho más fácil de leer?
O, ¿qué pasa con un miembro de la clase?
vs
Así es como he estado usando el encadenamiento, y típicamente mis métodos son solo para la configuración, por lo que solo tienen 2 líneas de largo, establezca un valor, entonces
Return Me
. Para nosotros, ha limpiado grandes líneas muy difíciles de leer y entender el código en una línea que se lee como una oración. algo comoVs algo como
Detrimento del encadenamiento
, es decir, donde no me gusta usarlo
No uso el encadenamiento cuando hay muchos parámetros para pasar a las rutinas, principalmente porque las líneas se alargan mucho y, como mencionó el OP, puede ser confuso cuando se llaman rutinas a otras clases para pasar a una de las Los métodos de encadenamiento.
También existe la preocupación de que una rutina devuelva datos no válidos, hasta ahora solo he usado encadenamiento cuando devuelvo la misma instancia que se llama. Como se señaló si encadena entre clases, dificulta la depuración (¿cuál devuelve nulo?) Y puede aumentar el acoplamiento de dependencias entre clases.
Conclusión
Como todo en la vida y la programación, encadenar no es bueno ni malo si puedes evitar lo malo, entonces encadenar puede ser un gran beneficio.
Intento seguir estas reglas.
fuente
El encadenamiento de métodos puede permitir diseñar DSL avanzadas en Java directamente. En esencia, puede modelar al menos estos tipos de reglas DSL:
Estas reglas pueden implementarse usando estas interfaces
Con estas reglas simples, puede implementar DSL complejos como SQL directamente en Java, como lo hace jOOQ , una biblioteca que he creado. Vea un ejemplo de SQL bastante complejo tomado de mi blog aquí:
Otro buen ejemplo es jRTF , un pequeño DSL diseñado para cementar documentos RTF directamente en Java. Un ejemplo:
fuente
El encadenamiento de métodos puede ser simplemente una novedad para la mayoría de los casos, pero creo que tiene su lugar. Se puede encontrar un ejemplo en el uso del registro activo de CodeIgniter :
Eso se ve mucho más limpio (y tiene más sentido, en mi opinión) que:
Realmente es subjetivo; Cada uno tiene su propia opinión.
fuente
Creo que la falacia principal es pensar que este es un enfoque orientado a objetos en general cuando, de hecho, es más un enfoque de programación funcional que cualquier otra cosa.
Las principales razones por las que lo uso son para la legibilidad y para evitar que mi código se vea inundado por variables.
Realmente no entiendo de qué están hablando los demás cuando dicen que daña la legibilidad. Es una de las formas de programación más concisas y coherentes que he usado.
También esto:
convertTextToVoice.LoadText ("source.txt"). ConvertToVoice ("destination.wav");
así es como lo usaría típicamente. Usarlo para encadenar x número de parámetros no es como lo uso típicamente. Si quisiera poner x número de parámetros en una llamada al método, usaría la sintaxis de params :
public void foo (params object [] items)
Y convierta los objetos según el tipo o simplemente use una matriz o colección de tipos de datos según su caso de uso.
fuente
Estoy de acuerdo, por lo tanto, cambié la forma en que se implementó una interfaz fluida en mi biblioteca.
Antes de:
Después:
En la implementación "antes", las funciones modificaron el objeto y terminaron en
return this
. Cambié la implementación para devolver un nuevo objeto del mismo tipo .Mi razonamiento para este cambio :
El valor de retorno no tenía nada que ver con la función, estaba simplemente allí para soportar la parte de encadenamiento. Debería haber sido una función nula según OOP.
El encadenamiento de métodos en las bibliotecas del sistema también lo implementa de esa manera (como linq o string):
El objeto original permanece intacto, lo que permite al usuario de la API decidir qué hacer con él. Permite:
Una implementación de copia también se puede utilizar para construir objetos:
Donde la
setBackground(color)
función cambia la instancia y no devuelve nada (como se supone que debe hacerlo) .El comportamiento de las funciones es más predecible (ver puntos 1 y 2).
El uso de un nombre de variable corto también puede reducir el desorden de código, sin forzar una API en el modelo.
Conclusión:
en mi opinión, una interfaz fluida que utiliza una
return this
implementación es simplemente incorrecta.fuente
return this
administrar o no. Es mi opinión que obtiene una API fluida de una manera "no natural". (Se agregó la razón 6: para mostrar una alternativa no fluida, que no tiene la funcionalidad de sobrecarga / agregada)El punto totalmente perdido aquí es que el método de encadenamiento permite DRY . Es un sustituto efectivo para el "con" (que está mal implementado en algunos idiomas).
Esto importa por la misma razón que DRY siempre importa; Si A resulta ser un error y estas operaciones deben realizarse en B, solo necesita actualizar en 1 lugar, no en 3.
Pragmáticamente, la ventaja es pequeña en este caso. Aún así, un poco menos escribiendo, un poco más robusto (SECO), lo tomaré.
fuente
Generalmente odio el método de encadenamiento porque creo que empeora la legibilidad. La compacidad a menudo se confunde con la legibilidad, pero no son los mismos términos. Si haces todo en una sola declaración, entonces eso es compacto, pero es menos legible (más difícil de seguir) la mayoría de las veces que hacerlo en varias declaraciones. Como notó, a menos que no pueda garantizar que el valor de retorno de los métodos utilizados sea el mismo, el encadenamiento de métodos será una fuente de confusión.
1.)
vs
2.)
vs
3.)
vs
Como puede ver, gana casi nada, porque debe agregar saltos de línea a su declaración única para que sea más legible y tiene que agregar sangría para dejar en claro que está hablando de diferentes objetos. Bueno, si quisiera usar un lenguaje basado en la ideación, aprendería Python en lugar de hacerlo, sin mencionar que la mayoría de los IDE eliminarán la sangría formateando automáticamente el código.
Creo que el único lugar donde este tipo de encadenamiento puede ser útil es canalizar secuencias en la CLI o unir múltiples consultas juntas en SQL. Ambos tienen un precio por múltiples declaraciones. Pero si desea resolver problemas complejos, terminará incluso con aquellos que pagan el precio y escriben el código en múltiples declaraciones utilizando variables o escribiendo scripts de bash y procedimientos almacenados o vistas.
A partir de las interpretaciones DRY: "Evite la repetición del conocimiento (no la repetición del texto)". y "Escriba menos, ni siquiera repita textos", el primero lo que el principio significa realmente, pero el segundo es un malentendido común porque muchas personas no pueden entender una mierda demasiado complicada como "Todo conocimiento debe tener una sola, inequívoca, representación autorizada dentro de un sistema ". El segundo es la compacidad a toda costa, que se rompe en este escenario, porque empeora la legibilidad. La primera interpretación se divide por DDD cuando copia código entre contextos limitados, porque el acoplamiento flexible es más importante en ese escenario.
fuente
El bueno:
El malo:
General:
Un buen enfoque es no utilizar el encadenamiento en general hasta que surjan situaciones o módulos específicos serían particularmente adecuados para ello.
El encadenamiento puede dañar gravemente la legibilidad en algunos casos, especialmente cuando se pesan en los puntos 1 y 2.
En caso de acusaciones, se puede utilizar de forma incorrecta, como en lugar de otro enfoque (pasar una matriz, por ejemplo) o mezclar métodos de formas extrañas (parent.setSomething (). GetChild (). SetSomething (). GetParent (). SetSomething ()).
fuente
Respuesta Opinión
El mayor inconveniente del encadenamiento es que puede ser difícil para el lector comprender cómo cada método afecta al objeto original, si lo hace, y qué tipo devuelve cada método.
Algunas preguntas:
La depuración, en la mayoría de los idiomas, puede ser más difícil de encadenar. Incluso si cada paso de la cadena está en su propia línea (lo que de alguna manera frustra el propósito del encadenamiento), puede ser difícil inspeccionar el valor devuelto después de cada paso, especialmente para los métodos no mutantes.
Los tiempos de compilación pueden ser más lentos según el idioma y el compilador, ya que las expresiones pueden ser mucho más complejas de resolver.
Creo que, como con todo, encadenar es una buena solución que puede ser útil en algún escenario. Debe usarse con precaución, comprender las implicaciones y limitar el número de elementos de la cadena a unos pocos.
fuente
En los idiomas escritos (que carecen
auto
o equivalentes) esto evita que el implementador tenga que declarar los tipos de resultados intermedios.Para cadenas más largas, puede estar tratando con varios tipos intermedios diferentes, necesitaría declarar cada uno de ellos.
Creo que este enfoque realmente se desarrolló en Java, donde a) todas las llamadas a funciones son invocaciones de funciones miembro, yb) se requieren tipos explícitos. Por supuesto, hay una compensación aquí en términos, perdiendo algo de claridad, pero en algunas situaciones algunas personas encuentran que vale la pena.
fuente