Android

3 fev, 2017

Introdução ao Dagger 2

Publicidade

O Dagger 2 é um framework mantido pelo Google e que tem por objetivo realizar a injeção de dependência em projetos Android e Java. Ele foi criado a partir do Dagger 1 que foi desenvolvido pela Square.

Neste artigo é mostrado a diferença entre o Dagger 1 e o Dagger 2.

Inversão de dependência e injeção de dependência

O princípio da inversão de dependência se trata-se de uma maneira específica para desacoplar as dependências entre os objetos. O objetivo deste princípio é a redução do acoplamento entre os componentes através de uma camada de abstração.

A inversão de dependência é um princípio do SOLID. Em sua definição, temos:

  1. Componentes de mais alto nível não devem depender de componentes de níveis mais baixos, mas ambos devem depender de abstrações.
  2. Abstrações não devem depender de implementações, mas as implementações devem depender de abstrações.

Vejamos um exemplo:

public class Exemplo {
  public List<Pedido> listarPedidosUsuario() {
      Usuario usuario = new Usuario();
      return usuario.listarPedidos();
  }
}

Código acoplado, dependendo do objeto e não de uma abstração.

O trecho de código acima possui alguns problemas:

  1. O código viola o princípio de inversão de dependência: a classe Exemplo conhece os detalhes da classe Usuario, ou seja, Exemplo depende da implementação de Usuario e não de sua abstração.
  2. Código acoplado: para listar os pedidos do usuário, é criada uma instância de Usuario, nossa classe mostrada acima, que possui, então, uma dependência com a classe Usuario. Com isso, qualquer alteração em Usuario afetará a classe Exemplo.

Para resolvermos o item 1, nossa classe Exemplo deverá passar a depender da abstração de Usuario, que chamaremos aqui de UsuarioContrato:

public interface UsuarioContrato {
    public List<Pedido> listarPedidos();
}

Para resolvermos o item 2, devemos remover o acoplamento do código, podemos fazer isso utilizando um construtor:

public Exemplo(UsuarioContrato usuario) {
    this.usuario = usuario;
}

Com isso, nossa classe não precisa mais criar uma instância de Usuario. Agora ela pede/recebe de outra classe ou entidade.

Com as mudanças feitas, temos:

public class Exemplo implements ExemploContrato {
    private UsuarioContrato usuario;

    public Exemplo(UsuarioContrato usuario) {
        this.usuario = usuario;
    }

    @Override
    public List<Pedido> listaPedidosUsuario() {
        return usuario.listarPedidos();
    }
}

Passar a dependência pelo construtor é uma forma de remover da nossa classe a responsabilidade de instanciar um objeto. Essa é uma forma de fazer a injeção de dependência, que é uma forma de implementar a inversão de dependência.

Agora, sempre que precisarmos utilizar nossa classe Exemplo, devemos fazer:

Usuario usuario = new Usuario();
Exemplo exemplo = new Exemplo(usuario);

O Dagger 2

O Dagger 2 é uma forma fornecer as dependências utilizadas em nossa classe Exemplo através de códigos gerados automaticamente. Ele se baseia na JSR 330, que define um conjunto de anotações para usar em classes injetáveis buscando maximizar a reusabilidade, testabilidade e manutenibilidade de um código.

Dentre essas anotações temos:

  • @Inject: Anotação utilizada quando se necessita pedir alguma dependência;
  • @Module: Anotação utilizada em classes que fornecem dependências;
  • @Provides: Anotação utilizadas em métodos que proveem dependências. Os métodos provedores são encontrados nas classes com a anotação @Module;
  • @Component: São basicamente injetores; uma ponte entre módulos e provides. Neles, definimos quem pode utilizar nossos módulos e quais módulos são utilizados.

Utilizando o Dagger 2 para resolver o nosso problema

Até aqui temos que para utilizar um objeto de Exemplo devemos fazer:

Usuario usuario = new Usuario();
Exemplo exemplo = new Exemplo(usuario);

Para utilizar o Dagger 2, são necessários os passos abaixo:

Configuração do gradle:

dependencies {
  compile 'com.google.dagger:dagger:2.x'
  annotationProcessor 'com.google.dagger:dagger-compiler:2.x'
}

Você pode checar a versão corrente no repositório do Dagger 2.

Criar um Module

@Module
public class ClassModule {

    @Provides
    public UsuarioContrato proverUsuario() {
        return new Usuario();
    }

    @Provides
    public ExemploContrato proverExemplo(UsuarioContrato usuario) {
        return new Exemplo(usuario);
    }
}

Para utilizar um dos nossos provides, devemos utilizar a anotação Inject:

public class MainActivity extends AppCompatActivity {
    @Inject  //anotação utilizada pelo dagger para injetar uma dependência
    ExemploContrato exemplo;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

Ao utilizar o Inject no código acima, estamos pedindo para que seja fornecido um objeto de Exemplo. O Dagger 2 verifica qual o objeto solicitado e busca em seus provides:

@Module
public class ClassModulo {

    @Provides
    public ExemploContrato proverExemplo(UsuarioContrato usuario) {
        return new Exemplo(usuario);
    }
}

Ao prover uma instância de Exemplo, o Dagger 2 checa que esta possui uma dependência de Usuario e busca essa dependência em seus provedores:

@Provides
public UsuarioContrato proverUsuario() {
    return new Usuario();
}

Criar um Component

@Component(modules = {ClasseModulo.class})//modulos acessíveis por esse componente
interface ComponenteExemplo {
    public void inject(MainActivity activity)//quem pode utilizar esse módulos
}

Fornecer nosso Component

Devemos disponibilizar nosso ComponenteExemplo em um local que seja acessível a todos. Normalmente é criada uma referência junto com nossa classe Application do projeto:

public class MainApplication extends android.app.Application {
    private static ComponenteExemplo component;

    @Override
    public void onCreate() {
        super.onCreate();
        initDagger();
    }

    private void initDagger() {
        component = DaggerComponenteExemplo
                .builder()
                .build();
    }

    public static ComponenteExemplo getComponent() {
        return component;
    }
}

A classe DaggerComponentExemplo é gerada em tempo de compilação (precisa fazer o build do projeto).

Informar sobre o uso do Module

Agora precisamos informar ao Dagger 2 que nossa classe fará uso de um dos nossos Components:

public class MainActivity extends AppCompatActivity {
    @Inject
    ExemploContrato exemplo;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MainApplication.getComponent().inject(this);// informando ao dagger sobre o uso de um component e a necessidade de injetar dependência
        List<Pedido> pedidos = exemplo.listaPedidosUsuario();
    }
}

Feito os passos acima, sempre que precisarmos de uma instância de Exemplo basta utilizar a anotação Inject.

Utilizando a injeção de dependência, nós temos uma facilidade muito grande para realizar, por exemplo, uma troca na implementação da classe Usuario. Isso ocorre pois não dependemos mais da implementação da classe Usuario, mas sim de sua abstração (UsuarioContrato).

Até aqui temos:

@Provides
public UsuarioContrato proverUsuario() {
    return new Usuario();
}

Caso seja necessário realizar uma mudança para uma nova implementação, é necessário apenas atualizar nosso provides:

@Provides
public UsuarioContrato proverUsuario() {
    return new NovaImplementacaoUsuario();
}

Pronto! Agora, ao solicitar a injeção de UsuarioContrato, será fornecido a dependência utilizando a nova implementação de nossa classe. Muito prático!

E é isso! Temos o básico sobre como utilizar o Dagger 2!

Conclusão

O princípio de inversão de dependência é interessante para evitar o acoplamento. A injeção de dependência é uma forma de fazer isso, o Dagger 2 é uma forma de fazer a injeção de dependência. Ele não é insubstituível, porém facilita o trabalho de evitar acoplamento e organizar todas as dependências do nosso projeto.

O código de exemplo utilizado pode ser encontrado aqui.

Você pode encontrar mais sobre o assunto: