Menu
Coddy logo textTech

Inyección de dependencias

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

Inyección de dependencias es una técnica en la que una estructura recibe sus dependencias desde el exterior en lugar de crearlas internamente. En Go, las interfaces hacen que este patrón sea natural y potente.

En lugar de codificar directamente una implementación específica dentro de una estructura, aceptas una interfaz. Esto te permite intercambiar implementaciones sin cambiar el código de la estructura:

type Notifier interface {
    Send(message string) string
}

type EmailNotifier struct{}
func (e EmailNotifier) Send(message string) string {
    return "Email: " + message
}

type SMSNotifier struct{}
func (s SMSNotifier) Send(message string) string {
    return "SMS: " + message
}

Ahora crea una estructura que dependa de la interfaz, no de un tipo concreto:

type OrderService struct {
    notifier Notifier  // dependencia inyectada vía interfaz
}

func NewOrderService(n Notifier) *OrderService {
    return &OrderService{notifier: n}
}

func (o *OrderService) PlaceOrder(item string) string {
    return o.notifier.Send("Order placed: " + item)
}

OrderService no sabe ni le importa si está utilizando correo electrónico o SMS. Inyectas la dependencia al crear el servicio:

func main() {
    emailService := NewOrderService(EmailNotifier{})
    fmt.Println(emailService.PlaceOrder("Book"))  // Email: Pedido realizado: Book
    
    smsService := NewOrderService(SMSNotifier{})
    fmt.Println(smsService.PlaceOrder("Phone"))   // SMS: Pedido realizado: Phone
}

Este enfoque hace que tu código sea más flexible y fácil de probar. Durante las pruebas, puedes inyectar un notificador simulado que no envía mensajes realmente. En producción, inyectas la implementación real. La estructura permanece sin cambios en ambos escenarios.

challenge icon

Desafío

Fácil

Construyamos un sistema de registro que demuestre el poder de la inyección de dependencias. Crearás un servicio que puede escribir registros en diferentes destinos, sin que el servicio sepa ni le importe adónde van realmente esos registros.

Organizarás tu código en tres archivos:

  • logger.go: Define una interfaz Logger que requiera un método Log(message string) string. Después, crea dos implementaciones diferentes de registradores:
    • ConsoleLogger con un campo Prefix. Su método Log devuelve [CONSOLE] [Prefix]: [message]
    • FileLogger con un campo Filename. Su método Log devuelve [FILE:[Filename]] [message]
  • service.go: Crea una estructura AppService que dependa de la interfaz Logger (no de un tipo concreto). Incluye una función constructora NewAppService que acepte un Logger y devuelva un puntero a AppService. Asigna a AppService un método llamado DoWork(task string) string que use el registrador inyectado para registrar el mensaje Processing: [task] y devuelva lo que devuelva el registrador.
  • main.go: Lee la configuración de la entrada, crea ambos tipos de registrador, inyecta cada uno en instancias separadas de AppService y llama a DoWork en cada servicio con la tarea proporcionada. Imprime el resultado de cada llamada al servicio.

Se proporcionarán las siguientes entradas:

  • Línea 1: Prefijo del registrador de consola
  • Línea 2: Nombre de archivo del registrador de archivos
  • Línea 3: Tarea que se procesará

Por ejemplo, dadas las entradas INFO, app.log y user authentication, tu salida debería ser:

[CONSOLE] INFO: Processing: user authentication
[FILE:app.log] Processing: user authentication

Observa que AppService no sabe si está registrando en una consola o en un archivo. Simplemente llama a Log en el registrador que se haya inyectado. Esta flexibilidad es la esencia de la inyección de dependencias: el mismo código del servicio funciona con implementaciones de registro completamente diferentes.

Pruébalo tú mismo

package main

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

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// Leer el prefijo del logger de consola
	prefix, _ := reader.ReadString('\n')
	prefix = prefix[:len(prefix)-1]
	
	// Leer el nombre de archivo del logger de archivo
	filename, _ := reader.ReadString('\n')
	filename = filename[:len(filename)-1]
	
	// Leer la tarea a procesar
	task, _ := reader.ReadString('\n')
	if len(task) > 0 && task[len(task)-1] == '\n' {
		task = task[:len(task)-1]
	}
	
	// TODO: Crear un ConsoleLogger con el prefijo
	
	// TODO: Crear un FileLogger con el nombre de archivo
	
	// TODO: Crear un AppService con el ConsoleLogger inyectado
	
	// TODO: Crear otro AppService con el FileLogger inyectado
	
	// TODO: Llamar a DoWork en cada servicio con la tarea e imprimir los resultados
	fmt.Println("result1")
	fmt.Println("result2")
}
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