Saltar al contenido principal

Go desde cero, parte 6: interfaces, el duck typing que compila

> serie: aprende go de 0 a 100 | artículo 6 de 19

Las interfaces en Go son la pieza que lo convierte de «lenguaje sencillito» a «lenguaje en el que se escriben Kubernetes y Docker». Y funcionan al revés de lo que esperas si vienes de Java.


Implementación implícita: sin implements

Una interfaz en Go es un conjunto de firmas de métodos. Y aquí viene el giro, un tipo la implementa automáticamente por el mero hecho de tener esos métodos. No se declara nada (A Tour of Go, interfaces y implementación implícita):

// Una interfaz define comportamiento: solo firmas.
type Figura interface {
  Area() float64
}

// Rectangulo ya tiene Area() del artículo 5.
// No hay que declarar nada: YA implementa Figura.

func imprimeArea(f Figura) {
  fmt.Printf("área: %.2f\n", f.Area())
}

imprimeArea(Rectangulo{Ancho: 3, Alto: 4})
imprimeArea(Circulo{Radio: 2})

En Java escribes class Rectangulo implements Figura y el acoplamiento queda grabado en la clase. En Go la interfaz puede definirse DESPUÉS que los tipos, incluso en otro paquete que ni conocen. Es el duck typing de Python («si camina como un pato…») pero verificado por el compilador: si falta un método, no compila (FAQ oficial).

La interfaz más rentable: io.Writer

La biblioteca estándar está construida sobre interfaces diminutas. La estrella es io.Writer, un solo método:

// io.Writer: la interfaz que mueve el mundo Go.
// type Writer interface {
// Write(p []byte) (n int, err error)
// }

func saluda(w io.Writer) {
  fmt.Fprintln(w, "hola desde una interfaz")
}

saluda(os.Stdout) // a consola
var buf bytes.Buffer
saluda(&buf) // a memoria (¡ideal para tests!)
f, _ := os.Create("log.txt")
saluda(f) // a archivo

Una función que acepta io.Writer escribe en consola, en memoria, en archivos, en conexiones de red o en un gzip sin cambiar una línea. Este es el poder que en otros lenguajes requiere frameworks de inyección de dependencias. Aquí es un parámetro.

Interfaces en Go: Diagrama pixelart ilustrando las interfaces en Go: tres tipos, Rectángulo, Círculo y Triángulo, encajando en un enchufe etiquetado interface Figura
[Si tiene Area(), encaja: así de simple es una interfaz en Go.]

fmt.Stringer: tu tipo se presenta solo

Otra interfaz de un método que usarás desde hoy (A Tour of Go, Stringers):

// fmt.Stringer es la interfaz más famosa de la stdlib:
// type Stringer interface { String() string }

type Nivel int

func (n Nivel) String() string {
  switch n {
    case 0:
    return "DEBUG"
    case 1:
    return "INFO"
    default:
    return "?"
  }
}

fmt.Println(Nivel(1)) // INFO, fmt lo detecta solo

El paquete fmt comprueba en tiempo de ejecución si tu valor implementa Stringer y usa tu String() al imprimir. Es el __str__ de Python o el toString() de Java, sin herencia de por medio. Y de paso resuelve la pega del artículo 2: nuestros niveles con iota ya imprimen con nombre.

Interfaces pequeñas: la filosofía

El consejo de Effective Go: las interfaces de uno o dos métodos son la norma, no la excepción. La regla de andar por casa: defínelas donde se CONSUMEN, no junto al tipo que las implementa, y pide solo lo que necesitas. Si tu función solo escribe, pide io.Writer, no un archivo entero.

Comparado con las interfaces de 15 métodos que se ven por otros barrios, esto es minimalismo funcional: mejor muchas piezas pequeñas que encajan que una maquinaria que solo encaja consigo misma. Ese es el espíritu de las interfaces en Go: pequeñas, implícitas y omnipresentes.


Reto de la semana: pon a prueba las interfaces en Go

  • Recupera Rectangulo y Circulo del artículo 5, define la interfaz Figura con Area() y Perimetro() y recorre un slice de figuras imprimiendo ambos valores.
  • Implementa fmt.Stringer para el tipo Nivel del artículo 2 con los cuatro niveles.
  • Bonus: escribe func informe(w io.Writer, figuras []Figura) y pruébala con os.Stdout y con un bytes.Buffer. Acabas de escribir código testeable sin saberlo.

Interfaces en Go: nivel completado.

En el próximo artículo: type assertions, type switches y la interfaz vacía. La parte de interfaces donde se esconden los sustos.

> exit
Retrato pixel art de Jenniffer Cubillos

gracias por leer