Menu
Coddy logo textTech

Por qué Go no tiene herencia

Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy. Lección 34 de 107.

Los lenguajes tradicionales orientados a objetos, como Java y C++, usan la herencia para compartir código entre tipos. Una clase hija extiende una clase padre, heredando todos sus campos y métodos. Go omite deliberadamente esta característica.

La herencia crea un acoplamiento estrecho entre los tipos. Cuando una clase principal cambia, todas las clases secundarias se ven afectadas.

Las jerarquías de herencia profundas se vuelven difíciles de comprender y mantener. El «problema de la clase base frágil» ocurre cuando las modificaciones en una clase base rompen inesperadamente las clases derivadas.

Los diseñadores de Go eligieron un camino diferente: composición en lugar de herencia. En lugar de decir «un Dog is an Animal», Go te anima a decir «un Dog has comportamientos similares a los de Animal». Este cambio sutil conduce a un código más flexible y fácil de mantener.

Go logra la reutilización de código mediante dos mecanismos que ya has aprendido:

  • Interfaces definen contratos de comportamiento sin detalles de implementación
  • La incrustación de structs permite que los tipos incluyan otros tipos y reutilicen sus métodos

Considera esta comparación. En la POO tradicional, podrías escribir class Dog extends Animal. En Go, integras una estructura Animal dentro de Dog e implementas interfaces compartidas. El resultado es una funcionalidad similar con un acoplamiento más flexible entre los tipos.

Este capítulo explora la inclusión de estructuras en profundidad y muestra cómo Go logra los beneficios de la herencia sin sus desventajas.

challenge icon

Desafío

Fácil

Construyamos un sistema de notificaciones que demuestre el enfoque de composición de Go en lugar de la herencia. Crearás tipos que compartan comportamiento mediante interfaces y la inclusión de estructuras, en lugar de jerarquías de clases.

Organizarás tu código en tres archivos:

  • notifier.go: Define una interfaz Notifier con un único método Notify(message string) string. Crea también una estructura BaseNotifier con un campo Name que será incluida por otros tipos. Dale a BaseNotifier un método llamado Format(message string) string que devuelva el mensaje con el nombre del notificador entre corchetes como prefijo.
  • channels.go: Crea dos tipos de canales de notificación que incluyan BaseNotifier e implementen la interfaz Notifier:
    • EmailNotifier con un campo adicional Address
    • SMSNotifier con un campo adicional Phone
    El método Notify de cada tipo debe usar el método incluido Format e incluir en la salida la información específica de su canal.
  • main.go: Crea una función llamada SendAlert que acepte cualquier Notifier y un mensaje, y después devuelva el resultado de llamar a Notify. Lee los detalles de la notificación de la entrada, crea ambos tipos de notificadores y demuestra cómo pueden utilizarse indistintamente mediante la interfaz.

Se proporcionarán las siguientes entradas:

  • Línea 1: Nombre del notificador para el correo electrónico
  • Línea 2: Dirección de correo electrónico
  • Línea 3: Nombre del notificador para SMS
  • Línea 4: Número de teléfono
  • Línea 5: Mensaje de alerta

Tu método Format en BaseNotifier debe devolver:

[Name] message

Tus métodos Notify deben devolver:

  • EmailNotifier: Email to [Address]: [formatted message]
  • SMSNotifier: SMS to [Phone]: [formatted message]

Por ejemplo, dadas las entradas Alerts, user@mail.com, Urgent, 555-1234 y Server down, tu salida debería ser:

Email to user@mail.com: [Alerts] Server down
SMS to 555-1234: [Urgent] Server down

Observa cómo ambos tipos de notificadores reutilizan el método Format de BaseNotifier mediante la inclusión, mientras que cada uno proporciona su propia implementación de Notify. La función SendAlert funciona con cualquier Notifier sin conocer el tipo concreto. Esto es la composición en lugar de la herencia en acción.

Pruébalo tú mismo

package main

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

// SendAlert acepta cualquier Notifier y un mensaje, devuelve el resultado de llamar a Notify
// TODO: Implementa la función SendAlert

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// Lee el nombre del notificador de email
	scanner.Scan()
	emailName := scanner.Text()
	
	// Lee la dirección de email
	scanner.Scan()
	emailAddress := scanner.Text()
	
	// Lee el nombre del notificador de SMS
	scanner.Scan()
	smsName := scanner.Text()
	
	// Lee el número de teléfono
	scanner.Scan()
	phoneNumber := scanner.Text()
	
	// Lee el mensaje de alerta
	scanner.Scan()
	alertMessage := scanner.Text()
	
	// TODO: Crea un EmailNotifier con emailName y emailAddress
	
	// TODO: Crea un SMSNotifier con smsName y phoneNumber
	
	// TODO: Usa SendAlert para enviar el alertMessage a través de ambos notificadores
	// e imprime los resultados
	
	// Suprime las advertencias de variables no usadas (elimina estas cuando uses las variables)
	_ = emailName
	_ = emailAddress
	_ = smsName
	_ = phoneNumber
	_ = alertMessage
}
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

Practica por tu cuenta: Compilador de Go online