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.
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 llamado
Rectangley otro llamado
Triangle` estos tipos tenían métodos para calcular el area y el perímetro.
Así el fichero main.go
quedarí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?