Inyección de Dependencias en Kotlin en la práctica: Guía completa

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: ¿Qué es la Inyección de Dependencias y por qué es necesaria en Kotlin?

La Inyección de Dependencias (DI) es un patrón de diseño que permite hacer el código más flexible, testeable y mantenible. En lugar de que un objeto cree sus propias dependencias, la DI las pasa (inyecta) desde el exterior. En Kotlin, con su sintaxis concisa y potentes capacidades, la DI se convierte en una herramienta especialmente elegante.

Imagine que está escribiendo una aplicación para pedir pizza. Tiene una clase OrderService que debe enviar notificaciones. Sin DI, se vería así:

class OrderService {    private val notifier = EmailNotifier() // Acoplamiento rígido

fun placeOrder(order: Order) { // Lógica del pedido notifier.send("¡Su pedido ha sido recibido!") }}

Problema: no podrá reemplazar fácilmente EmailNotifier por SmsNotifier ni probar OrderService sin enviar correos reales. La DI resuelve este problema inyectando la dependencia a través del constructor:

class OrderService(private val notifier: Notifier) {    fun placeOrder(order: Order) {        notifier.send("¡Su pedido ha sido recibido!")    }}

Ahora OrderService no sabe cómo se envía la notificación. Esta es la esencia de la DI: bajo acoplamiento y alta reutilización.



Formas principales de implementar DI en Kotlin

En Kotlin existen tres enfoques principales para la inyección de dependencias: DI manual, uso de bibliotecas (Koin, Dagger 2) e inyección a través de frameworks (por ejemplo, Spring). Veamos cada uno en la práctica.



1. Inyección manual de dependencias (Manual DI)

La forma más sencilla es pasar las dependencias a través del constructor o setters. Esto no requiere bibliotecas adicionales y es ideal para proyectos pequeños.

Ejemplo: Creemos un servicio simple para procesar pagos.

// Interfaz de pasarela de pagointerface PaymentGateway {    fun process(amount: Double): Boolean}

// Implementación a través de PayPalclass PayPalGateway : PaymentGateway { override fun process(amount: Double): Boolean { println("Procesando pago de $amount a través de PayPal") return true }}

// Servicio de pedidos con inyección de dependenciaclass OrderService(private val paymentGateway: PaymentGateway) { fun checkout(total: Double) { if (paymentGateway.process(total)) { println("¡Pedido realizado!") } }}

// Punto de entrada — "fábrica" o "contenedor"fun main() { val gateway = PayPalGateway() val service = OrderService(gateway) service.checkout(100.0)}

Ventajas: Simplicidad, control total, sin dependencias de bibliotecas.
Desventajas: Al crecer el proyecto, la gestión manual de todas las dependencias se vuelve compleja (es necesario crear un "contenedor" o "fábrica" manualmente).



2. Inyección de dependencias con Koin

Koin es una biblioteca ligera para DI, escrita en Kotlin. No utiliza reflexión ni requiere generación de código, lo que la hace ideal para proyectos pequeños y medianos.

Instalación: Agregue en build.gradle.kts:

implementation("io.insert-koin:koin-core:3.5.6")

Ejemplo de uso:

import org.koin.core.context.startKoinimport org.koin.dsl.module

// Módulo de dependenciasval appModule = module { single<PaymentGateway> { PayPalGateway() } factory { OrderService(get()) } // get() obtiene automáticamente PaymentGateway}

fun main() { startKoin { modules(appModule) }

// Obtenemos el servicio del contenedor val service: OrderService = org.koin.core.component.get() service.checkout(200.0)}

Conceptos clave de Koin:

  • single — crea una única instancia (singleton)

Blogs

Book Recommendations