Go con TDD: Visibilidad y naming

Continuamos con la serie de aprendiendo Go Con TDD.
Esta vez el post va a ser un poco especial. Vamos a hablar de visibilidad de los métodos y de por qué Go es especial en ese sentido.
Además comprobaremos que tener buenos tests y saber utilizar un buen IDE nos permiten hacer cambios en nuestro software de manera sencilla.

Si recordamos solo en el primer post de la serio 1. “Aprendiendo Go con TDD” hemos escrito código ejecutable que no fuesen tests. En otras palabras, no hemos hecho ninguna función main todavía.

Dormant Dreams Adrift, Malibu Ca

Creando nuestra función main

Lo primero será crear una carpeta llamada 00-main, esta carpeta contendrá un solo fichero llamado main.go que será el punto de entrada de nuestra aplicación de ejemplo.

package main

import (
    "fmt"
)

func main() {
    fmt.Println("Hello world")
}

Este fichero nos imprimirá por el terminal “Hello world” cuando ejecutemos run, es decir algo así:

╰─$ go run main.go
Hello world

Bien ya tenemos nuestro sistema funcionando, ahora vamos a ir llamando a todas las utilizades que hemos ido ahciendo durante la serie.

Hello

Empezamos con Hello, si recordamos el post Aprendiendo Go con TDD, sino podemos echar un vistazo.
Lo que desarrollamos fué una función HelloWorld que imprimia “Hello World” o Hello más el nombre, si llamabamos a la función con un parámetro.
Una ventaja de utilizar TDD es que podemos echar un ojo a los tests para recordar como se utiliza la función.

Para llamar a una función que está dentro de un paquete solo tenemos que escribir el nombre del paquete, hello en nuestro caso y despues punto y el nombre de la función.
Algo así:

package main

import (
    "fmt"
    "github.com/jeslopcru/golang-examples/01-hello"
)

func main() {
    fmt.Println("01 - Hello")
    fmt.Println(hello.HelloWorld("Jesús"))
}

Al ejecutar esto obtenemos:

╰─$ go run main.go
01 - Hello
Hello, Jesús

Primera lección de visibilidad

Pues aquí llegamos a la primera lección de visibilidad Golang no tienen identificadores de visibilidad propiamente dichos public, private,... sino que se hace implicitamente con el nombre de la función.

Sólo las funciones que empiezan por Mayúscula son públicas el resto (en minúscula o empezando por _) solo serán visibles dentro del paquete. Esta regla se aplica a variables, tipos, funciones, métodos, constantes, campos.

Calculator

Ahora vamos a segur con calculator. SI recordamos, lo que hacíamos en calculator era realizar la suma de dos números o de múltiples, dependiendo del método al que llamásemos.
Aquí es mucho más sencillo conocer como se invocaba a la función, ya que aprte de los test tambien tenemos la documentación

Documentación

Cómo ya comentamos en el post Poco a poco con Go y TDD: package, funciones, bucles, arrays, cobertura de test
si escribimos comentarios encima de las funciones, golang generará automáticamente
Para ver la documentación de los paquetes que tenemos descargados, abrimos un terminal y escribimos:

$ godoc -http=:6060

Ahora entrando en localhost:6060/pkg/ veremos toda la lista de paquetes descargados y podemos buscar el nuestro: http://localhost:6060/pkg/github.com/jeslopcru/golang-examples/02-calculator/

Por lo que solo nos queda ir al fichero main.go las llamadas a esas funciones y escribir su salida por el terminal. Nos quedaría un fichero así:

package main

import (
    "fmt"
    "github.com/jeslopcru/golang-examples/01-hello"
    "github.com/jeslopcru/golang-examples/02-calculator"
)

func main() {
    fmt.Println("01 - Hello")
    fmt.Println(hello.HelloWorld("Jesús"))
    fmt.Println("")

    fmt.Println("02 - Calculator")
    sum := calculator.Add(5, 2)
    fmt.Println(fmt.Sprintf("Add 5 plus 2 is %v", sum))
    numberList := []int{1, 2, 3}
    sumMultiple := calculator.AddMultiple(numberList)
    fmt.Println(fmt.Sprintf("AddMultiple %v is %v", numberList, sumMultiple))
    fmt.Println("")
}

y al ejecutarlo obtenemos una salida como esta:

╰─$ go run main.go
01 - Hello
Hello, Jesús

02 - Calculator
Add 5 plus 2 is 7
AddMultiple [1 2 3] is 6

Geometría

Aquí empezamos con lo divertido structs`` Si recordamos el post de geometría [Go con TDD: structs, methods y geometría](https://jesuslc.com/?p=2333) declaramos un nuevo tipo llamadoRectangley otro llamadoTriangle` estos tipos tenían métodos para calcular el area y el perímetro.

Así el fichero main.goquedaría así:

package main

import (
    "fmt"
    "github.com/jeslopcru/golang-examples/01-hello"
    "github.com/jeslopcru/golang-examples/02-calculator"
    "github.com/jeslopcru/golang-examples/03-geometry"
)

func main() {
    fmt.Println("01 - Hello")
    fmt.Println(hello.HelloWorld("Jesús"))
    fmt.Println("")

    fmt.Println("02 - Calculator")
    sum := calculator.Add(5, 2)
    fmt.Println(fmt.Sprintf("Add 5 plus 2 is %v", sum))
    numberList := []int{1, 2, 3}
    sumMultiple := calculator.AddMultiple(numberList)
    fmt.Println(fmt.Sprintf("AddMultiple %v is %v", numberList, sumMultiple))
    fmt.Println("")

    fmt.Println("03 - Geometry")
    rectangle := geometry.Rectangle{10.0, 5.0}
    fmt.Println(fmt.Sprintf("Rectangle%v with Area: %f", rectangle, rectangle.Area()))
}

y al ejecutar…

╰─$ go run main.go                                                                                                              2 ↵
# command-line-arguments
./main.go:24:34: implicit assignment of unexported field 'a' in geometry.Rectangle literal
./main.go:24:40: implicit assignment of unexported field 'b' in geometry.Rectangle literal

Nos hemos chocado de frente con la visibilidad Como hemos comentado antes los parámetros con minúscula solo son visibles dentro del package.
Por lo que nuestro gozo en un pozo. No podemos utilizar los tipos fuera de su package, a menos que hagamos refactoring

Como tenemos un buen arnes de seguridad, toda la funcionalidad está creada haciendo TDD, podemos facilmente renombrar los tipos Triangle y Rectangle para que los parámetros empiecen con mayúsculas.
Con PHPStorm es super sencillo nos vamos al fichero geometry.go que está en la carpeta 03-geometry y renombramos los parámetros.
Si necesitamos más detalles de como refactorizar y sacarle partido a PHPStorm podemos ir al post https://jesuslc.com/2015/12/23/como-refactorizar-utilizando-phpstorm/

Listo, los tipos nos han quedado así:

type Rectangle struct {
    SideA float64
    SideB float64
}

type Triangle struct {
    SideA float64
    SideB float64
    SideC float64
}

Por lo que ahora si ejecutamos la función main otra vez obtendremos:

╰─$ go run main.go                                                                                                              2 ↵
01 - Hello
Hello, Jesús

02 - Calculator
Add 5 plus 2 is 7
AddMultiple [1 2 3] is 6

03 - Geometry
Rectangle{10 5} with Area: 50.000000

Todo funciona

Más visibilidad

Go tiene una jerarquía de scope (lo traducióamos como ámbito) muy compacta, peor la palabra inglesa es bastante común.

  • universo: identificadores predeclarados como int y string
  • paquete (package): todos los ficheros de un paquete viven en el mismo scope
  • archivo: solo para nombres de importación de paquetes; no muy importante en la práctica
  • función(function): la habitual
  • bloque: el habitual

GO no tiene namespaces como tal, lo más cercano son los paquetes.

En Java, el nombre ‘y’ podría referirse a cualquier cosa. En cambio en Go, ‘y’ (o incluso ‘Y’) siempre se define dentro del paquete.
Por lo que la interpretación de ‘x.Y’ es clara: encuentra ‘x’ localmente, ‘Y’ pertenece a él.

Más geometría

Bueno, ya solo nos queda usar los tipos que declaramos en el package more-geometry. El fichero main.go nos quedaría algo así:

package main

import (
    "fmt"
    "github.com/jeslopcru/golang-examples/01-hello"
    "github.com/jeslopcru/golang-examples/02-calculator"
    "github.com/jeslopcru/golang-examples/03-geometry"
    "github.com/jeslopcru/golang-examples/04-more-geometry"
)

func main() {
    fmt.Println("01 - Hello")
    fmt.Println(hello.HelloWorld("Jesús"))
    fmt.Println("")

    fmt.Println("02 - Calculator")
    sum := calculator.Add(5, 2)
    fmt.Println(fmt.Sprintf("Add 5 plus 2 is %v", sum))
    numberList := []int{1, 2, 3}
    sumMultiple := calculator.AddMultiple(numberList)
    fmt.Println(fmt.Sprintf("AddMultiple %v is %v", numberList, sumMultiple))
    fmt.Println("")

    fmt.Println("03 - Geometry")
    rectangle := geometry.Rectangle{10.0, 5.0}
    fmt.Println(fmt.Sprintf("Rectangle%v with Area: %f", rectangle, rectangle.Area()))
    fmt.Println("")

    fmt.Println("04 - More Geometry")
    circle := more_geometry.Circle{5}
    fmt.Println(fmt.Sprintf("circle%v with Area: %f", circle, circle.Area()))
    fmt.Println("")

}

Al ejecutarlo nos encontramos con

╭─jesuslc@MacBook-Pro-de-Jesus ~/go/src/github.com/jeslopcru/golang-examples/00-main  ‹master*›
╰─$ go run main.go
# command-line-arguments
./main.go:30:33: implicit assignment of unexported field 'radius' in more_geometry.Circle literal

Pero este error ya nos suena ¿verdad? pasa exactamente lo mismo que con geometry. Tenemos que renombrar todos los tipos.
Una vez este todo empezando con mayúscula al ejecutar obtendremos algo como esto:

╰─$ go run main.go                                                                                                              2 ↵
01 - Hello
Hello, Jesús

02 - Calculator
Add 5 plus 2 is 7
AddMultiple [1 2 3] is 6

03 - Geometry
Rectangle{10 5} with Area: 50.000000

04 - More Geometry
circle{5} with Area: 78.539816

Conclusiones

Hemos aprendido un como crear un fichero ejecutable desde la libea de comandos. Ya incluso podríamos empezar a crear nuestras primeras aplicaciones.
Tambien, aunque un poco por la fuerza hemos interiorizado como funciona la visibilidad en Go.
Viniendo de PHP, no tener identificadores de visibilidad es en princpio algo extraño y un poco dificil de ver, pero enseguida nos acostumbramos.
Además teniendo un IDE com Golang tenemos mucho camino recorrido, porque facilita mucho las cosas, en el momento que estamos escribiendo algo incorrecto, lo tenemos marcado en rojo.

Parece que vamos por buen camino para seguir aprendiendo go ¿no crees?

 

Anuncios

Comenta la entrada

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.