Olá pessoal, tudo certo?
Desenvolver uma boa experiência para o usuário (UX), em nível profissional, não é facil. Certo?! Considere: Há uma mistura “tenebrosa” de dados, interação, atratividade visual, conectividade, multithreading, segurança, internacionalização, validação e um bocado de “mágica”.
Dessa mistura de necessidades, infere-se uma diversidade de competências (conhecimento + habilidade + atitude) raramente encontrada em um único profissional. Em outras palavras, uma UX realmente bacana implica no envolvimento de diversos profissionais.
Como arquitetos, temos a responsabilidade de “pensar” na construção de nossos softwares de forma que a UX seja resultado de atividades independentes. Ou seja, uma das metas deve ser permitir que cada profissional envolvido tenha condições de realizar suas atividades com independência e auto-suficiência. O “efeito-colateral” dessa preocupação é desacoplamento e incremento em testabilidade (só coisas boas).
Há diversos patterns que favorecem a construção de software com boa UX. Entre eles, MVVM. O post de hoje é sobre ele…
O que é MVVM?
Resposta direta: É um padrão de design para aplicativos.
Se for considerar todas as boas prática para desenvolvimento com WPF ou Silverlight, estará considerando invariavelmente MVVM.
Como qualquer pattern, provê um vocabulário comum que ajuda nas discussões do time de desenvolvimento.
MVVM se tornou uma forma comum de discutir, projetar e implementar aplicações WPF e Silverlight.
Como todo padrão de design, é composta por um conjunto de guidelines e ideias que ajudam na criação estrutural de um software, preservando sua manutenabilidade e compreensão.
Classificação de objetos com MVVM
Em uma aplicação MVVM, podemos agrupar os objetos em três categorias. São elas:
- Model – que contêm os dados consumidos e modificados pelos usuários. Estão também nessa categoria códigos associados ao processamento de regras de negócio, validação de entradas e mecanismos de rastreamento para modificações (logs);
- View – Elemento de UI que mostra os dados permitindo que o usuário modifique o estado do programa via dispositivo de entrada (como teclado e mouse). Também mostra vídeos, fotos ou qualquer outro tipo de dado que o usário deseje ver em sua tela.
- ViewModel – model planejado diretamente para View.
Outra classificação comum é separar objetos entre:
- Negócio – basicamente o mesmo conjunto relacionado a Model
- Apresentação – objetos projetados para serem Views e ViewModels.
Esse esquema pode ser resumido graficamente. Observe:
Repare a “proximidade” da View e da ViewModel. Pelo indicado, elas devem “rodar” na mesma tier. Para aplicações com interface muito rica, rodando em client-side, há uma implicação quase que direta de que o código da Viewmodel também seja client-side.
Considero MVVM ideal para ambientes ricos de execução para UI (como aplicações WPF e Silverlight), mas não tão interessante para aplicações dependentes de renderização simples.
Outra representação comum, considera um “Service Agent”. Observe:
Essa representação evidencia a presença de duas Tiers. É bastante comum para aplicações Silverlight. Na prática, o “lado do cliente” costuma ter uma representação pobre do conjunto de models provido pelo servidor.
Considere, mais uma vez, que essa representação é agnóstica em tecnologia. Há diversas alternativas para desenvolvimento, por exemplo, da infraestrutura de serviços: WCF puro, WCF Data Services, RIA Services …
ViewModel NÃO É um tipo refinado (requentado) de code-behind
Geralmente, quando falamos sobre MVVM, temos facilidade para explicar o conceito de Model e o conceito de View. Entretanto, temos alguma dificuldade apra explicar o que é uma ViewModel.
Comecemos deixando mais claro o que uma ViewModel não é: uma ViewModel não é um tipo mais elegante de Code-behind para a View. Digo isso porque essa é uma confusão bem comum e que sacrifica toda a riqueza desse pattern. Aliás, considere: quando alguém considera ViewModels como sendo uma nova forma de code-behind é porque, provavelmente, costuma colocar muitá lógica em arquivos code-behind.
Uma ViewModel é uma abstração da interface com o usuário. Logo, não deve possuir conhecimento de elementos específicos de UI que estejam na View. Toda lógica específica de uma View deverá estar apenas no code-behind dessa view.
Por que planejar e implementar ViewModels?!
Há diversas razões para criar e utilizar ViewModels. A mais importante delas, ao meu ver, é que elas permitem tratar a interface com o usuário como algo que possa ser projetado com as mesmas práticas de engenharia e orientação a objetos aplicáveis para outras partes de uma aplicação. Isso inclui, por exemplo:
- possibilidade de escrever, facilmente, testes de unidade e integração para cada funcionalidade da interface com o usuário.
- views que se limitam a renderizar suas ViewModels podem ser modificadas, ou mesmo substituídas, com pouca ou nenhuma alteração nas ViewModels;
- facilitam a utilização de frameworks, como o MEF, para dinamicamente compor uma ViewModel, facilimente habilitando suporte a plugins na camada de interface;
- Alta coesão das Models pois quaisquer dados complementares necessários para a View são responsabilidade da ViewModel (exemplo: lista de países para um combo)
Por que é mais fácil entender MVVM usando Silverlight/WPF
Os mecanismos fundamentais para criação de aplicações baseadas em MVVM são data binds e commands. Embora possamos entender e desenvolver aplicações MVVM em diversas plataformas/tecnologias, é bem mais fácil utilizando Silverlight/WPF que foram “pensadas” para atender esse pattern.
Tanto em Silverlight (quanto WPF), podemos (diria mais, devemos) criar Views muito ricas utilizando apenas XAML (uma DSL para elaboração de interfaces baseada em XML). Na prática, não há necessidade de code-behind, visto que há uma ampla variedade de “reações” e “comportamentos” que podem ser relacionados aos elementos de interface sem nenhum código.
Em Silverlight ou WPF, a View é sempre um arquivo XAML.
Além disso, todos os elementos gráficos utilizados nas Views esstão prontos para acomodar a ViewModel.
Ligando dados – Data binding
Quando “instanciamos” uma View temos a responsabilidade de informar a ela qual será a sua ViewModel. Em Silverlight/WPF, toda View possui uma propriedade chamada DataContext e é para essa propriedade que indicamos a View. Em palavras simples: A propriedade DataContext da View SEMPRE DEVE apontar para a ViewModel relacionada.
A ViewModel deverá prover propriedades que serão relacionadas (ligadas) a cada “campo” de entrada da View. Essa “ligação” recebe o nome de data binding.
Quando o valor de uma propriedade da ViewModel é modificado, todas as Views que a consideram recebem uma notificação (de uma olhada na interface IPropertyChanged). Logo, todas as Views se “atualizam” automaticamente.
De forma semelhante, alterações feitas na View são “submetidas” automaticamente para a ViewModel, bia bindings.
Caberá a ViewModel a responsabilidade de manter dados sincronizados com o Model.
Eis uma boa representação gráfica (extraída daqui) do processo:
Ações como dados – Commands
Outra característica marcante do pattern MVVM é a inexistência de métodos na interface pública da ViewModel. Se suas ViewModels possuem métodos, então você não está implementando MVVM em sua plenitude.
ViewModels devem prover objetos “Command” através de propriedades públicas. Esses objetos “Command” são ligados, via data binding, a botões e assemelhados. Caberá ao elemento de interface “ligado” ao comando, evocar o “Execute” do comando. O elemento também deverá atualizar sua disponibilidade conforme status provido por esse “Command”.
Para ilustrar melhor o conceito, consideremos rapidamente a interface pública que deverá ser implementada por um objeto “Command” em aplicações Silverlight:
public interface ICommand { bool CanExecute(object parameter); public event EventHandler CanExecuteChanged; public void Execute(object parameter); }
Considere:
- Para cada “operação” de tela, deverá ser escrito um comando. (Exemplo: gravar, pesquisar, etc…);
- Métodos públicos da Model se convertem, geralmente, em objetos “Command” providos pela ViewModel;
- Commands facilitam a implementação de bons artifícios de UX, como Undo e Redo.
Afinal, por que MVVM facilita a construção da UX?
Fora do pattern MVVM, percebe-se uma sobreposição das funções relacionadas ao design da interface, escrita de code-behind e lógica “core” de negócio. Observe:
Alguns fatos frequentes, quando MVVM não é usado ou é mal implementado:
- O designer da UI constrói uma série de “telas” bem acabadas e bem trabalhadas, que não eram “recriadas” com exatidão pelo DEV. Logo, designers resolvem aprender o básico de programação para implementar com mais “exatidão” sua proposta de interface. Surge o “designer-programador”;
- O programador se incomoda por ter que “limpar” o código “sujo” entregue pelo designer. Para não sofrer “retaliações”, resolve aprender um pouco de “design gráfico” para não “comprometer” a UI. Surge o “programador-desiner”;
- O programador acaba misturando lógica de negócio com code-behind da UI. Surge a UI altamente acoplada;
- A lógica de negócio replica ou ignora muito do código implementado no “code-behind”. Surge o código duplicado e GAPs de teste.
Alguns benefícios da MVVM bem implementada:
- O designer constrói, sozinho, toda a UI. Para isso, dependendo da tecnologia adotada, pode usar programas que trabalham de uma forma em já está habituado (considere a suite Expression);
- Não há “code behind”. Logo, o designer possui total liberdade para criar diversas versões das Views;
- Todo código para atender as Views está organizado em uma ViewModel. Logo, não há necessidade de “sujar” a Model com lógica de apresentação;
- Todas as operações que podem ser executadas pela View estão codificadas em “Commands”. No caso mais simples, esses “Commands” chamam métodos da Model;
- Operações de “Interface” não sujam a Model;
- Há uma separação clara de responsabilidades entre a ViewModel e a Model. A ViewModel NÃO IMPLEMENTA lógica de negócio;
- As ViewModels isolam as Models da interface. Isso significa que alterações de negócio ficam “escondidas” das Views;
- As ViewModels operam na mesma Tier das Views. Logo, podem “esconder” a lógica de conexão com outras tiers.
Há muitas outros pontos a considerar. Entretanto, acho que avançamos bastante.
Por hoje, era isso!
Antonio G. Castro Junior
março 8, 2011
Elemar
Parabéns pelo post, especialmente quando voce fala sobre o “designer-programador” e “programador-designer”.
O que voce tem a dizer sobre o Html5 e Frameworks como o Ext Js (Sencha)?
Ricardo
março 10, 2011
“Se suas ViewModels possuem métodos, então você não está implementando MVVM em sua plenitude.”
Acho que caberia uma observação no caso de utilização de um framework para MVVM como Caliburn Micro onde posso associar um evento qualquer de algum controle a um método do ViewModel, por exemplo, o “Click” do botão “Gravar” ao método “Gravar” do ViewModel. Crio métodos no meu ViewModel, acredito que continuo usando o padrão MVVM e não preciso ficar adicionando objetos Commands no meu ViewModel.
http://caliburnmicro.codeplex.com/wikipage?title=Basic%20Configuration%2c%20Actions%20and%20Conventions&referringTitle=Documentation
Talvez , se tiver interesse, possa fazer um artigo sobre o Caliburn Micro no seu blog.