Los tipos de datos de toda la vida; enteros, cadenas de texto, booleanos, etc. funcionan muy bien para realizar tareas triviales, pero el desarrollo de software no siempre trata acerca de encontrar el mayor de dos números. Hay muchos problemas que resolver y muchas entidades que modelar.
En efecto, gran parte de la solución de un problema, cuando programamos, pasa por representar de manera adecuada a una entidad del mundo real y a su flujo de vida. Pues bien, ¿De que forma representamos un automóvil y su flujo de fabricación? ¿Como modelamos una factura o un chocolate? y más allá, ¿Como modelamos dos chocolates de distintas marcas?
Cada lenguajes de programación provee su propia forma de lidiar con este problema, la cual se puede resumir en algún tipo de datos personalizado. Algunos ponen a nuestra disposición arreglos asociativos, otros clases, etc. Go por su parte nos deja utilizar structs, los cuales ya mencionabamos en un artículo anterior.
Una struct es un tipo de datos definido por el usuario (en el sentido de que quien programa es el usuario) que agrupa un conjunto de campos que también tienen tipos definidos.
Retomando el ejemplo del automóvil, si lo abstraemos, vemos que posee atributos cómo marca, modelo, año de fabricación, color, etc. Usemos este caso para ejemplificar la declaración de structs
Declaración de structs
type Automovil struct {
Marca string
Color string
Ano int
}
playground
De esta forma declaramos una struct type conteniendo los campos Marca, Color y Ano cómo una estructura de datos personalizada llamada Automovil.
package main
import "fmt"
type Automovil struct {
Marca string
Color string
Ano int
}
func(a Automovil) Acelerar() {
fmt.Println("Este automóvil " + a.Marca + " de color " + a.Color + " esta acelerado")
}
func main() {
myCar := Automovil{
Marca: "Suzuki",
Color: "Rojo",
Ano: 1999, // Notese la coma al final de la última línea
}
fmt.Println("Mi Auto: ", myCar)
}
playground
¿Se dio cuenta de cómo definimos una variable de tipo struct Automovil justo al principio de la función Main? Esta forma de inicialización es conocida con el nombre de inicialización por struct literal. También podemos inicializar variables con nuestro struct con el estamento new
var myCar *Automovil
myCar = new(Automovil)
myCar.Marca = "Suzuki"
myCar.Color = "Rojo"
myCar.Ano = 1999
fmt.Println("Mi Auto: ", myCar)
playground
Asociando métodos a nuestro struct
En Go, las structs puede tener métodos asociados.
package main
import "fmt"
type Automovil struct {
Marca string
Color string
Ano int
}
func (a Automovil) Acelerar() {
fmt.Println("Este automóvil " + a.Marca + " de color " + a.Color + " esta acelerado")
}
func main() {
myCar := Automovil{
Marca: "Suzuki",
Color: "Rojo",
Ano: 1999,
}
myCar.Acelerar()
}
Structs, clases y herencia
Ud podrá pensar; Si un struct tiene campos y también métodos ¿En que se diferencia de una clase? No se equivoque; son parecidos pero no iguales, ambos esconden ideas diferentes en su concepción aunque a nosotros cómo usuarios nos permiten hacer cosas parecidas.
La primera diferencia es que Go no implementa mecanismos de herencia, con esto, surge la segunda diferencia, el encapsulamiento en Go se gestiona a nivel de paquetes, donde un elemento es exportable o no según si su letra inicial es mayúscula o minúscula; con la consecuencia de la no existencia del ámbito protected ¡Porque no existe herencia!
Como no hay herencia, necesitamos una forma para no repetir la implementación de comportamiento. En Go se tomó la decisión de diseño de hacerlo por la vía de la composición, que muy resumidamente es armar nuestros objetos a partir de otros. Si Ud ha trabajado con React o Flutter, entonces este concepto no le será nuevo, pues este patrón forma parte intrínseca de ellos para la creación de componentes.
Un pequeña nota sobre composición sobre herencia
Hay una rica y variada discusión acerca de composición sobre herencia, la cual es muy grande y escapa de las pretenciones de este artículo. Si no ha escuchado sobre el asunto, baste decir que podemos pensar cuando hablamos de composición en una relación del tipo tiene un, esta formado por, mientras que cuando hablamos de herencia, nos referimos a una relación del tipo es un. Fuente: McConnell, Steve (2004). Code Complete: A Practical Handbook of Software Construction. Microsoft Press; 2nd edición.
También puede encontrar una jugosa discusión sobre el tema en este hilo de Stackoverflow.
Composición
Para los diseñadores de Go la decisión de optar por la composición como la forma idiomática de compartir comportamiento y estado es tan importante, que el lenguaje nos permite incrustar en un struct en otro. Ej.
package main
import "fmt"
// Automovil el struct base que incrustaremos
type Automovil struct {
Marca string
Color string
Ano int
}
func (a Automovil) Acelerar() {
fmt.Println("Este automóvil " + a.Marca + " de color " + a.Color + " esta acelerado")
}
// Delorean Porqué si vas a construir una máquina del tiempo ¿por qué no hacerlo con estilo? Dr. Emmet Lathrop Brawn
type Delorean struct {
Automovil
}
func (d Delorean) TimeSkip() {
fmt.Println("Viajando al 5 de noviembre de 1955 ¿Esa es la granja de Peabody?")
}
func NewDelorean() Delorean {
d := Delorean{}
d.Marca = "Delorean"
d.Color = "Plata"
d.Ano = 1988
return d
}
func main() {
myCar := Automovil{
Marca: "Suzuki",
Color: "Rojo",
Ano: 1999,
}
myCar.Acelerar()
autoGenial := NewDelorean()
autoGenial.Acelerar()
autoGenial.TimeSkip()
}
Esto nos abre la puerta a sofisticadas formas de modelar las entidades que son parte de nuestro modelo de negocio. Pero eso es conversaciónm para otro artículo.


0 comentarios:
Publicar un comentario