En el artículo anterior quedó definido el constructor de objetos de tipo Square y la función de extensión square que utiliza dicho constructor para crear y añadir ‘Cuadrados’ dentro del Panel. Al final del artículo anterior el código del método main lucía de la siguiente manera:

main.kt — Paso 3

Construcción de la figura geométrica ‘Triángulo’

Repitiendo todo el procedimiento que hicimos con la figura ‘Cuadrado’ vamos a reemplazar la instanciación de la figura geométrica de tipo Triangle dentro del bloque de código de la función panel, así como su posterior inserción dentro del Panel. Es decir, reemplazaremos las instrucciones…

…por…

Claramente la función square y la función triangle son idénticas. Podemos asumir entonces que la función triangle es una función de extensión de la clase Panel y a su vez es de orden superior que recibe un lambda y que retorna un objeto de tipo Triangle. El signature sería el siguiente:

fun Panel.triangle(?): Triangle

💬 Pongo temporalmente un signo de interrogación como parámetro de entrada ya que lo único que sabemos es que recibe un lambda.

Dado que la función triangle sigue el mismo patrón que la función square llegamos a la conclusión de que la función sigue el patrón de diseño ‘Constructor’ para construir objetos de tipo Triangle así que procedemos a crear la clase constructora de figuras ‘Triángulo’. Desplázate hasta el paquete console_shapes_dsl.builders y crea una nueva clase Kotlin llamada TriangleBuilder. La implementación quedaría de la siguiente manera:

💬 Aquí empezamos a notar código duplicado. Al final del artículo abstraeremos el código a una clase Padre.

El lambda es análogo al de la función square siendo ahora posible definir su tipo de función de la siguiente manera: TriangleBuilder.() -> Unit.

Finalmente el signature de la función triangle queda de la siguiente manera:

fun Panel.triangle(init: TriangleBuilder.() -> Unit): Triangle

Desplázate hasta el paquete console_shapes_dsl.external y añade el siguiente código en el archivo Extension.kt:

fun Panel.triangle(init: TriangleBuilder.() -> Unit): Triangle {

}

Sabemos que la función debe cumplir con los siguientes puntos:
— Crear un objeto de tipo Triangle con los parámetros correspondientes al bloque de código del lambda.
— Incluir el objeto de tipo Triangle dentro del Panel.
— Retornar el objeto de tipo Triangle.

La implementación de la función quedaría de la siguiente manera:

fun Panel.triangle(init: TriangleBuilder.() -> Unit): Triangle {
val builder = TriangleBuilder()
builder.init()
val triangle = builder.build()
this.addShape(triangle)
return triangle
}

💬 Si ejecutas el código verás que nuevamente funciona.

La función anterior la podemos reducir a una sola línea valiéndonos de las funciones de ámbito apply y also de la siguiente manera:

Ahora puedes desplazarte al método main y ejecutar el código sin problemas. Por el momento el método main luce así:

main.kt — Paso 4

Construcción de la figura geométrica ‘Rombo’

Con la experiencia adquirida, luego de substituir la creación e inserción de las figuras ‘Cuadrado’ y ‘Triángulo’ dentro del Panel, podemos avanzar más rápido esta vez.

El objetivo ahora es reemplazar las instrucciones…

…por…

Como ya sabemos que vamos a tener que crear un intermediario que se encargue de la creación de figuras de tipo Rhombus, así que procederemos a crearlo. Desplázate hasta el paquete console_shapes_dsl.builders y crea una nueva clase Kotlin llamada RhombusBuilder. La implementación quedaría de la siguiente manera:

Ahora saltamos a la definición de la función rhombus y guiados por las funciones square y triangle podemos decir que es una función de extensión de la clase Panel, que a su vez es una función de orden superior que recibe un lambda cuyo tipo de función es RhombusBuilder.() -> Unit y que retorna un objeto de tipo Rhombus. El signature de la función sería el siguiente:

fun Panel.rhombus(init: RhombusBuilder.() -> Unit): Rhombus

Desplázate hasta el paquete console_shapes_dsl.external y añade el siguiente código en el archivo Extension.kt:

fun Panel.rhombus(init: RhombusBuilder.() -> Unit): Rhombus {

}

Al igual que las funciones square y triangle, la función rhombus debe cumplir con los siguientes puntos:
— Crear un objeto de tipo Rhombus con los parámetros correspondientes al bloque de código del lambda.
— Incluir el objeto de tipo Rhombus dentro del Panel.
— Retornar el objeto de tipo Rhombus.

La implementación de la función quedaría de la siguiente manera:

fun Panel.rhombus(init: RhombusBuilder.() -> Unit): Rhombus {
val builder = RhombusBuilder()
builder.init()
val rhombus = builder.build()
this.addShape(rhombus)
return rhombus
}

💬 Si ejecutas el código verás que nuevamente funciona.

La función anterior la podemos reducir a una sola línea valiéndonos de las funciones de ámbito apply y also de la siguiente manera:

Desplázate al método main y asegúrate de que todo está en orden. El método main debería lucir así:

main.kt — Paso 5

Podríamos dejar los 3 constructores de figuras tal y como están, sin embargo, como buena práctica deberíamos abstraer el código común a una clase padre facilitando así la inclusión de más figuras en el futuro.

Abstracción del código común de las clases constructoras

El código común de las 3 clases constructoras es el siguiente:
— Definición de la propiedad lines inicializada en 0
— Definición de la propiedad char inicializada con el caracter asterisco *
— Definición de la función build que retorna un objeto de tipo Shape

Como buena práctica también podríamos crear una constante para establecer el caracter por defecto que tendrá la figura en caso de que no se establezca durante su construcción.

Desplázate hasta el paquete console_shapes_dsl.builders y crea una nueva clase abstracta Kotlin llamada ShapeBuilder. La implementación quedaría de la siguiente manera:

La función build la marcamos como abstracta para que sea definida en cada clase constructora particular.

Ahora desplázate hasta la clase SquareBuilder y haz que extienda a la clase ShapeBuilder. Luego elimina la declaración de las propiedades lines y char y finalmente modifica la función build de la siguiente manera:

override fun build() = Square(lines, char)

La clase SquareBuilder completa quedaría así:

💬 Asegúrate de que todo funciona perfectamente antes de continuar con la modificación de las otras 2 clases constructoras.

Puedes modificar las clases TriangleBuilder y RhombusBuilder de manera análoga a la clase SquareBuilder. La definición de dichas clases sería la siguiente:

TriangleBuilder:

RhombusBuilder:

Hemos llegado al final de este artículo. Solo nos queda definir la funciones space y composed que añaden los espacios en blanco y las figuras compuestas al Panel respectivamente. En el siguiente artículo crearemos estas funciones y además aplicaremos más conceptos y características de Kotlin para crear un DSL más idiomático.

--

--

Glenn Sandoval
Kotlin en Android

I’m a software developer who loves learning and making new things all the time. I especially like mobile technology.