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.