Desenvolvimento

18 nov, 2010

Entendendo o Pattern Model-View-ViewModel (MVVM)

Publicidade

Inversão de controle, desacoplamento e injeção de dependência são formas de manter um código limpo, fácil de dar manutenção e que seja organizado, nos fazendo assim entender e amadurecer no processo de desenvolvimento de software. Hoje, falaremos de um pattern chamado Model-View-ViewModel (MVVM) e veremos na prática alguns desses conceitos.

O MVVM é um pattern que foi criado em 2005 por John Gossman, um dos arquitetos do WPF e Silverlight na Microsoft. O MVVM assemelha-se em alguns aspectos o MVC (Model View Controller) e ao MVP (Model View Presenter), podemos até dizer que ele é uma especialização do MVP adaptado para a arquitetura do WPF e Silverlight. Conceitualmente, o MVVM e o MVP são idênticos, o que os diferencia é que o MVVM é específico para a arquitetura do WPF e Silverlight, e o MVP é independente de plataforma.

O MVVM visa a estabelecer uma clara separação de responsabilidades em uma aplicação WPF e Silverlight, mantendo uma espécie de façade entre o Modelo de Objetos (entenda classes de negócio, serviços externos e até mesmo acesso a banco de dados) e a View, que é a interface, com a qual o usuário interage. Para entendermos melhor como se dá essa separação e para visualizar como os componentes interagem dentro desse cenário, observe a figura abaixo:

Como ilustra a figura acima, há uma clara separação das camadas. A camada Model (Modelo) não conhece a View (camada de apresentação) e vice-versa. Na verdade, a View conhece a ViewModel e se comunica com ela através do mecanismo de binding. E são os avançados mecanismos de binding, eventos roteados e comandos roteados, que fazem do MVVM um pattern poderoso para construção de aplicações WPF e Silverlight.

Talvez você esteja se perguntado: tá bom, a View está ligada a ViewModel através do mecanismo de binding, mas como funciona essa comunicação?

Observe abaixo:

Veja como é simples: a View, através do databinding, interage com a ViewModel notificando a ocorrência de eventos e o disparo de comandos. A ViewModel, por sua vez, responde a essa notificação realizando alguma ação no modelo, seja obtendo algum dado, atualizando ou inserindo informações no modelo.

Responsabilidades e características

View: a responsabilidade da View é definir a aparência ou a estrutura que o usuário vê na tela. O ideal é que o codebehind da view contenha apenas a chamada ao método InitializeComponent dentro do construtor, ou, em alguns casos, o código que manipule os controles visuais, ou crie animações, algo que é mais difícil de fazer em XAML. A View se liga ao ViewModel através da propriedade DataContext, que é setada para a classe ViewModel correspondente àquela View. Veja no código de exemplo, que será disponibilizado para baixar, como é feita declarativamente a ligação da View com o ViewModel através da propriedade DataContext.

Características comuns

  • A View é um elemento visual, como um objeto Window, Page, UserControl ou DataTemplate.
  • Ela referencia a ViewModel através da propriedade DataContext. Os controles da View são preenchidos com propriedades ou comando, expostos pela ViewModel.
  • O codebehind da view define comportamentos visuais (Behaviors) difíceis de expressar em XAM.

ViewModel: a responsabilidade da ViewModel, no contexto do MVVM, é disponibilizar para a View uma lógica de apresentação. A View Model não tem nenhum conhecimento específico sobre a view, ou sobre como ela é implementada, nem o seu tipo. A ViewModel implementa propriedades e comandos para que a View possa preencher seus controles, e a notifica caso haja alteração de estado, seja através de eventos ou de notificação de alteração. A ViewModel é peça fundamental no MVVM porque é ela quem vai coordenar as iterações da View com o Model, já que um não tem conhecimento do outro. Além de tudo isso, a ViewModel pode implementar a lógica de validação, para garantir a consistência dos dados.

Características comuns

  • A ViewModel é uma classe não visual, que expõe para a View uma lógica de apresentação
  • É testável, independentemente da View ou do Model
  • Coordena as iterações entre a View e o Model
  • Não referencia a View, na verdade, não tem nenhum conhecimento sobre ela
  • Implementa as interfaces INotifyPropertyChanged
  • Expõe propriedade e comando para que a View utilize para preencher seus controles, e notifica a View quando o estado de uma determinada propriedade muda, via implementação da inteface INotifyPropertyChanged ou INotifyCollectionChanged
  • Pode conter a lógica de validação, através da implementação das intefaces IDataErrorInfo ou da INotifyDataErrorInfo

Model: no MVVM, o Model encapsula a lógica de negócios e os dados. O Model nada mais é do que o modelo de domínio de uma aplicação, ou seja, as classes de negócio que serão utilizadas em uma determinada aplicação. Ele também contém os papéis e a validação dos dados de acordo com o negócio, cuja aplicação em questão visa a atender.

Características comuns

  • O Modelo são classes que encapsulam a lógica de negócios e os dados
  • O Modelo não referencia diretamente a View ou ViewModel
  • Ele provê eventos de notificação de mudança de estado, através das interfaces INotifyPropertyChanged and INotifyCollectionChanged. Isso facilita o preenchimento de dados na View
  • O Modelo de dados contém validação de dados e reporta os erros através da interface INotifyDataErrorInfo
  • O Modelo de dados geralmente é utilizado com um repositório (pode ser o Repository Pattern) ou serviço

O MVVM permite a você ter uma visão da clara separação da Interface com o usuário (View), sua lógica de apresentação (ViewModel) e os seus Dados (Model). E, trabalhando dessa forma, temos separação de responsabilidades, desacoplamento e conseguimos evoluir e manter melhor as nossas aplicações.

Já falamos muito, vamos a um exemplo prático. Observe a figura abaixo e veja como ficou a nossa solução de exemplo:

É um projeto simples, apenas para ilustrar os conceitos mostrados no artigo. Vamos começar pela tela que lista os clientes.

Veja que ao abrir o arquivo TodosClientesView.xaml.cs, você verá que o codebehind contém somente a chamada ao método InitializeComponent e nada mais.

Mas ao abrir o arquivo TodosClientesView.xaml você verá que a propriedade DataContext, da grid dentro do User Control, está apontando para o ViewModel correspondente a essa tela, no caso, a classe TodosClientesViewModel.

TodosClientesView.xaml

TodosClientesView.xaml.cs

Vemos que não há ligação da View com o Model. É a responsabilidade da ViewModel fazer essa ligação, como já foi dito anteriormente. Se você observar, a propriedade ItemsSource da Grid está apontando para TodosClientes, que é justamente uma propriedade da classe ViewModel. Observe a figura abaixo:

Se você observar bem, a classe TodosClientesViewModel herda da classe ViewModeBase; isso se dá pelo fato de que, como as classes ViewModel precisam coordenar as ações da View e reagir a iterações realizadas pelo usuário na mesma, ela precisa implementar a interface INotifyPropertyChanged. Para facilitar, é definida uma classe base que implementa a interface e as classes derivadas apenas herdam e utilizam sua implementação da interface. Olhe o diagrama das classes ViewModel:

Note que a ViewModel da tela de cadastro de clientes também implementa a interface IDataErrorInfo, para também poder obter os erros de validação.

E, assim, vemos como fica clara a separação das responsabilidades da View, da ViewModel e do Model, e como recursos avançados como databinding, commands e eventos roteados tornam esse pattern muito poderoso e eficiente para construção de aplicações ricas.

Resumindo, a View conversa com a ViewModel, que conversa com o Model, ou seja, a view não conhece o model, que não conhece a view nem a viewmodel, totalmente desacoplado e de fácil manutenção. A aplicação de exemplo tem como objetivo apenas deixar consolidado os conceitos do pattern aprendido aqui.

Bom, pessoal, espero ter ajudado e colaborado com o aprendizado de vocês. Vejo no MVVM um auxílio poderoso na hora de construir aplicações ricas com boas práticas. Se você vai começar a mergulhar nesse maravilhoso mundo das interfaces ricas, com certeza vai precisar desse aliado do MVVM. Um abraço e até mais.