Android Testing Part 3 — MVP “Model” e Dependency Injection

Dario Mungoi
Google Developer Experts
4 min readOct 25, 2016

No último post “Mais MVP” falei sobre como devemos organizar um projecto para que facilmente possamos implementar o padrão de arquitectura MVP e demonstrei como é feita a implementação das componentes View e Presenter.

Este post, tem como objectivo terminar com a explicação do MVP e a sua implementação em um projecto para que possamos passar para o objectivo principal desta série de ensinar a escrever testes para uma app android.

Ao longo deste post, irei falar sobre:

  • Dependency Injection(O que é? Porque falar disto);
  • Implementar um Model clean e Genérico;

Dependency Injection

“Dependency Injection is an expensive term used for a very cheap concept” — Daniel Lew (Android @ Trello e GDE)

Segundo @Daniel Lew Dependency Injection é um termo muito bonito e diria eu que intimidador para um conceito muito simples.(Principalmente para quem está a começar a programar para android)
E isto se deve ao simples facto de ter este termo(no android) relacionado a bibliotecas tipo Dagger que podem ser muito boas mas que deixam todo muito confuso só de tentar entender como funciona

O termo Dependency Injection é utilizado quando existe uma parte do código dependente de outra para realizar alguma certa operação e injectamos está variável na função que é dependente dela.(Tao simples quanto isto)

Um cenário básico que tenho a certeza que todos já estiveram nalgum momento, é precisar de uma variável X em uma classe Y para que esta possa realizar uma operação Z.

Olhando para o cenário acima, pode-se logo dizer que a operação Z depende de X e este pode ser passado a classe Y de 2 formas:

  1. Passar X como parâmetro do construtor na criação da classe Y.
  2. Criar X dentro da classe Y.

Os métodos vão permitir realizar a operação Z mas o mais importante a reter aqui é que “Injectar” o X a classe Y como uma variável importante para realização da operação Z.

Porque falar de Dependency Injection

O principal motivo de ter colocado este conceito de dependency injection aqui no início da série e ligada ao Model e pela importância que este conceito tem na criação de um bom ambiente de testes em que um dos factores principais até que os testes não sejam afectados por variáveis/componentes externas.

Olhando para o model como o componente responsável por ler/armazenar os dados de uma determinada fonte, efectuar os testes assumindo uma só fonte iria pressupor que teríamos de depender da fonte dos dados(dados da consola, disco ou um servidor) para efectuar os testes necessários a nossa arquitectura. Sendo assim, o conceito de dependency injection irá permitir que possamos injectar uma fonte de dados fake que criamos para testes e nos focamos apenas nos componentes que queremos testar sem se preocupar se a implementação da REST API ou dados no disco esta bem feita.(Non of our business).

Implementando um Model Clean e Genérico

Aplicar o conceito de dependency injection, depende muito de como estruturamos o código. Sendo assim, temos de estruturar as nossas classes devidamente para que possamos fazer o aproveitamento dos benefícios que este conceito oferece mencionados acima.

O nosso model, terá diferentes componentes interligados como mostra o diagrama abaixo:

Como mostra o diagrama acima(uma complicação) o Model e composto pelas seguintes classes:

EventServiceApi
Esta interface define o contracto que qualquer fonte de dados(Disco, Rede) deverá seguir.
Para além dos métodos esta interface contém uma outra interface com um método de callback que será utilizado para comunicar de volta com Repositório(mencionado abaixo)como mostra o código abaixo.

Com esta interface criada, podemos ler dados de diferentes fontes(Disco, REST API) bastando simplesmente criar uma nova classe, implementar os métodos definidos no contracto de acordo com a fonte de dados que estamos a implementar. (Exemplo: Uma implementação do EventsService que lê os dados de uma REST API, faríamos dentro do método loadEvent uma chamada para o API utilizando o Retrofit ou outro mecanismo qualquer)

public interface EventsServiceApi {

interface EventsServiceApiCallback<T>{
void onEventsLoaded(T events);

}
void loadEvents(EventsQueryForm form, EventsServiceApiCallback<List<Event>> callback);

}

EventRepository

A interface EventRepository, define os mesmos métodos que a interface EventsServiceApi e uma interface com métodos de callback para que esta possa enviar a informação de volta ao Presenter.

Esta interface é o único ponto de comunicação com o Presnter e garante que este apenas saiba como pedir os dados que precisa sem sequer se preocupar com a origem dos dados.

Para alem dos métodos e a interface para permitir a comunicação com o Presenter, a implementação do EventRepository contém como atributo uma referência a class EventsServiceApi que passamos ao construir um novo repositório a implementação desejada do EventServiceApi(Seja para ler pelo disco ou a partir de uma REST API).

A interface EventRepository e a implementação ficariam algo assim:

public interface EventsRepository{

interface EventsRepositoryCallback{
void onEventsLoaded(List<Event> events);
}

void loadEvents(EventsQueryForm queryForm,EventsRepositoryCallback callback);

}
public class EventsRepositoryImpl implements EventsRepoistory {

private EventsServiceApi mApi;

public EventsRepositoryImpl(EventsServiceApi api){
this.mApi = api;
}


@Override
public void loadEvents(EventsQueryForm queryForm, final EventsRepositoryCallback callback) {
mApi.loadEvents(queryForm, new EventsServiceApi.EventsServiceApiCallback<List<Event>>() {
@Override
public void onEventsLoaded(List<Event> events) {
callback.onEventsLoaded(events);
}
});
}
}

Com o model devidamente criado e estruturado, finalmente foi atingido o objectivo de implementar devidamente o MVP que teria os componentes interligados como mostra o diagrama abaixo.

Continuaremos no próximo post, onde irei falar sobre build flavours e porque estes são importantes na criação de um ambiente de testes e como estão relacionadas a ao conceito de dependency injection.

Se achaste este post interessante clique no coraçãozinho verde abaixo e partilhe com amigos que também podem estar interessados.

Ate a próxima =)

DM

--

--

Dario Mungoi
Google Developer Experts

Senior Android Developer at @Shopify, Design Sprint Master, former Android GDE