Menu
Coddy logo textTech

Polimorfismo mediante interfaces

Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy — lección 41 de 107.

El polimorfismo permite que diferentes tipos sean tratados de manera uniforme a través de una interfaz compartida. En Go, esto se logra completamente a través de interfaces, sin herencia ni jerarquías de clases.

Cuando una función acepta un tipo de interfaz como parámetro, se puede pasar cualquier tipo concreto que implemente esa interfaz. La función no necesita conocer el tipo específico; solo le importa el comportamiento definido por la interfaz:

type Speaker interface {
    Speak() string
}

type Dog struct{ Name string }
func (d Dog) Speak() string { return "Woof!" }

type Cat struct{ Name string }
func (c Cat) Speak() string { return "Meow!" }

func MakeSound(s Speaker) {
    fmt.Println(s.Speak())
}

Ahora MakeSound funciona con cualquier tipo que tenga un método Speak():

func main() {
    dog := Dog{Name: "Rex"}
    cat := Cat{Name: "Whiskers"}
    
    MakeSound(dog)  // ¡Guau!
    MakeSound(cat)  // ¡Miau!
}

La misma llamada a la función produce un comportamiento diferente dependiendo del tipo real pasado. Esto es polimorfismo en acción. La función MakeSound se escribe una vez pero funciona con un número ilimitado de tipos, siempre que cumplan con la interfaz Speaker.

Este enfoque mantiene tu código flexible y extensible. Agregar un nuevo tipo que hable no requiere cambios en las funciones existentes; simplemente implementa la interfaz y funcionará automáticamente.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de descripción de vehículos que demuestre el polimorfismo en acción. Crearás diferentes tipos de vehículos que comparten un comportamiento común a través de una interfaz, y luego escribirás una única función que funcione con cualquier vehículo.

Organizarás tu código en dos archivos:

  • vehicles.go: Define una interfaz Describer que requiera un método Describe() string. Luego crea tres tipos de vehículos que implementen cada uno esta interfaz a su manera:
    • Car con los campos Brand y Model; su método Describe() devuelve Car: [Brand] [Model]
    • Motorcycle con los campos Brand y EngineCC (int); su método Describe() devuelve Motorcycle: [Brand] [EngineCC]cc
    • Bicycle con el campo Type (como "Mountain" o "Road"); su método Describe() devuelve Bicycle: [Type]
  • main.go: Crea una función llamada PrintDescription que acepte cualquier Describer e imprima el resultado de llamar a Describe(). Lee los detalles del vehículo desde la entrada, crea uno de cada tipo de vehículo y pasa cada uno a PrintDescription para demostrar que la misma función funciona con los tres tipos diferentes.

Se proporcionarán las siguientes entradas:

  • Línea 1: Marca del coche (Car brand)
  • Línea 2: Modelo del coche (Car model)
  • Línea 3: Marca de la motocicleta (Motorcycle brand)
  • Línea 4: CC del motor de la motocicleta (Motorcycle engine CC - entero)
  • Línea 5: Tipo de bicicleta (Bicycle type)

Por ejemplo, dados Toyota, Camry, Honda, 600, y Mountain, tu salida debería ser:

Car: Toyota Camry
Motorcycle: Honda 600cc
Bicycle: Mountain

Observa cómo PrintDescription no necesita saber si está recibiendo un Car, Motorcycle o Bicycle; simplemente llama a Describe() y cada tipo responde con su propia salida única. Esto es el polimorfismo: una función, múltiples comportamientos.

Hoja de referencia

El polimorfismo permite que diferentes tipos sean tratados de manera uniforme a través de una interfaz compartida. En Go, esto se logra mediante interfaces sin herencia ni jerarquías de clases.

Cuando una función acepta un tipo de interfaz como parámetro, se puede pasar cualquier tipo concreto que implemente esa interfaz:

type Speaker interface {
    Speak() string
}

type Dog struct{ Name string }
func (d Dog) Speak() string { return "Woof!" }

type Cat struct{ Name string }
func (c Cat) Speak() string { return "Meow!" }

func MakeSound(s Speaker) {
    fmt.Println(s.Speak())
}

La misma función funciona con diferentes tipos:

func main() {
    dog := Dog{Name: "Rex"}
    cat := Cat{Name: "Whiskers"}
    
    MakeSound(dog)  // Woof!
    MakeSound(cat)  // Meow!
}

La función se escribe una vez pero funciona con tipos ilimitados, siempre que cumplan con la interfaz. Agregar nuevos tipos no requiere cambios en las funciones existentes; simplemente implemente la interfaz.

Pruébalo tú mismo

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

// TODO: Crea una función llamada PrintDescription que acepte cualquier Describer
// y que imprima el resultado de llamar a Describe()

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Leer los detalles del coche
	carBrand, _ := reader.ReadString('\n')
	carBrand = strings.TrimSpace(carBrand)
	carModel, _ := reader.ReadString('\n')
	carModel = strings.TrimSpace(carModel)

	// Leer los detalles de la motocicleta
	motoBrand, _ := reader.ReadString('\n')
	motoBrand = strings.TrimSpace(motoBrand)
	motoEngineStr, _ := reader.ReadString('\n')
	motoEngineStr = strings.TrimSpace(motoEngineStr)
	motoEngine, _ := strconv.Atoi(motoEngineStr)

	// Leer los detalles de la bicicleta
	bicycleType, _ := reader.ReadString('\n')
	bicycleType = strings.TrimSpace(bicycleType)

	// TODO: Crea un Car, Motorcycle y Bicycle utilizando los valores de entrada

	// TODO: Llama a PrintDescription para cada vehículo para demostrar el polimorfismo
	fmt.Println("TODO: Print vehicle descriptions")
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos