Menu
Coddy logo textTech

Implementación implícita

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

En muchos lenguajes orientados a objetos como Java o C#, debes declarar explícitamente que un tipo implementa una interfaz utilizando palabras clave como implements. Go adopta un enfoque diferente: la implementación de interfaces es implícita.

Un tipo satisface una interfaz simplemente implementando todos sus métodos. No es necesario declarar la relación en ningún lugar. El compilador lo determina automáticamente.

type Writer interface {
    Write(data string) int
}

type FileWriter struct {
    Filename string
}

// FileWriter implementa implícitamente Writer
func (f FileWriter) Write(data string) int {
    fmt.Println("Writing to", f.Filename)
    return len(data)
}

Observa que no hay ninguna declaración implements Writer en FileWriter. Debido a que FileWriter tiene un método Write(data string) int que coincide con la firma de la interfaz, satisface automáticamente la interfaz Writer.

Este diseño tiene una consecuencia poderosa: puedes definir interfaces después de que los tipos ya existan. Si una biblioteca de terceros tiene un tipo con los métodos que necesitas, puedes crear una interfaz que este ya satisfaga sin modificar el código original.

func Save(w Writer, content string) {
    w.Write(content)
}

func main() {
    fw := FileWriter{Filename: "data.txt"}
    Save(fw, "Hello!")  // Funciona porque FileWriter satisface a Writer
}

La implementación implícita mantiene el código de Go desacoplado y flexible. Los tipos no necesitan conocer las interfaces de antemano, lo que facilita la composición de sistemas a partir de partes independientes.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de entrega de mensajes que demuestre cómo los tipos satisfacen implícitamente las interfaces en Go. Crearás diferentes tipos de mensajeros que satisfacen la misma interfaz, sin declarar nunca esa relación de forma explícita.

Organizarás tu código en dos archivos:

  • messengers.go: Define una interfaz Messenger con un único método Send(message string) string. Luego, crea dos structs que satisfarán implícitamente esta interfaz:
    • EmailMessenger con un campo Address (string)
    • SMSMessenger con un campo PhoneNumber (string)
    Cada struct debe tener un método Send que devuelva un string describiendo la entrega. Ninguno de los structs debe declarar explícitamente que implementa Messenger; Go lo determinará automáticamente basándose en las firmas de los métodos.
  • main.go: Crea una función llamada DeliverMessage que acepte cualquier Messenger y un string de mensaje, y luego devuelva el resultado de llamar a Send. Lee la información de contacto de la entrada, crea ambos tipos de mensajeros y utiliza tu función para entregar mensajes a través de cada uno.

Se proporcionarán las siguientes entradas:

  • Línea 1: Dirección de correo electrónico
  • Línea 2: Número de teléfono
  • Línea 3: Mensaje a enviar

Tus métodos Send deben devolver strings en estos formatos:

  • EmailMessenger: Email to [Address]: [message]
  • SMSMessenger: SMS to [PhoneNumber]: [message]

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

Email to alice@example.com: Hello!
SMS to 555-1234: Hello!

La idea clave aquí es que tu función DeliverMessage acepta cualquier Messenger, y tanto EmailMessenger como SMSMessenger satisfacen esa interfaz simplemente por tener un método Send coincidente. No hay ninguna palabra clave implements en ninguna parte; la implementación implícita de Go se encarga de todo.

Hoja de referencia

En Go, la implementación de interfaces es implícita. Un tipo satisface una interfaz simplemente implementando todos sus métodos; no se necesita ninguna declaración explícita.

type Writer interface {
    Write(data string) int
}

type FileWriter struct {
    Filename string
}

// FileWriter implementa implícitamente Writer
func (f FileWriter) Write(data string) int {
    fmt.Println("Writing to", f.Filename)
    return len(data)
}

No hay una declaración implements Writer. Debido a que FileWriter tiene un método Write(data string) int que coincide con la firma de la interfaz, satisface automáticamente la interfaz Writer.

Puedes usar cualquier tipo que satisfaga una interfaz sin que el tipo conozca la interfaz:

func Save(w Writer, content string) {
    w.Write(content)
}

func main() {
    fw := FileWriter{Filename: "data.txt"}
    Save(fw, "Hello!")  // Funciona porque FileWriter satisface Writer
}

Este diseño permite definir interfaces después de que los tipos ya existan, haciendo que el código sea desacoplado y flexible.

Pruébalo tú mismo

package main

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

// TODO: Crear una función DeliverMessage que acepte cualquier Messenger
// y una cadena de mensaje, luego devuelva el resultado de llamar a Send

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// Leer dirección de correo electrónico
	email, _ := reader.ReadString('\n')
	email = email[:len(email)-1]
	
	// Leer número de teléfono
	phone, _ := reader.ReadString('\n')
	phone = phone[:len(phone)-1]
	
	// Leer mensaje
	message, _ := reader.ReadString('\n')
	if len(message) > 0 && message[len(message)-1] == '\n' {
		message = message[:len(message)-1]
	}
	
	// TODO: Crear un EmailMessenger con la dirección de correo electrónico
	
	// TODO: Crear un SMSMessenger con el número de teléfono
	
	// TODO: Usar DeliverMessage para enviar el mensaje a través de cada mensajero
	// y mostrar los resultados
}
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