paint-brush
Cómo solucionar problemas de Kotln al tratar con objetivos de sitios de usopor@jesperancinha
106 lecturas

Cómo solucionar problemas de Kotln al tratar con objetivos de sitios de uso

por João Esperancinha10m2024/03/21
Read on Terminal Reader
Read this story w/o Javascript

Demasiado Largo; Para Leer

Por qué y dónde deberíamos usar objetivos de sitio de uso en Kotlin y en qué se diferencia eso en Kotlin cuando se usan marcos como Spring Framework.
featured image - Cómo solucionar problemas de Kotln al tratar con objetivos de sitios de uso
João Esperancinha HackerNoon profile picture
0-item
1-item
2-item
3-item


Uno de los mayores puntos fuertes de Kotlin también puede ser su debilidad. Cuando hoy hablamos de clases de datos, y de la misma manera de registros java, tendemos a centrarnos, al menos mucha gente que conozco, en la eliminación completa del código repetitivo. El código repetitivo ha sido algo que ha molestado a los desarrolladores durante años. Existe no sólo en Java, sino que también lo tienen otros lenguajes. Si pensamos en crear objetos de acceso a datos de cualquier forma, realmente no importa si lo hacemos en Kotlin o Java o cualquier otro lenguaje.


Frecuentemente repetimos el mismo proceso: creación del repositorio, servicio y controlador. Mucho de lo que hacemos sigue siendo repetitivo. Sin embargo, el código repetitivo puede haber sido parte de la decisión de crear clases de datos o registros Java, un paradigma más importante de la ingeniería de software fue el punto focal real de la creación de este tipo de estructuras de datos.


Todo se reduce a la inmutabilidad o, en cualquier caso, a la posibilidad de la misma. Retrocedamos en el tiempo y retrocedamos a 1995, donde comenzamos en algún momento a crear estructuras de datos en las que especificaríamos cosas diferentes. Definiríamos los campos, los captadores, los definidores, las propiedades, los descriptores de acceso, los atributos y los parámetros de manera que podamos pasar nuestros argumentos a los métodos, las funciones o los constructores. Para esta parte tendremos una programación divertida utilizando los personajes de la serie de mediados de los 80: The Golden Girls. Dicho esto, veamos cómo sería una clase durante unos 15 años:


 public class GoldenGirlsJava { public String goldenGirl1; private final String goldenGirl2; private final String goldenGirl3; private final String goldenGirl4; public GoldenGirlsJava() { this.goldenGirl1 = "Dorothy Zbornak"; this.goldenGirl2 = "Rose Nylund"; this.goldenGirl3 = "Blanche Devereaux"; this.goldenGirl4 = "Sophia Petrillo"; } public GoldenGirlsJava( String goldenGirl1, String goldenGirl2, String goldenGirl3, String goldenGirl4 ) { this.goldenGirl1 = goldenGirl1; this.goldenGirl2 = goldenGirl2; this.goldenGirl3 = goldenGirl3; this.goldenGirl4 = goldenGirl4; } public String getGoldenGirl1() { return goldenGirl1; } public void setGoldenGirl1(String goldenGirl1) { this.goldenGirl1 = goldenGirl1; } public String getGoldenGirl2() { return goldenGirl2; } public String getGoldenGirl3() { return goldenGirl3; } public String getGoldenGirl4() { return goldenGirl4; } @Override public String toString() { return "GoldenGirlsJava{" + "goldenGirl1='" + goldenGirl1 + '\'' + ", goldenGirl2='" + goldenGirl2 + '\'' + ", goldenGirl3='" + goldenGirl3 + '\'' + ", goldenGirl4='" + goldenGirl4 + '\'' + '}'; } }


Esta fue una enorme cantidad de código para escribir solo para crear dos constructores. Aunque el código hash y los iguales no eran necesariamente obligatorios en todas las ocasiones, casi siempre tuvimos que implementar un constructor sin argumentos junto a un constructor más generalista. Lo bueno de crear todo este código repetitivo es que sabíamos muy claramente cómo se vería todo después de compilarlo en código de bytes.


Sin embargo, y en cualquier caso, el código repetitivo siempre fue un tema polémico para muchos desarrolladores, por lo que en 2009 apareció lombok y revolucionó la forma en que creamos estructuras de datos. Introdujo el concepto de usar un procesador de anotaciones e interpretar anotaciones específicas que darían las cualidades que necesitábamos para nuestras clases, por lo que una clase anotada lombok se vería así:

 @Getter @Setter @AllArgsConstructor @ToString public class GoldenGirlsLombok { public String goldenGirl1; private final String goldenGirl2; private final String goldenGirl3; private final String goldenGirl4; public GoldenGirlsLombok() { this.goldenGirl1 = "Dorothy Zbornak"; this.goldenGirl2 = "Rose Nylund"; this.goldenGirl3 = "Blanche Devereaux"; this.goldenGirl4 = "Sophia Petrillo"; } }


¡Y durante un tiempo la gente estuvo muy contenta con Lombok! Finalmente, el peso de tener que crear todo ese código repetitivo desapareció. Pero esa sensación duró unos 7 años, cuando llegó un nuevo jugador y esta vez fue algo que la industria del desarrollo de software no esperaba. Se llamó Kotlin y en 2016 debutó con la introducción inmediata de clases de datos. Nuestra implementación de chicas doradas en Kotlin ahora se vería así:

 data class GoldenGirls( var goldenGirl1: String = "Dorothy Zbornak", private val goldenGirl2: String = "Rose Nylund", private val goldenGirl3: String = "Blanche Devereaux", private val goldenGirl4: String = "Sophia Petrillo" )


Aunque Kotlin lentamente comenzó a ganar seguidores, Java, por otro lado, se dio cuenta de que había algo más en el mercado y algunos desarrollos en ese lado comenzaron a ganar fuerza, como el proyecto Loom, que había estado en desarrollo pero que también quedó en un segundo plano por un tiempo. Es por eso que, con el lanzamiento de Java 14 en 20202, Java introdujo registros java y las estructuras de datos en Java ahora se verían así:

 public record GoldenGirlsRecord( String goldenGirl1, String goldenGirl2, String goldenGirl3, String goldenGirl4 ) { public GoldenGirlsRecord() { this( "Dorothy Zbornak", "Rose Nylund", "Blanche Devereaux", "Sophia Petrillo" ); } }


Y hasta el día de hoy, la simplificación y reducción del código parece continuar. Sin embargo, con la reducción del código repetitivo, los conceptos de campos, captadores, definidores, propiedades, descriptores de acceso, atributos y parámetros se volvieron mucho menos visuales y menos fáciles de mapear en nuestras mentes. Nos guste o no, esos conceptos siguen siendo cómo funciona la JVM y organiza el código con el código de bytes. Pero la simplificación excesiva del código en realidad consiste en hacer que el código sea más fácil de leer y, en el caso de las clases de datos y los registros de Java, la idea también es crear estructuras de datos. que son inmutables o parcialmente inmutables.


Los registros de Java son verdaderamente inmutables en el sentido de que todos los valores que contiene o las referencias que contiene no se pueden modificar. Las clases de datos de Kotlin también pueden ser verdaderamente inmutables por las mismas razones, pero no tienen por qué serlo, lo que de alguna manera da permiso a los desarrolladores para crear código complicado y, lo peor de todo, mutable. En cualquier caso, todo esto está bien hasta que necesitemos para trabajar con marcos como Spring Framework, que dependen en gran medida de anotaciones. Aquí hay un ejemplo:

 @Entity @Table(name = "shelf_case") data class Case( @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) val id: Long?, var designation: String?, var weight: Long? ) { constructor() : this(0L, null, null) override fun toString(): String { return super.toString() } }


Este ejemplo funciona bien. Para el ojo desprevenido, no está sucediendo nada especial aquí. Esto funciona en Kotlin, de la misma manera que funcionaría en Java. Pero ahora echemos un vistazo a un ejemplo similar, pero esta vez con anotaciones de Jackson:

 data class AccountNumbersPassiveDto( @NotNull val accountNumberLong: Long?, val accountNumberNullable: Long?, @DecimalMax(value = "10") @DecimalMin(value = "5") val accountNumber: BigDecimal, val accountNumberEven: Int, val accountNumberOdd: Int, @Positive val accountNumberPositive: Int, @Negative val accountNumberNegative: Int, @DecimalMax(value = "5", groups = [LowProfile::class]) @DecimalMax(value = "10", groups = [MiddleProfile::class]) @DecimalMax(value = "15", groups = [HighProfile::class]) @DecimalMax(value = "20") val accountNumberMaxList:Int )


Este ejemplo fallará y la razón es que en Kotlin, no hay forma de decirle al compilador dónde exactamente queremos que se aplique nuestra anotación. En el ejemplo anterior con la anotación @Entity , que es una anotación de persistencia de Yakarta, las anotaciones se aplican correctamente a los campos. Si descompilamos ese código encontraremos esto:

 public final class Case { @Id @GeneratedValue( strategy = GenerationType.SEQUENCE ) @Nullable private final Long id; @Nullable private String designation; @Nullable private Long weight;


Pero para este último, que es un ejemplo de validación de Yakarta, encontramos esto:

 public final class AccountNumbersPassiveDto { @Nullable private final Long accountNumberLong; @Nullable private final Long accountNumberNullable; @NotNull private final BigDecimal accountNumber; private final int accountNumberEven; private final int accountNumberOdd;



Esto significa que AccountNumbersDto no se ha visto afectado por esas anotaciones en sus campos. Tuvimos suerte en el primer ejemplo. En realidad, también podría haber fallado. Para especificar dónde debe ir nuestra anotación, Kotlin nos brinda la posibilidad de usar destinos de sitio de uso como prefijo de cualquier anotación. El requisito es, por supuesto, que se cumplan las condiciones. En este caso, una forma de solucionar este problema es anteponer todas las anotaciones con @field de esta manera:

 data class AccountNumbersPassiveDto( @field:NotNull val accountNumberLong: Long?, val accountNumberNullable: Long?, @field:DecimalMax(value = "10") @field:DecimalMin(value = "5") val accountNumber: BigDecimal, val accountNumberEven: Int, val accountNumberOdd: Int, @field:Positive val accountNumberPositive: Int, @field:Negative val accountNumberNegative: Int, @field:DecimalMax(value = "5", groups = [LowProfile::class]) @field:DecimalMax(value = "10", groups = [MiddleProfile::class]) @field:DecimalMax(value = "15", groups = [HighProfile::class]) @field:DecimalMax(value = "20") val accountNumberMaxList:Int )


Si ahora intentamos descompilar el código de bytes resultante usando IntelliJ, encontraremos que da como resultado este código en Java:

 public final class AccountNumbersPassiveDto { @NotNull @Nullable private final Long accountNumberLong; @Nullable private final Long accountNumberNullable; @DecimalMax("10") @DecimalMin("5") @org.jetbrains.annotations.NotNull


Esto significa que las anotaciones se han aplicado al campo y que nuestro código funciona como se supone que debe hacerlo.


Probablemente puedas imaginar a estas alturas que las personas que llevan mucho tiempo trabajando en Java, no tendrán problemas en adaptarse a Kotlin porque todos sabemos qué son los campos, parámetros, propiedades, etc. Lo que observamos hoy y de lo que también soy testigo es que parece que la simplificación del código tiene el potencial de resultar contraproducente. Ha habido bastantes ocasiones en las que me he dado cuenta del tiempo dedicado a proyectos tratando de descubrir por qué en algunos proyectos o módulos las anotaciones simplemente no parecen funcionar. Y todo parece reducirse a no hacer uso de los objetivos del sitio de uso.


Mi teoría sobre lo que es más probable es que las nuevas generaciones de desarrolladores probablemente considerarán cosas que mi generación no pensó que fueran complicadas y aprendieron por instinto, como material realmente complicado. Tal es el caso de los objetivos de sitios de uso. Solían ser cosas que podíamos visualizar muy fácilmente. Sin embargo en los tiempos actuales, la confusión generada al respecto parece estar retrasando en algunas ocasiones el desarrollo de los proyectos a tiempo. Por supuesto, no podría generalizar, pero existe potencial para bien y para mal con los registros de Java y las clases de datos. Es difícil decir cómo las clases de datos y los registros darán forma a nuestro futuro, pero una cosa que está clara es que debido a esta problemática, otros frameworks están apostando por estar completamente libres de anotaciones, como es el caso de Ktor.


He creado documentación sobre esto en Scribed: Fields-in-Java-and-Kotlin-and-What-to-Expect y también en slide-share: campos-in-java-and-kotlin-and-what-to-expect .


También puedes encontrar los ejemplos que he dado en GitHub. El ejemplo de las chicas doradas se puede encontrar aquí: jeorg-kotlin-test-drives y los ejemplos de anotaciones de validación y persistencia de Jakarta se pueden encontrar aquí: https://github.com/jesperancinha/jeorg-spring-master-test-drives .


Finalmente también creé un video sobre esto en YouTube que puedes ver aquí:

También publicadoaquí .