Injeção de Dependência em Kotlin: um guia simples para iniciantes

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

O que é Injeção de Dependência e por que é necessária em Kotlin?



Injeção de Dependência (DI, do inglês Dependency Injection) é um dos princípios-chave de design no desenvolvimento moderno, especialmente em Kotlin. Se você está apenas começando sua jornada na programação, esse conceito pode parecer complexo. Na verdade, é simples: Injeção de Dependência é uma forma de passar as dependências de um objeto de fora, em vez de criá-las dentro do próprio objeto.



Vamos usar um exemplo do dia a dia. Imagine que você está assando um bolo. Em vez de moer a farinha, coletar os ovos e bater a manteiga você mesmo (criar dependências internamente), você simplesmente vai ao mercado e compra os ingredientes prontos (recebe as dependências de fora). A DI funciona da mesma forma: você "encomenda" componentes prontos, em vez de criá-los manualmente.



Em Kotlin, a DI é especialmente importante porque a linguagem é amplamente usada para criar aplicativos Android, soluções de servidor com Ktor e microsserviços. Sem DI, o código fica fortemente acoplado, difícil de testar e manter. Com DI, você obtém:

  • Flexibilidade — substituir facilmente uma implementação por outra
  • Testabilidade — é possível usar objetos mock
  • Código limpo — as classes não sabem como suas dependências são criadas


O problema sem Injeção de Dependência



Vamos considerar uma situação típica. Temos uma classe UserService que trabalha com um banco de dados. Sem DI, fica assim:



class UserService {    private val database = Database() // Acoplamento forte

fun getUser(id: Int): User? { return database.query("SELECT * FROM users WHERE id = $id") }}


Quais são os problemas aqui? Primeiro, UserService cria ele mesmo a instância de Database. Se quisermos substituir o banco de dados real por um de teste (por exemplo, in-memory), teremos que alterar o código dentro da classe. Segundo, o teste unitário se torna impossível — não podemos usar um banco de dados falso. Terceiro, a classe viola o princípio da responsabilidade única (SRP): ela não apenas processa usuários, mas também gerencia a criação do banco de dados.



Imagine dezenas dessas dependências — o código vira um "espaguete", onde tudo está ligado a tudo. É exatamente para resolver esse problema que existe a Injeção de Dependência.



Implementando Injeção de Dependência manualmente



A maneira mais simples de injetar dependências é passá-las através do construtor. Isso é chamado de injeção por construtor. Vamos reescrever nosso exemplo:



class UserService(private val database: Database) {    fun getUser(id: Int): User? {        return database.query("SELECT * FROM users WHERE id = $id")    }}

// Uso:val database = Database()val userService = UserService(database)


Agora, UserService não sabe como Database é criado. Ele simplesmente recebe uma instância pronta. Podemos facilmente substituir o banco de dados:



class FakeDatabase : Database() {    override fun query(sql: String): User? {        return User(1, "Usuário de teste")    }}

// Em testes:val fakeDb = FakeDatabase()val userService = UserService(fakeDb)


Essa abordagem é chamada de DI manual. Para projetos pequenos, é suficiente. Você simplesmente cria todas as dependências em um só lugar (por exemplo, em main() ou em uma classe contêiner especial) e as passa para onde são necessárias.



No entanto, a DI manual tem uma desvantagem: quando o projeto cresce, o número de dependências se torna enorme. Você terá que rastrear manualmente a ordem de criação dos objetos e passá-los por vários níveis. É aqui que os frameworks DI entram em ação.



Injeção de Dependência com Koin



Koin é um framework DI popular para Kotlin que não requer geração de código

Blogs

Book Recommendations