Inyección de Dependencias (Dependency Injection) en Kotlin: 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 a Dependency Injection en Kotlin



El desarrollo moderno en Kotlin requiere una gestión adecuada de las dependencias entre clases. Dependency Injection (DI) es un patrón de diseño que permite pasar las dependencias de un objeto desde el exterior, en lugar de crearlas internamente. Esto reduce el acoplamiento del código, simplifica las pruebas y hace que la aplicación sea más flexible.



En este artículo, analizaremos qué es la inyección de dependencias, por qué es necesaria y veremos tres enfoques para implementarla en Kotlin: inyección manual, uso de la biblioteca Hilt y el framework ligero Koin. El material será útil tanto para principiantes como para desarrolladores experimentados que deseen ordenar la arquitectura de sus proyectos.



Fundamentos de Dependency Injection



El problema sin DI



Imagina una clase UserService que crea directamente una instancia de UserRepository:



class UserService {    private val repository = UserRepository() // acoplamiento fuerte

fun getUser(id: Int): User { return repository.findById(id) }}


Este código es difícil de probar: no puedes reemplazar UserRepository con un objeto mock. Además, si cambia el constructor de UserRepository, tendrás que modificar todos los lugares donde se crea.



Solución: inyección a través del constructor



La forma más sencilla de aplicar DI es pasar la dependencia a través del constructor:



class UserService(private val repository: UserRepository) {    fun getUser(id: Int): User {        return repository.findById(id)    }}


Ahora UserService no sabe cómo se crea UserRepository. Puedes pasar cualquier implementación: una base de datos real, una capa falsa o un mock para pruebas.



Inversión de Control (IoC)



DI está estrechamente relacionado con el principio de Inversion of Control (IoC). En lugar de que la clase controle la creación de sus dependencias, el control se transfiere a un contenedor externo o fábrica. Esto hace que el sistema esté débilmente acoplado y sea fácilmente extensible.



Inyección manual de dependencias (Manual DI)



Para proyectos pequeños o con fines educativos, puedes prescindir de frameworks. Crearemos un contenedor de dependencias simple manualmente.



Ejemplo: contenedor de dependencias



// Definimos la interfaz del repositoriointerface UserRepository {    fun findById(id: Int): User}

// Implementaciónclass UserRepositoryImpl : UserRepository { override fun findById(id: Int): User { // lógica para obtener el usuario de la BD return User(id, "User$id") }}

// Contenedor que reúne todas las dependenciasclass AppContainer { val userRepository: UserRepository = UserRepositoryImpl() val userService: UserService by lazy { UserService(userRepository) }}

// Usofun main() { val container = AppContainer() val service = container.userService println(service.getUser(1))}


Ventajas: control total, sin dependencias externas, facilidad de comprensión.

Desventajas: a medida que el proyecto crece, el contenedor se expande y se vuelve difícil gestionar el ciclo de vida de los objetos y sus ámbitos.



Inyección de dependencias con Hilt



Hilt es la biblioteca oficial de Google para DI en aplicaciones Android con Kotlin. Está construida sobre Dagger, pero es significativamente más fácil de configurar.



Configuración de Hilt



Agrega en build.gradle.kts (nivel de proyecto):



plugins {    id("com.google.dagger.hilt.android") version "2.48" apply false}


En build.gradle.kts del módulo de la aplicación:



plugins {    id("com.google.dagger.hilt.android")}

dependencies { implementation("com.google.dagger:hilt-android:2.48") kapt("com.google.dagger:hilt-compiler:2.48")

Blogs

Book Recommendations