Android CWB -> Parte 2 [ Factory MVP ]

Carlos
6 min readSep 12, 2019

Continuamos o aplicativo, na primeira parte tentei montar uma base para mostrar como irá indo o dev do projeto. Agora vamos voltar ao ponto que estavamos e ir melhorando tanto o design como a arquitetura toda em sí.

Se você não viu a primeira parte segue o link:

https://medium.com/@nicolaugalves/android-cwb-iniciando-projeto-c3a2c5670879

Bom, até o momento nosso app foi até a chamada falsa do aplicativo e parou em nosso UseCase.

Precisamos agora definir que: Como não podemos fazer uma requisição na MainThread principal do Aplicativo, pois senão o app congela e ai trava tudo hehe, vamos ter que escolher como seguimos aqui em diante.

O ideal é que tenhamos um repositório agora e este decida se vai fazer uma chamada local ou na internet. Falando em termos mais técnicos, vamos ver se usamos o retrofit ou outra biblioteca que se comunique com um servidor ou vamos procurar essas infos no banco de dados local.

Então vamo lá, vamos escrever nosso repositório:

Para isso, lá no core, na pasta data, crie uma classe chamada GetUserRepository como interface e GetUserRepositoryImpl como implementação do GetUserRepository.

Ele vai ter o método execute, que vem do GetUserUseCase pois lá na implementação do GetUserUseCase agora não vamos mais criar um objeto User. Vamos chamar o repositório e ele vai criar nosso User.

Ficou assim:

E o teste, lá que fizemos, passou?

Passou claro. Estamos criando uma boa estrutura então nossas alterações não irão impactar.

E para reforçar, já comecei a fazer o teste do GetUserUseCase pois colocamos o repositório lá no construtor dele.

Teste também passou.

Logo mais vamos fazer o do Repositório.

Segure as ancoras, que você deve estar pensando: “Ah mas ta muito fácil", pois é, esse é o segredo do bau de 7 chaves. Fazer com calma, e pensar certo em como fazer. Anyway, você vai ver que vamos mexer no Repositório lá na frente e ai vamos ver como adaptar mais testes para cobrirmos todas as mudanças.

Nosso estatus no momento do coverage do nosso projeto:

Vamos tentar chegar no máximo em tudo, e não vem com desculpa que é porque só estou eu trabalhando nele hein?

Continuando…

É hora de dentro do repository colocarmos um dataSource class, essa classe vai ser essencial para começarmos a implementar o Banco de dados local no nosso framework. A utilidade que eu vejo nesse extra é que depois com isto a implementação do banco de dados vai ser fácil de modificar para qual queiramos, Room, Realm, SQLite, etc.. Deixo te mostrar então:

Bom vamos lá, Criei um pacote chamado database, pois a princípio vamos buscar nosso usuário no banco local. Criei a classe GetUserDataSource, e o repositório executa pelo datasource. Assim, lá na chamada do GetUserDataSource no pacote database, eu não fico dependente de qual banco queira usar. Se quiser uso Room, implemento o Room e assim vai.

Não esqueço que para isso, no construtor do presenter na classe Login, eu coloco a instancia deste GetUserDataSource no construtor do Repositório. E claro, valido os testes, para isso alterei meu teste do RepositoryImpl para mockar o datasource dele. E para finalizar, crio o teste do meu GetUserDataSource também, para mantermos um controle do que esta sendo feito.

Agora você deve ter percebido que o LoginPresenter esta ficando carregado no construtor

loginPresenterImpl = LoginPresenterImpl(
GetUserUseCase(
GetUserRepositoryImpl(
GetUserDataSource()
)
)
)

Então, para isso, eu criei o AndroidCWBApplication, e nele vamos começar a fazer nossa injeção de dependência. Existes vários frameworks que fazem isso, Koin, Dagger2, ou até mesmo na mão se consegue fazer. A importância do DI (usado com cautela também) é facilitar e não complicar. A hora que se precisa garantir uma instancia única que é injetada e desinjetada(?) do seu código temos que avaliar não para um único caso, por exemplo, usar em algumas partes do código e não em outras. Tem que pensar-se em usar bem essa ferramenta. Senão ela perde seu significado e ai fica difícil convencer outros desenvolvedores ainda mais pelo tempo de curva de aprendizado que eles vão precisar encarar nesse tempo.

Criei a classe HomeActivity para nosso projeto não ficar só na tela de login, agora na hora que fazemos o Login estático ele vai ir para a tela nova. Para isso acrescentei também os eventos na View do LoginPresenter.View.

Visto isso, vamos lá,

Eu particularmente não sou de reinventar a roda toda hora mas sair usando frameworks a todo custo não me parece muito interessante. Primeiro tento ver se dá pra eu ter o controle (Muitas libs você não tem como modificar o .jar dos métodos e mesmo fazendo um extension no Kotlin você carrega seu aplicativo com mais peso de biblioteca do que realmente precisaria, além dos problemas de Gradle de versões diferentes na hora do Sync) . E visto que é algo mais complexo (Tem que entender o processo, tempo mínimo para se localizar) ai bom, talvez apelo pra libs, ou seja, calma, você consegue fazer. Dito tudo isto, eu vou neste caso fazer nativo a princípio, depois quero mostrar que sei também fazer com libs como Koin, Dagger2, etc.

O código ficou o seguinte no nativo:

No meu AndroidCWBApplication:

Criei um pacote chamado di_native que de certa forma é um framework que eu fiz, então..

Criei o construtor para ele, depois vamos poder ter alguns controles lá dentro, sharepreferences entre outras coisas:

class AndroidCWBMvp(application: Application, private val interactors: Interactors) {
}

E minha Factory, que vai me fazer o papel de me criar uma única instancia das minhas dependências. Ao meu entender, a facilidade que isso nos dá em diferença do Singleton, é que, Singleton você não seguindo esse padrão de DI, o programador cria singletons de classes que já existem, causando boilerplate, problema de manutenção pois se perde o controle, etc. Fica a seu critério, o que eu estou escrevendo aqui não é por lei. São minhas visões.

Seguindo então o Factory do meu MVP ficou assim:

object AndroidCWBMvpFactory {

lateinit var dependencies: Interactors

fun inject(dependencies: Interactors) {
this.dependencies = dependencies
}

fun <T : BasePresenter<T>?> create(modelClass: Class<T>): T {
if (AndroidCWBMvp::class.java.isAssignableFrom(modelClass)) {
return modelClass.getConstructor(
Interactors::class.java
)
.newInstance(
dependencies
)
} else {
throw IllegalStateException("MVP must extend AndroidCWBMvp")
}
}
}

Para isso criei um data class chamado interactos, lá dentro vai ter todos os acessos que precise. E não, ele não vai no core pois tenho visão da aplicação no futuro ai ficaria uma interdependência de modulos. Estamos tentando não ter isso com esta organização.

data class Interactors (
val getUser: GetUserUseCaseImpl
)

Por fim, lá no meu setupView() no LoginActivity meu presenter ficou assim:

loginPresenterImpl = LoginPresenterImpl(
AndroidCWBMvpFactory.dependencies.getUser
)

Agora, ao longo do projeto, vamos ver que a hora que se criar em outros presenter, supostamente(?) ele não deve criar instancias repetidas.

Para finalizar então, vamos rodar os testes e ver se não alteramos nada do que já estava passando.

Todos passaram. Ótima notícia, estamos no caminho certo.

Mais um adendo para você, eu to usando //region e //endregion em algumas partes do código, isso facilita a leitura principalmente com o excesso de override.

Então é isso, no próximo tópico vou focar em instanciar o banco de dados.

O app todo vai ser de início offline. Depois eu vou colocar um callback para chamadas do retrofit ou usar rxAndroid ou na verdade o que gostaria mesmo seria usar o Coroutines!

Vejo você numa próxima espero que tenha gostado.

Segue a parte 3 do projeto:

https://medium.com/@nicolaugalves/android-cwb-parte-3-a0c856c22d8d

Att, Carlos Nicolau Galves.

--

--