¿Por qué las Interfaces?

Clase 20 de 42Curso Práctico de Go: Creación de un Servidor Web

Contenido del curso

Construyendo un Servidor Web

Resumen

Cuando trabajas con múltiples structs en Go que comparten comportamientos similares, es fácil caer en la trampa de escribir funciones prácticamente idénticas para cada uno. Entender este problema es el primer paso para apreciar el poder de las interfaces y cómo transforman la manera en que organizamos nuestro código.

¿Cómo se estructura un programa sin interfaces en Go?

El escenario planteado es simple pero revelador: necesitamos tres tipos de animales — un perro, un pez y un pájaro — y cada uno debe indicar cómo se mueve. Para lograrlo, se crean tres structs vacíos [0:42]:

go type perro struct{} type pez struct{} type pajaro struct{}

Cada struct recibe un método receiver distinto que describe su movimiento [1:10]:

go func (p perro) caminar() string { return "soy un perro y camino" }

func (p pez) nadar() string { return "soy un pez y estoy nadando" }

func (p pajaro) volar() string { return "soy un pájaro y estoy volando" }

Un método receiver es una función asociada a un struct específico. Permite que cada tipo de dato tenga comportamiento propio, similar a los métodos de una clase en otros lenguajes.

¿Qué funciones se necesitan para mover cada animal?

Después de instanciar cada animal, se crean tres funciones independientes que reciben como parámetro el tipo correspondiente y simplemente imprimen el resultado del método de movimiento [2:20]:

go func moverPerro(p perro) { fmt.Println(p.caminar()) }

func moverPez(p pez) { fmt.Println(p.nadar()) }

func moverPajaro(p pajaro) { fmt.Println(p.volar()) }

En la función main, se crean las instancias y se llaman estas funciones [3:05]:

go func main() { p := perro{} pez := pez{} pajaro := pajaro{}

moverPerro(p) moverPez(pez) moverPajaro(pajaro)

}

El programa funciona correctamente. Cada animal imprime su mensaje de movimiento. Pero hay un problema serio debajo de la superficie.

¿Por qué este enfoque viola principios de ingeniería de software?

Al observar las funciones moverPerro, moverPez y moverPajaro, queda claro que son prácticamente idénticas [3:40]. La única diferencia entre ellas es el tipo de dato que reciben como parámetro. Esta situación rompe el principio DRY (Don't Repeat Yourself), uno de los fundamentos más importantes en ingeniería de software.

  • El código es repetitivo y difícil de mantener.
  • Si cambia la lógica de movimiento, hay que modificar cada función por separado.
  • Agregar un nuevo animal implica crear un struct, un método y una función adicional.
  • El efecto en cadena de cualquier cambio multiplica el esfuerzo de mantenimiento.

¿Qué riesgos genera la duplicación de código?

Imagina que el proyecto crece y necesitas 10 o 20 tipos de animales. Cada uno requeriría su propia función de movimiento, generando decenas de funciones casi iguales. Cualquier ajuste global — como cambiar el formato de impresión o agregar un log — obligaría a tocar todas y cada una de esas funciones.

Este patrón de código repetitivo no solo consume tiempo de desarrollo, sino que aumenta la probabilidad de errores y hace que el programa sea más difícil de escalar.

¿Cómo resuelven las interfaces este problema?

La solución está en las interfaces de Go. Una interfaz permite definir un contrato: cualquier struct que implemente cierto método puede ser tratado de forma uniforme. En lugar de tener una función por cada animal, se tendría una sola función que acepte cualquier tipo que cumpla con la interfaz.

Esto elimina la duplicación, centraliza la lógica y hace que agregar nuevos animales sea tan simple como crear el struct e implementar el método requerido, sin tocar el resto del código.

Si ya identificas patrones repetitivos en tus proyectos con Go, reflexiona sobre qué funciones podrían unificarse mediante una interfaz. Comparte en los comentarios qué casos de uso se te ocurren para aplicar este concepto.