Menu
Coddy logo textTech

Duck Typing en Go

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

El término duck typing proviene del refrán: "Si camina como un pato y grazna como un pato, entonces es un pato". En Go, esto significa que un tipo no necesita declarar explícitamente que implementa una interfaz; solo necesita tener los métodos adecuados.

Considera esta interfaz y dos tipos completamente no relacionados:

type Quacker interface {
    Quack() string
}

type Duck struct{}
func (d Duck) Quack() string { return "Quack!" }

type Robot struct{}
func (r Robot) Quack() string { return "Beep-quack!" }

Ni Duck ni Robot mencionan a Quacker en ninguna parte de sus definiciones. Sin embargo, ambos satisfacen la interfaz simplemente porque tienen un método Quack() con la firma correcta:

func MakeItQuack(q Quacker) {
    fmt.Println(q.Quack())
}

func main() {
    MakeItQuack(Duck{})   // Quack!
    MakeItQuack(Robot{})  // Beep-quack!
}

Esta satisfacción implícita es poderosa porque te permite definir interfaces después de que los tipos ya existan. Puedes crear una interfaz que coincida con tipos de paquetes externos sin modificar su código fuente. Los tipos no necesitan conocer tu interfaz; simplemente necesitan comportarse correctamente.

El duck typing en Go te ofrece la flexibilidad de los lenguajes dinámicos mientras mantiene la seguridad de tipos en tiempo de compilación. El compilador verifica que los tipos realmente tengan los métodos requeridos antes de permitir que se utilicen como valores de interfaz.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de mensajería que demuestre el duck typing en acción. Crearás tipos completamente no relacionados que pueden enviar mensajes a través de una interfaz común, sin que ninguno de ellos declare explícitamente que la implementa.

Organizarás tu código en tres archivos:

  • messengers.go: Crea tres structs no relacionados que casualmente comparten el mismo comportamiento:
    • Phone con un campo Number—su método SendMessage(text string) string devuelve SMS to [Number]: [text]
    • Computer con un campo Email—su método SendMessage(text string) string devuelve Email to [Email]: [text]
    • Pigeon con un campo Name—su método SendMessage(text string) string devuelve [Name] carries: [text]
    Observa que ninguno de estos tipos menciona ninguna interfaz; simplemente tienen la misma firma de método.
  • sender.go: Define una interfaz Messenger que requiera un método SendMessage(text string) string. Crea una función llamada Broadcast que reciba un slice de Messenger y un string de mensaje, y luego devuelva un slice de strings que contenga el resultado de llamar a SendMessage en cada mensajero.
  • main.go: Lee los detalles de la entrada, crea uno de cada tipo de mensajero, coléctalos en un slice de Messenger y usa Broadcast para enviar un mensaje a través de todos ellos. Imprime cada resultado en su propia línea.

Se proporcionarán las siguientes entradas:

  • Línea 1: Número de teléfono
  • Línea 2: Dirección de correo electrónico
  • Línea 3: Nombre de la paloma
  • Línea 4: Mensaje a difundir

Por ejemplo, dados 555-1234, alice@mail.com, Winston y Hello World, tu salida debería ser:

SMS to 555-1234: Hello World
Email to alice@mail.com: Hello World
Winston carries: Hello World

La idea clave aquí es que Phone, Computer y Pigeon no tienen nada en común: no comparten un tipo base ni implementan explícitamente Messenger. Sin embargo, debido a que todos "graznan" de la misma manera (tienen el método coincidente), Go permite que todos se utilicen como valores de Messenger. Esto es el duck typing en funcionamiento.

Hoja de referencia

En Go, el duck typing significa que un tipo no necesita declarar explícitamente que implementa una interfaz; solo necesita tener los métodos requeridos con firmas coincidentes.

Un tipo satisface implícitamente una interfaz si tiene todos los métodos que la interfaz requiere:

type Quacker interface {
    Quack() string
}

type Duck struct{}
func (d Duck) Quack() string { return "Quack!" }

type Robot struct{}
func (r Robot) Quack() string { return "Beep-quack!" }

Tanto Duck como Robot satisfacen la interfaz Quacker sin mencionarla en sus definiciones:

func MakeItQuack(q Quacker) {
    fmt.Println(q.Quack())
}

func main() {
    MakeItQuack(Duck{})   // Quack!
    MakeItQuack(Robot{})  // Beep-quack!
}

Esta satisfacción implícita permite definir interfaces después de que los tipos ya existan, e incluso crear interfaces que coincidan con tipos de paquetes externos sin modificar su código fuente. El compilador verifica en tiempo de compilación que los tipos tengan los métodos requeridos, proporcionando seguridad de tipos mientras se mantiene la flexibilidad.

Pruébalo tú mismo

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// Leer el número de teléfono
	scanner.Scan()
	phoneNumber := scanner.Text()
	
	// Leer la dirección de correo electrónico
	scanner.Scan()
	email := scanner.Text()
	
	// Leer el nombre de la paloma
	scanner.Scan()
	pigeonName := scanner.Text()
	
	// Leer el mensaje a difundir
	scanner.Scan()
	message := scanner.Text()
	
	// TODO: Crear uno de cada tipo de mensajero (Phone, Computer, Pigeon)
	
	// TODO: Recolectar todos los mensajeros en un slice de Messenger
	
	// TODO: Usar Broadcast para enviar el mensaje a través de todos los mensajeros
	
	// TODO: Imprimir cada resultado en su propia línea
	
	// Marcador de posición para usar variables (eliminar al implementar)
	_ = phoneNumber
	_ = email
	_ = pigeonName
	_ = message
}
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