Recientemente vi una base de código que tenía una clase de datos Address
definida en algún lugar y luego en un lugar diferente:
fun Address.toAnschrift() = let { address ->
Anschrift().apply {
// mapping code here...
}
}
Me pareció confuso no tener este método en la dirección directamente. ¿Existen patrones establecidos o mejores prácticas para utilizar los métodos de extensión? ¿O el libro sobre eso todavía tiene que ser escrito?
Tenga en cuenta que a pesar de la sintaxis de Kotlin en mi ejemplo, estoy interesado en las mejores prácticas generales, ya que también se aplicaría a C # u otros lenguajes con esas capacidades.
Respuestas:
Los métodos de extensión no proporcionan algo que no se puede hacer de otras maneras. Son azúcar sintáctico para hacer que el desarrollo sea más agradable, sin proporcionar un beneficio técnico real.
Los métodos de extensión son relativamente nuevos. Los estándares y las convenciones generalmente se deciden por opinión popular (además de cierta experiencia a largo plazo sobre lo que termina siendo una mala idea), y no creo que estemos en el punto en el que podamos trazar una línea definitiva con la que todos estén de acuerdo.
Sin embargo, puedo ver varios argumentos, basados en las cosas que escuché de mis compañeros de trabajo.
1. Generalmente están reemplazando los métodos estáticos.
Los métodos de extensión se basan más comúnmente en cosas que de otro modo habrían sido métodos estáticos, no métodos de clase. Ellos se ven como métodos de clase, pero no lo son en realidad.
Los métodos de extensión simplemente no deberían considerarse una alternativa para los métodos de clase; sino más bien como una forma de dar a los métodos estáticos de manera sintáctica una apariencia y sensación de "clase de método".
2. Cuando su método previsto es específico para un proyecto consumidor pero no la biblioteca fuente.
El hecho de que desarrolle tanto la biblioteca como el consumidor no significa que esté dispuesto a poner la lógica donde sea que encaje.
Por ejemplo:
PersonDto
clase proveniente de tuDataLayer
proyecto.WebUI
proyecto quiere poder convertirPersonDto
a aPersonViewModel
.No puede (y no querría) agregar este método de conversión a su
DataLayer
proyecto. Dado que este método tiene un alcance para elWebUI
proyecto, debe vivir dentro de ese proyecto.Un método de extensión le permite tener este método accesible globalmente dentro de su proyecto (y posibles consumidores de su proyecto), sin requerir que la
DataLayer
biblioteca lo implemente.3. Si cree que las clases de datos solo deben contener datos.
Por ejemplo, su
Person
clase contiene las propiedades de una persona. Pero desea poder tener algunos métodos que formateen algunos de los datos:He visto muchos compañeros de trabajo que odian la idea de mezclar datos y lógica en una clase. No estoy del todo de acuerdo con cosas simples como formatear datos, pero reconozco que en el ejemplo anterior, se siente sucio
Person
tener que dependerSecurityQuestionAnswers
.Al poner este método en un método de extensión, se evita el ensuciamiento de la clase de datos pura.
Tenga en cuenta que este argumento es de estilo. Esto es similar a las clases parciales. Hacerlo tiene poco o ningún beneficio técnico, pero le permite separar el código en varios archivos si lo considera más limpio (por ejemplo, si muchas personas usan la clase pero no se preocupan por sus métodos personalizados adicionales).
4. Métodos auxiliares
En la mayoría de los proyectos, tiendo a terminar con clases de ayuda. Estas son clases estáticas que generalmente proporcionan una colección de métodos para formatear fácilmente.
Por ejemplo, una empresa en la que trabajé tenía un formato de fecha y hora particular que querían usar. En lugar de tener que pegar la cadena de formato en todo el lugar, o hacer que la cadena de formato sea una variable global, opté por un método de extensión DateTime:
¿Es eso mejor que un método auxiliar estático normal? Creo que sí. Limpia la sintaxis. En vez de
Yo podría hacer:
Esto es similar a una versión fluida de la misma sintaxis. Me gusta más, a pesar de que no hay un beneficio técnico de tener uno sobre el otro.
Todos estos argumentos son subjetivos. Ninguno de ellos cubre un caso que de otra manera nunca podría estar cubierto.
Pero, de nuevo, podemos tomar su afirmación de que nunca son necesarios al extremo:
La respuesta a esta pregunta, y la suya, sigue siendo la misma:
Sin embargo, una mención extra particular:
fuente
Estoy de acuerdo con Flater en que no ha habido suficiente tiempo para que la convención se cristalice, pero sí tenemos el ejemplo de las bibliotecas de clases de .NET Framework. Tenga en cuenta que cuando los métodos LINQ se agregaron a .NET, no se agregaron directamente
IEnumerable
, sino como métodos de extensión.¿Qué podemos sacar de esto? LINQ es
A.B().C()
lugar deC(B(A))
), ySi tiene las tres características, los métodos de extensión tienen mucho sentido. De hecho, yo diría que si un conjunto de métodos tiene la función # 3 y cualquiera de las dos primeras funciones, debería considerar hacerlas como métodos de extensión.
Métodos de extensión:
null
valores de manera más flexible ya que un método de extensión llamado desde unnull
valor simplemente recibe elnull
como su primer argumento,Los métodos de extensión tienen un beneficio más: todavía se pueden usar como un método estático y se pueden pasar directamente como un valor a una función de orden superior. Entonces, si
MyMethod
es un método de extensión, en lugar de decir,myList.Select(x => x.MyMethod())
simplemente podría usarmyList.Select(MyMethod)
. Eso me parece más agradable.fuente
La fuerza impulsora detrás de los métodos de extensión es la capacidad de agregar la funcionalidad que necesita a todas las clases o interfaces de un determinado tipo, independientemente de si están selladas o en otro conjunto. Puede ver evidencia de esto con el código LINQ, agregando funciones a todos los
IEnumerable<T>
objetos, ya sean listas o pilas, etc. El concepto era probar las funciones en el futuro para que si se le ocurriera el suyoIEnumerable<T>
pudiera usar LINQ automáticamente con él.Dicho esto, la función de idioma es lo suficientemente nueva como para que no tenga pautas sobre cuándo usarlas o no. Sin embargo, sí permiten varias cosas que antes no eran posibles:
Para ser justos, solo el tiempo dirá qué tan lejos está demasiado lejos, o en qué punto los métodos de extensión se convierten en un obstáculo en lugar de una bendición. Como se mencionó en otra respuesta, no hay nada en un método de extensión que no se pueda hacer con un método estático. Sin embargo, cuando se usan juiciosamente, pueden hacer que el código sea más fácil de leer.
¿Qué pasa con el uso intencional de métodos de extensiones en el mismo ensamblado?
Creo que es valioso tener una funcionalidad central bien probada y confiable, y luego no jugar con ella hasta que sea absolutamente necesario. El nuevo código que depende de él, pero necesita que brinde funcionalidad únicamente para el beneficio del nuevo código, puede usar los métodos de extensión para agregar cualquier característica que admita el nuevo código. Esas funciones solo son de interés para el nuevo código, y el alcance de los métodos de extensión se limita al espacio de nombres en el que se declaran.
No descartaría ciegamente el uso de métodos de extensión, pero consideraría si la nueva funcionalidad realmente debería agregarse al código central existente que está bien probado.
fuente
Una de las razones más comunes para usar métodos de extensión es como un medio de "extender, no modificar" las interfaces. En lugar de tener
IFoo
yIFoo2
, donde este último introduce un nuevo métodoBar
,Bar
se agrega aIFoo
través de un método de extensión.Una de las razones por las que Linq usa métodos de extensión
Where
,Select
etc. es para permitir a los usuarios definir sus propias versiones de estos métodos, que luego son utilizados por la sintaxis de consulta de Linq también.Más allá de eso, el enfoque que uso, que parece ajustarse a lo que comúnmente veo en .NET framework al menos es:
Así que si tengo una
Option<T>
, me gustaría poner cosas comoHasValue
,Value
,==
, etc dentro del tipo en sí. Pero algo así como unaMap
función, que convierte la mónada deT1
aT2
, pondría un método de extensión:fuente
One of the reasons Linq uses extension methods for Where, Select etc is in order to allow users to define their own versions of these methods, which are then used by the Linq query syntax too.
-- ¿Estás seguro de eso? La sintaxis de Linq hace un uso extensivo de las expresiones lambda (es decir, funciones de primera clase), por lo que no es necesario escribir sus propias versiones deSelect
yWhere
. No conozco casos en los que las personas estén lanzando sus propias versiones de estas funciones.if (!predicate(item))
código en eseif (predicate(item))
, los resultados cambian a medida que usa mi versión deWhere
.