domingo, 13 de dezembro de 2009

Do MVC para o MVP (Model-View-Presenter)

Este post é o segundo de uma série de traduções de artigos do blog Design Codes, que falam um pouco sobre a evolução do padrão MVC desde o final dos anos 70, até chegar aos dias de hoje, momento em que uma de suas variações - o MVP - está se popularizando, tendo inclusive sido citado como uma best practice no Google I/O 2009 para o desenvolvimento de aplicações GWT.


Clique Aqui Clique Aqui Clique Aqui


Neste artigo, iremos olhar mais de perto o design pattern MVP (Model-View-Presenter) e revisar a forma na qual ele evoluiu desde seus predecessores no Smalltalk ( "MVC Clássico" e "Application Model MVC"). Espero que, depois desta revisão, sejamos capazes de distinguir o MVP do MVC e endender o porquê de a discussão MVC x MVP deixar tanta gente confusa.

Abstract

O primeiro artigo sobre MVP foi pulicado em 1996 por 'Mike Potel' e declarado como a nova geração do modelo de programação C++ e Java da IBM. Era construído com base no modelo de programação do MVC Clássico do Smalltalk, com o intuito de se adaptar melhor aos novos sistemas que foram introduzidos na época e para suportar aplicações cliente-servidor (Leia mais em Web MVC).

O Smalltalk, que duas décadas antes inspirou muitas frameworks e bibliotecas com o MVC, buscava por uma nova arquitetura para a sua próxima geração de frameworks, chamada "Dolphin Smalltalk". Eles analisaram o 'Application Model MVC' (a partir de agora referenciado, neste artigo, por AM-MVC), que era a variação mais recente do MVC, e concluiram que ele não atenderia às suas necessidades. Continuaram procurando, até descobrirem o artigo da IBM descrevendo o MVP, muito similar ao AM-MVC, mas com a guinada certa que o tornava mais adequado ao que estavam procurando. Eles o lapidaram ainda mais, e chegaram a um MVP simplificado.

O modelo MVP do Dolphin Smalltalk ainda atende bem às frameworks atuais e tem sido usado como arquitetura base para aplicações Rich-Client e Thin-Client do .Net Framework (nota do tradutor: no Google I/O 2009, o MVP foi apresentado como uma best-practice para usar com o GWT). Leia mais em Web MVP.

Em 2006, Martin Fowler renomeou o "Dolphin MVP" para Supervising Controller, dando um passo atrás em direção ao AM-MVC, e apresentou uma outra variação do MVP chamada Passive View.

Este artigo foca no MVP/Dolphin e não no MVP/IBM por dois motivos:

  • Primeiro, por ser uma tendência atual. O MVP/IBM possui raizes extras como Interactor, Command e Selection desnecessários nos dias de hoje.
  • Segundo, este artigo tenta distinguir o MVP do MVC, apresentando-os lado a lado, e explicando a forma como o MVP evoluiu a partir do MVC.

Os adeptos do Dolphin apresentam o MVP como uma evolução do AM-MVC, onde o Presenter substitui o Application Model; A IBM, por sua vez, apresenta o MVP como uma evolução do MVC Clássico, onde o Presenter substitui o Controller. De fato, o MVP/IBM é o original, mas a abordagem do Dolphin é mais atraente e fácil de explicar, uma vez que o AM-MVC é muito mais próximo do MVP do que o Controller do MVC Clássico.

O Conceito MVC

A idéia por trás do MVC é fazer uma clara separação entre os objetos do domínio (model) e os objetos da apresentação (view/controller). Ele utiliza o mecanismo Observer-Synchronization para suportar múltiplas apresentações (observadores) de um mesmo objeto de domínio (observável), e para manter os objetos de domínio completamente desacoplados dos seus respectivos objetos de apresentação.

Este conceito foi largamente experimentado nas últimas três décadas, de forma que mesmo com as significativas melhorias que o MVP introduz, no final das contas o conceito continua o mesmo.

O MVC Clássico e o 'Application Model' MVC (AM-MVC)

Antes de continuar, é interessante ler sobre os predecessores do MVP, no primeiro artigo desta série, que fala sobre o Padrão MVC. Em resumo: Tanto no 'MVC Clássico' como no 'AM-MVC', a view é um widget da tela, e cada view tem como par um controller, que processa as entradas do usuário (por exemplo cliques de mouse e digitação no teclado), e comanda o modelo conforme apropriado. O AM-MVC introduz o 'Application Model', que age como uma classe intermediária entre o modelo e a apresentação (view/controllers), de forma a proteger o modelo de detalhes da apresentação.

O que há de errado com o AM-MVC?

Os adeptos do Dolphin Smalltalk esbarraram com o fato de que o AM-MVC não seria mais aplicável, e que ele tinha alguns problemas de usabilidade.

  • Não era mais aplicável, pois não mais havia necessidade por um controller para escutar aos eventos do mouse e teclado. Isso ocorreu após sistemas operacionais como o MS Windows diponibilizassem conjuntos nativos de widgets com os quais a UI podia ser construída. Os próprios widgets capturavam os eventos de mouse e teclado, tornando o controller desnecessário.
  • O maior problema de usabilidade era que, apesar do AM ser responsável por lógica da view, ele não podia acessá-la diretamente: Se por um lado, o AM deveria estar desacoplado da view através do mecanismo Observer Synchronization, ao mesmo tempo ele é responsável por funcionalidades da view, por exemplo alterar cores para destacar erros de validação, habilitar e desabilitar widgets, etc. Por isso, ele às vezes precisa ter acesso à view, o que rompe o padrão observer.

MVP - A guinada

Como mencionado acima, o MVC não é para ser meramente substituído. Podemos notar que o problema do MVC não está em seu conceito, e sim no papel que suas entidades (model-view-controller) desempenham. Ao invés de substituí-lo, o Dolphin deu uma guinada de 60° para o MVP.

Entidades

View é um conjunto de widgets, que respondem às ações dos usuários e deixando para o presenter o tratamento desses eventos. Seu propósito é exibir dados do modelo.

Presenter se encarrega da lógica de apresentação, comanda o modelo e altera a apresentação de acordo com as regras da aplicação. É altamente acoplada com a View

Model são os objetos de negócio que representam o domínio do problema, armazenando os dados, interagindo com o banco de dados e serviços externos, lidando com a lógica de negócio. É totalmente ignorante da UI (portanto desacoplada do view/presenter).

Colaboração

Como mencionado antes, o MVP/Dolphin se assemelha muito ao Supervising Controller.

  • User triggered action: a view delega as entradas do usuário para o presenter;
  • Update display: o presenter executa alguma operação relacionada a UI, alterando seu estado interno, e a atualizando diretamente
  • Command: o presenter comanda o modelo da forma apropriada
  • Event (State Changed): o modelo atualiza o seu estado e dispara um evento apropriado
  • A view trata o evento - e (Query) pega os dados relevantes diretamente do modelo, para poder atualizar sua exibição.

Dando um passo atrás em direção ao AM-MVC, temos o padrão Passive View

  • User triggered action: a view delega as entradas do usuário para o presenter;
  • Update display: o presenter executa alguma operação relacionada a UI, alterando seu estado interno, e a atualizando diretamente
  • Command: o presenter comanda o modelo da forma apropriada
  • Event (State Changed): o modelo atualiza o seu estado e dispara um evento apropriado
  • O presenter trata o evento - ele obtém os dados relevantes do modelo, e atualiza a exibição da view.

Revisitando os problemas do MVC

  • O Dolphin resolveu o problema da aplicabilidade do controller, movendo suas responsabilidades para a view e para o presenter. A view ficou com a responsabilidade de responder aos eventos de mouse e teclado (entradas do usuário), enquanto o presenter ficou com a responsabilidade de comandar o modelo quando apropriado.
  • O Dolphin resolveou o problema da usabilidade, substituindo o Application Model pelo Presenter, o qual tem permissão para se comunicar diretamente com a view quando necessário.

Note que a view no MVP/Dolphin (Supervising Controller) se comunica com o modelo e se registra com seus eventos, enquanto no AM-MVC toda interação entre a view e o model é intermediada pelo 'Application Model'. O padrão Passive View (Fowler) é descrito de forma semelhante, com a intermediação view/model sendo feita através do presenter.

Interação

Vamos colocar os conceitos em prática, revisando a GUI de controle de volume que usamos na primeira parte desta série.

Relembrando: o usuário clica no botão "aumentar volume", e em caso de sucesso, o volume é aumentado em uma unidade, e a caixa de texto exibe o valor do volume atualizado. Se o volume exceder 12, a cor da caixa de texto deve ser mudada para vermelho.

O diagrama abaixo mostra como o MVP (Passive View) lida com este caso de uso.

Vimos na primeira partedesta série que o Application Model (AM) lidava com as regras lógicas (alterar a cor) já que a lógica de apresentação fazia parte de suas responsabilidades, e vimos também que o fato de o AM não poder mudar a cor da caixa de texto diretamente levava a um código deselegante, com o AM tendo que disparar um evento para o caixa de texto.

No MVP, o presenter se encarrega da lógica de apresentação, sendo o único a ter que lidar com a regra: agora, o presenter pode mudara cor da caixa de texto diretamente, e nenhum código adicional é necessário.

Outra coisa a se notar é que o presenter responde ao evento "VolumeChanged" e delega este evento para a view, ao invés de alterar diretamente a caixa de texto para atualizar o valor do volume. A razão para mostrar isso é estressar o fato de que nem todas as atualizações precisam ser iniciadas diretamente pelo presenter. Este deve acessar a view diretammente apenas para comportamentos que não se encaixem no padrão Observer Synchronization (foi o caso da mudança de cor de um widget específico, como resposta a uma lógica da aplicação).

MVC x MVP

Há dois MVC's - MVC Clássico e Application Model MVC, assim como dois MVP's - Supervising Controller and Passive View.

Desta forma, há 4 tipos de combinações MVC x MVP:

1) MVC Clássico x MVP PassiveView

2) MVC Clássico x MVP SupervisingController

3) AM-MVC x MVP PassiveView

4) AM-MVC x MVP SupervisingController

Se entendermos as diferenças entre o MVC Clásico e o AM-MVC; bem como as diferenças entre o MVP-PV e o MVP-SC, uma comparação entre o AM-MVC e o MVP-PV deve ser suficiente para entender as quatro variações.

AM-MVC x MVP-PV

View

No AM-MVC (assim como no MVC Clássico), todo widget é uma view. No MVP, a view é a tela em si. A view do MVP é uma composição de widgets, agrupados em um container de widgets, onde a tela em si é um agrupamento de widgets ou de containers de widgets.

Presenter

O ApplicationModel no AM-MVC e o presenter no MVP-PV são ambos responsáveis pela lógica de apresentação, e ambos agem como classes intermediárias entre o modelo e a view. A grande diferença é que o presenter é altamente acoplado à view, podendo modificá-la diretamente, enquanto o AM é pouco acoplado à view, devendo se comunicar com ela somente através de eventos.

olhando de relance, pode parecer que o AM-MVC foi fracamente projetado. Se o AM está encarregado da lógica de apresentação, como pode estar pouco acoplado à view?? A razão disso é que o AM lidava com um número indeterminado de pares view-controller, então o comportamento padrão era agir como um observer - assim ele não tinha que ser alterado cada vez que um novo widget fosse adicionado à tela. Já o MVP lida com uma única view (a própria tela), de modo que o presenter pode ficar altamente acoplado à sua view.

Ainda que o padrão para o AM era o baixo acoplamento com relação à view, em aplicações reais frequentemente isto era burlado, e por comodidade se permitia o AM ganhar acesso às suas views, quebrando o padrão observer.

Model

O modelo desempenha o mesmo papel em ambas as abordagens, uma vez que ele está desacoplado das responsabilidades de apresentação.

Ainda confuso?

O principal motivo para tanta confusão é o uso exagerado e às vezes errôneo do termo controller.

Algums pessoas não levam em consideração o fato que o Controller MVC versa sobre processar a entrada do usuário e traduzí-la em comandos do modelo. Ele não versa sobre mediar a comunicação entre o modelo e a view. Este equívoco é a razão pela qual o controller é frequentemente referido como 'Application Model' (ou 'Application Layer'), da mesma forma como o MVP é usado, porém sob a denominação de MVC.

É importante entender que a principal função do controller é interceptar a entrada do usuário, outras funções como comandar o modelo e alterar a view são apenas subprodutos desta função principal; da mesma forma, a função do presenter é lidar com lógica da aplicação, e funções como interpretar eventos são subprodutos de sua função principal.

Com a introdução das aplicações web, a necessidade de processar entradas do usuário ressurgiu. Agora, as entradas são enviadas para o servidor na forma de pedidos HTTP, e o controller ressurge na forma de novos padrões:

  • Application Controller: lida com aspectos relacionados ao fluxo entre páginas (page flow);
  • Page Controller lida com pedidos para uma página específica; e
  • Front Controller lida com todos os pedidos de uma aplicação Web.

MVP ou não MVP? Eis a questão!

Em sistemas que possuiem um domain model complexo, um application model e muitas views que espelham os dados do modelo, pode fazer sentido pagar o preço do MVP (aprender o padrão, analisar o design, etc). Ao escolher o MVP, devemos nos responder se é possível separar o presenter da view. Algumas perguntas podem ajudar na decisão:

  1. Espera-se que o sistema tenha suporte tanto para aplicações desktop como web? Neste caso, faz todo sentido usar um Presenter para interagir com os dois tipos de view.
  2. O sistema tem muitas views requerindo mais de um presenter? Neste caso, é mais racional criar uma única view, com vários presenters, que podem ser substituídos "on-the-fly".
  3. Quando não for suficiente testar somente o domain model e quisermos testar também o Application Model.

Caso as considerações acima não sejam aplicáveis, o presenter pode ser embutido na própria View. Também depende do ponto de vista do desenvolvedor.

Para alguns, o MVP melhora a usabilidade, a leitura e a facilidade de busca / navegação no código da aplicação. É mais fácil entender métodos "PlayButtonClicked" no presenter do que onPlayButtonClicked na view. Também é cômodo fazer a view lidar apenas com questões de visualização, deixando o presenter lidar com a maior parte da lógica de reagir aos eventos do modelo.

3 comentários:

  1. Amigão belo post viu.

    Não teria nenhum exemplo pratico ?? tipo utilizando o mvp para web e widows com a mesma solução.

    ResponderExcluir
  2. se a view passa os eventos para o controle, então não é mvc? é largamente divulgado na web esse modelo
    http://portalgwt.com/index.php?option=com_content&view=article&id=73:mvp&catid=34:artigos&Itemid=44

    ResponderExcluir