Padrão de Arquitetura Andróide Parte 1: Model-View-Controller

Florina Muntenescu
Florina Muntenescu

Follow

Nov 1, 2016 – 6 min read

Há um ano atrás, quando a maioria da equipa actual do Android começou a trabalhar em upday, a aplicação estava longe de ser a aplicação robusta e estável que nós queríamos que fosse. Tentamos entender porque nosso código estava em tão má forma e encontramos dois principais culpados: a mudança contínua da IU e a falta de uma arquitetura que suportasse a flexibilidade que precisávamos. O aplicativo já estava em seu quarto redesenho em seis meses. O padrão de design escolhido parecia ser Model-View-Controller mas já era então um “mutante”, longe de como deveria ser.

Vamos descobrir juntos o que é o padrão Model-View-Controller; como tem sido aplicado no Android ao longo dos anos; como deveria ser aplicado para que possa maximizar a testabilidade; e algumas de suas vantagens e desvantagens.

Num mundo onde a lógica da interface do utilizador tende a mudar mais frequentemente do que a lógica empresarial, os programadores de desktop e Web precisavam de uma forma de separar a funcionalidade da interface do utilizador. O padrão MVC foi sua solução.

  • Model – a camada de dados, responsável por gerenciar a lógica de negócios e lidar com a API da rede ou banco de dados.
  • View – a camada UI – uma visualização dos dados do Model.
  • Controller – a camada lógica, é notificada do comportamento do usuário e atualiza o Model conforme necessário.

>

>

Estrutura da classe Model-View-Controller

Então, isto significa que tanto o Controlador como a Vista dependem do Modelo: o Controlador para actualizar os dados, a Vista para obter os dados. Mas, o mais importante para o desktop e Web devs naquela época: o Modelo era separado e podia ser testado independentemente da IU. Várias variantes do MVC apareceram. As mais conhecidas estão relacionadas a se o Modelo é passivo ou se está ativamente notificando que ele mudou. Aqui estão mais detalhes:

Modelo Passivo

Na versão do Modelo Passivo, o Controlador é a única classe que manipula o Modelo. Baseado nas ações do usuário, o Controlador tem que modificar o Modelo. Após o Modelo ter sido atualizado, o Controlador notificará a View que também precisa atualizar. Nesse momento, a View irá solicitar os dados do Modelo.

Model-View-Controller – passive Model – behavior
Active Model

Para os casos em que o Controlador não é a única classe que modifica o Modelo, o Modelo precisa de uma forma de notificar a View, e outras classes, sobre atualizações. Isto é conseguido com a ajuda do padrão Observer. O Modelo contém uma coleção de observadores que estão interessados em atualizações. A View implementa a interface do observador e registra como observador ao Modelo.

Model-View-Controller – active Model – class structure

A cada atualização do Modelo, ele também irá iterar através da coleção de observadores e chamar o método update. A implementação deste método na View irá então disparar o pedido dos últimos dados do Modelo.

Model-View-Controller – active Model – behavior

Model-View-Controller in Android

Por volta de 2011, quando o Android começou a tornar-se cada vez mais popular, surgiram naturalmente questões de arquitectura. Como MVC era um dos padrões mais populares da UI naquela época, os desenvolvedores tentaram aplicá-lo ao Android também.

Se você pesquisar no StackOverflow por perguntas como “Como aplicar MVC no Android”, uma das respostas mais populares afirmou que no Android, uma atividade é tanto a View quanto o Controlador. Olhando para trás, isto parece quase loucura! Mas, nesse ponto, a ênfase principal foi em tornar o Modelo testável e geralmente a escolha da implementação para a View e o Controlador era dependente da plataforma.

Como MVC deve ser aplicado no Android

Agora, a questão de como aplicar os padrões MVC tem uma resposta que é mais fácil de encontrar. As Actividades, Fragmentos e Vistas devem ser as Vistas no mundo MVC. Os Controladores devem ser classes separadas que não estendem ou usam nenhuma classe Android, e o mesmo acontece com os Modelos.

Surge um problema ao conectar o Controlador à View, uma vez que o Controlador precisa dizer à View para atualizar. Na arquitetura passiva do Modelo MVC, o Controlador precisa manter uma referência para a View. A maneira mais fácil de fazer isso, enquanto se concentra em testes, é ter uma interface BaseView, que a Activity/Fragment/View estenderia. Assim, o Controlador teria uma referência para a BaseView.

Vantagens

O padrão Model-View-Controller suporta altamente a separação de preocupações. Esta vantagem não só aumenta a testabilidade do código como também facilita a sua extensão, permitindo uma implementação bastante fácil de novas funcionalidades.

As classes Model não têm qualquer referência às classes Android e, portanto, são simples para o teste unitário. O Controlador não estende ou implementa nenhuma classe Android e deve ter uma referência a uma classe de interface da View. Desta forma, o teste unitário do Controlador também é possível.

Se as Views respeitam o princípio da responsabilidade única, então a sua função é apenas actualizar o Controlador para cada evento do utilizador e apenas mostrar os dados do Modelo, sem implementar qualquer lógica de negócio. Neste caso, testes de IU devem ser suficientes para cobrir as funcionalidades da View.

A View Depende do Controlador e do Modelo

A dependência da View do Modelo começa a ser uma desvantagem em Views complexas. A fim de minimizar a lógica na Vista, o Modelo deve ser capaz de fornecer métodos testáveis para cada elemento que é exibido. Numa implementação activa do Modelo, isto aumenta exponencialmente o número de classes e métodos, dado que seriam necessários Observadores para cada tipo de dados.

Dado que a View depende tanto do Controlador como do Modelo, alterações na lógica da IU podem requerer actualizações em várias classes, diminuindo a flexibilidade do padrão.

Quem Maneja a Lógica da IU?

De acordo com o padrão MVC, o Controlador actualiza o Modelo e a Vista obtém os dados a serem exibidos a partir do Modelo. Mas quem decide como exibir os dados? É o Modelo ou a Vista? Considere o seguinte exemplo: nós temos um User, com nome e sobrenome. Na View precisamos exibir o nome do usuário como “Sobrenome, Nome” (por exemplo, “Doe, John”).

Se a função do Modelo é apenas fornecer os dados “brutos”, significa que o código na View seria:

String firstName = userModel.getFirstName(); 
String lastName = userModel.getLastName(); nameTextView.setText(lastName + ", " + firstName)

Então isso significa que seria responsabilidade da View lidar com a lógica da IU. Mas isto torna a lógica da IU impossível de ser testada por unidade.

A outra abordagem é ter o Modelo a expor apenas os dados que precisam ser exibidos, escondendo qualquer lógica de negócio da View. Mas então, acabamos com Modelos que lidam tanto com lógica de negócios quanto com lógica de IU. Seria testável por unidade, mas então o Modelo acaba sendo implicitamente dependente da View.

String name = userModel.getDisplayName(); nameTextView.setText(name);

Conclusion

No início do Android o padrão Model-View-Controller parecia ter confundido muitos desenvolvedores e levou a um código que era difícil, se não impossível de ser testado por unidade.

A dependência da View do Modelo e ter lógica na View conduziu nossa base de código a um estado a partir do qual era impossível recuperar sem refatorar completamente o aplicativo. Qual foi a nova abordagem na arquitetura e por quê? Descubra lendo este blog post.