O Model View View-Model foi desenvolvido pela Microsoft para direcionar plataformas de desenvolvimento de UI que suportam programação orientada a eventos, especificamente Windows Presentation Foundation (WPF) e Silverlight nas plataformas .NET usando linguagens XAML e .NET. Nos anos seguintes, muitas estruturas Javascript, como Angular, Knockout e ExtJS, adotaram o padrão.
Como a maioria dos padrões de software, o MVVM tem seus usos apropriados e seus abusos. Sob quais condições o uso do MVVM é apropriado? Quando é desaconselhável?
Respostas:
O MVVM deve ser usado onde interações complexas do usuário usando UIs de alta fidelidade são necessárias (por exemplo, WPF ).
Para UIs em que esse tipo de interação rica não é necessária, o MVVM pode ser um exagero; MVP pode ser uma escolha mais natural. Para aplicativos da Web, o MVC é mais adequado. Para aplicativos muito pequenos que nunca serão maiores (como pequenos utilitários Winforms), o code-behind é adequado.
fonte
Às vezes, o MVVM pode ser uma armadilha. Da minha experiência, favorece aplicativos do tipo CRUD (formulários sobre dados) versus interfaces de usuário mais orientadas a tarefas. Não estou dizendo que isso implique arquitetura ruim para as camadas de back-end / outros no aplicativo, mas já vi muitos aplicativos MVVM vindo com a arquitetura "DDD light". Não sei exatamente por que, talvez, porque a ligação é tão fácil e é muito simples configurar um aplicativo com um ORM e MVVM / Binding usando objetos de domínio POCO / Anemic.
fonte
O MVVM é um band-aid para camadas de ligação de dados mal projetadas. Em particular, ele tem sido muito utilizado no mundo WPF / silverlight / WP7 devido a limitações na ligação de dados no WPF / XAML.
A partir de agora, vou assumir que estamos falando sobre WPF / XAML, pois isso tornará as coisas mais claras. Vamos analisar algumas das deficiências que o MVVM se propõe a resolver no WPF / XAML.
Formato dos dados x formato da interface do usuário
A 'VM' no MVVM cria um conjunto de objetos definidos em C # que são mapeados para um conjunto de objetos de apresentação definido em XAML. Esses objetos C # geralmente são conectados ao XAML por meio de propriedades DataContext nos objetos de apresentação.
Como resultado, o gráfico de objeto viewmodel precisa mapear no gráfico de objeto de apresentação do aplicativo. Isso não quer dizer que o mapeamento precise ser individual, mas se um controle de lista estiver contido em um controle de janela, deve haver uma maneira de passar do objeto DataContext da janela para um objeto que descreva os dados dessa lista.
O gráfico de objeto do viewmodel desacopla o gráfico do objeto de modelo do gráfico da interface do usuário com êxito, mas às custas de uma camada adicional do viewmodel que deve ser construída e mantida.
Se eu quiser mover alguns dados da tela A para a tela B, preciso mexer nos modelos de exibição. Na mente de um profissional, essa é uma alteração na interface do usuário. Ele deve ter lugar exclusivamente no mundo do XAML. Infelizmente, isso raramente acontece. Pior ainda, dependendo de como os modelos de exibição estão estruturados e de quão ativamente os dados são alterados, pode ser necessário um redirecionamento de dados para realizar essa alteração.
Como solucionar a ligação de dados não expressiva
As ligações WPF / XAML são insuficientemente expressivas. Você basicamente fornece uma maneira de chegar a um objeto, um caminho de propriedade para percorrer e vincular conversores para adaptar o valor da propriedade de dados ao que o objeto de apresentação exige.
Se você precisar vincular uma propriedade em C # a algo mais complexo que isso, estará basicamente sem sorte. Eu nunca vi um aplicativo WPF sem um conversor de ligação que se tornou verdadeiro / falso em Visível / Reduzido. Muitos aplicativos WPF também tendem a ter algo chamado NegatingVisibilityConverter ou similar que inverte a polaridade. Isso deve estar disparando alarmes.
O MVVM fornece diretrizes para estruturar seu código C # que pode ser usado para suavizar essa limitação. Você pode expor uma propriedade no seu viewmodel chamada SomeButtonVisibility e apenas vinculá-la à visibilidade desse botão. Seu XAML é legal e bonito agora ... mas você se tornou um funcionário - agora você precisa expor + atualizar as ligações em dois lugares (a interface do usuário e o código em C #) quando a interface do usuário evolui. Se você precisar que o mesmo botão esteja em outra tela, precisará expor uma propriedade semelhante em um modelo de exibição que essa tela possa acessar. Pior, não posso apenas olhar para o XAML e ver quando o botão ficará mais visível. Assim que as ligações se tornam um pouco triviais, tenho que fazer um trabalho de detetive no código C #.
O acesso aos dados tem escopo agressivo
Como os dados geralmente entram na interface do usuário por meio das propriedades do DataContext, é difícil representar dados globais ou de sessão de maneira consistente em todo o aplicativo.
A idéia do "usuário conectado no momento" é um ótimo exemplo - isso geralmente é algo verdadeiramente global em uma instância do seu aplicativo. No WPF / XAML, é muito difícil garantir o acesso global ao usuário atual de maneira consistente.
O que eu gostaria de fazer é usar a palavra "CurrentUser" em ligações de dados livremente para se referir ao usuário conectado no momento. Em vez disso, tenho que garantir que todos os DataContext me permitam acessar o objeto de usuário atual. O MVVM pode acomodar isso, mas os modelos de exibição serão uma bagunça, pois todos eles precisam fornecer acesso a esses dados globais.
Um exemplo em que o MVVM cai
Digamos que temos uma lista de usuários. Ao lado de cada usuário, queremos exibir um botão "excluir usuário", mas apenas se o usuário conectado no momento for um administrador. Além disso, os usuários não têm permissão para se excluir.
Os objetos do modelo não devem saber sobre o usuário conectado no momento - eles apenas representam registros do usuário no banco de dados, mas de alguma forma o usuário conectado no momento precisa ser exposto a ligações de dados nas linhas da lista. O MVVM determina que devemos criar um objeto viewmodel para cada linha da lista que compõe o usuário conectado no momento com o usuário representado por essa linha da lista e expor uma propriedade chamada "DeleteButtonVisibility" ou "CanDelete" nesse objeto viewmodel (dependendo de seus sentimentos) sobre conversores de ligação).
Esse objeto parecerá muito com um objeto Usuário da maioria das outras maneiras - talvez seja necessário refletir todas as propriedades do objeto do modelo de usuário e encaminhar atualizações para esses dados à medida que forem alterados. Isso é realmente nojento - mais uma vez, o MVVM o transforma em funcionário, forçando-o a manter esse objeto semelhante ao usuário.
Considere - você provavelmente também precisa representar as propriedades do usuário em um banco de dados, modelo e exibição. Se você tem uma API entre você e seu banco de dados, é pior - eles estão representados no banco de dados, no servidor da API, no cliente da API, no modelo e na visualização. Eu realmente hesitaria em adotar um padrão de design que adicionasse outra camada que precisa ser tocada toda vez que uma propriedade for adicionada ou alterada.
Pior ainda, essa camada é dimensionada com a complexidade da sua interface do usuário, não com a complexidade do seu modelo de dados. Muitas vezes, os mesmos dados são representados em muitos lugares e na sua interface do usuário - isso não apenas adiciona uma camada, mas também uma camada com muita área de superfície extra!
Como as coisas poderiam ter sido
No caso descrito acima, eu gostaria de dizer:
O CurrentUser seria exposto globalmente a todos os XAML no meu aplicativo. Id se referiria a uma propriedade no DataContext para a minha linha da lista. A visibilidade converteria de booleano automaticamente. Quaisquer atualizações no Id, CurrentUser.IsAdmin, CurrentUser ou CurrentUser.Id acionariam uma atualização na visibilidade desse botão. Mole-mole.
Em vez disso, o WPF / XAML força seus usuários a criar uma bagunça completa. Até onde eu sei, alguns blogueiros criativos colocaram um nome nessa bagunça e esse nome era MVVM. Não se deixe enganar - ele não pertence à mesma classe que os padrões de design do GoF. Este é um truque feio para solucionar um feio sistema de ligação de dados.
(Essa abordagem às vezes é chamada de "Programação Reativa Funcional", caso você esteja procurando mais leituras).
Em conclusão
Se você deve trabalhar no WPF / XAML, ainda não recomendo o MVVM.
Você deseja que seu código seja estruturado como o exemplo "como as coisas poderiam ter sido" acima, o modelo exposto diretamente para visualização, com expressões complexas de ligação de dados + coerções flexíveis de valor. É muito melhor - mais legível, mais gravável e mais sustentável.
O MVVM diz para você estruturar seu código de maneira mais detalhada e menos sustentável.
Em vez do MVVM, crie algumas coisas para ajudá-lo a aproximar a boa experiência: Desenvolva uma convenção para expor o estado global à sua interface do usuário de forma consistente. Crie você mesmo algumas ferramentas de conversores de ligação, MultiBinding, etc., que permitem expressar expressões de ligação mais complexas. Crie uma biblioteca de conversores de ligação para ajudar a tornar os casos de coerção menos dolorosos.
Ainda melhor - substitua XAML por algo mais expressivo. XAML é um formato XML muito simples para instanciar objetos C # - não seria difícil criar uma variante mais expressiva.
Minha outra recomendação: não use kits de ferramentas que forçam esses tipos de comprometimentos. Eles prejudicarão a qualidade do seu produto final, empurrando você para uma porcaria como o MVVM, em vez de se concentrar no domínio do problema.
fonte
Gosto muito do MVVM e acho seus desafios motivadores e vejo muitos benefícios, mas ...
Para aplicativos ou jogos que exigem muito código de interface do usuário / interação para adicionar muitos comportamentos personalizados, mantendo o desempenho atualizado - geralmente é melhor usar um MVVM um pouco sujo - use-o quando for útil ou mais centralizado em dados do que em áreas centralizadas de interação do código. Digamos que você queira criar um controle e movê-lo entre diferentes controles-pai ou armazená-lo em cache ...
Ele tem uma curva de aprendizado bastante íngreme; portanto, a menos que você tenha tempo, planeje desenvolver muito no WPF / SL, tenha designers especializados em Blend, sinta a necessidade de escrever código de teste para sua interface do usuário ou espere fazer anos de manutenção em seu projeto - pode não dar resultado.
Poucos designers conhecem o Blend, nem todos os projetos valem a pena concentrar o teste automatizado na camada de apresentação, pois ele precisa ser testado manualmente de qualquer maneira e é assim que você encontrará os bugs mais importantes, não testando suas VMs.
É realmente um investimento. Você precisa entender os conceitos básicos do WPF / SL e XAML primeiro e, em seguida, descobrir as maneiras de fazer as ligações corretamente, conectar-se vs ao vms em alguma ordem, acertar o comando, escolher uma estrutura na maioria dos casos, o que poderia seja problemático devido ao licenciamento, construa uma biblioteca de sippets para codificar com eficiência, apenas para descobrir que a plataforma nem sempre funciona bem com ligações e que é necessário criar uma biblioteca de comportamentos que lhe dão o que você precisa.
No geral, porém - se você superar todos os obstáculos e se tornar bastante eficiente no padrão - tudo retribui com clareza, facilidade de manutenção e ... direito de se gabar? :)
fonte
Sou programador do WPF / Silverlight há anos criando aplicativos enormes, como sistemas de negociação, no MVVM.
Para mim, com o passar dos anos, aprendi que o MVVM rigoroso consome tempo e custa dinheiro. Por estrito, quero dizer regras como "sem código por trás".
É impossível em qualquer coisa, exceto no aplicativo de banco de dados / formulário mais básico, não ter código por trás.
Seu designer especificará algo no primeiro dia que não é possível com os controles padrão; portanto, você deve criar uma série de controles personalizados ou agrupar os controles existentes para suportar o paradigma de interação enquanto trabalha com o MVVM.
Eu escrevi todos os tipos de controles de interface do usuário swish usando matemática, inércia, gestos etc. e seu trabalho duro.
Mas isso é tudo por trás do código, simplesmente não está na visão. Você tem que lidar com cliques de botão, rolagem, arraste etc., mas tudo está bem envolvido em um conjunto de controles, o que de alguma forma torna esse código por trás correto.
Muitas vezes, é mais fácil simplesmente escrever o código por trás e as coisas inteligentes da interface do usuário na visualização, em vez de criar um conjunto de controles apenas por isso.
O objetivo do MVVM é separar a lógica do aplicativo / negócios da lógica da interface do usuário. O sistema de ligação e o MVVM são uma boa maneira de fazer isso.
Os outros objetivos apontados, como o designer XAML em uma mesa, o programador C # na outra, um trabalhando no Blend o outro no VS, são uma falácia.
Ser capaz de redesenhar rapidamente uma interface do usuário é outra falácia. Isso nunca acontece, sua otimização prematura; qualquer retrabalho importante precisa de muito trabalho nos modelos de exibição. Ajustar é uma coisa, mas as revisões rápidas da interface do usuário não são possíveis; os modelos de visualização devem se encaixar no paradigma de interação, para que o acoplamento seja mais rígido do que você imagina.
Para mim, eu uso o MVVM para manter minha lógica de aplicativo separada (eu geralmente uso um controlador testável e um conjunto de modelos de exibição sem lógica), mas qualquer código e truques são necessários para fazer com que minha interface do usuário pareça e atue de uma maneira sexy, eu não suar.
Caso contrário, você acabará reinando em seus projetos, animações legais etc. apenas porque a idéia de como MVVM-arizar tudo se torna muito incompreensível.
O MVVM, como a maioria das coisas na vida, tem um ponto ideal entre dois extremos.
O mais importante no design de software é enviar o produto com antecedência, descobrir quais recursos são usados, qual interface do usuário funciona bem e não escrever um código bonito para um produto que ninguém usa.
fonte
Se o seu aplicativo exigir que você se ligue a quantidades excessivas de dados em tempo real, o MVVM poderá realmente atrapalhar, pois está introduzindo abstrações que atrasam o processo e, supondo que estamos falando sobre WPF / Silverlight / WP7 agora, a ligação o motor atualmente não é tão eficiente; embora melhorias estejam a caminho.
MVVM, como está, também não é a única parte da equação que você precisa considerar. O MVVM puro precisa ser suportado com infraestrutura, como o padrão Mediador, para permitir a comunicação entre os ViewModels desconectados.
Apesar da opinião de Blucz acima, o MVVM está na linha dos padrões GoF. Se você der uma olhada nos padrões, o MVVM é o equivalente ao padrão Model View Passive Presenter, que apareceu aproximadamente ao mesmo tempo que o MVVM. Muitas vezes, aqui, as pessoas reclamam da complexidade do MVVM simplesmente porque não entendem como projetar usando-o.
Outra área em que encontrei problemas com o MVVM é onde você precisa incorporar uma tecnologia de terceiros que não foi projetada para dar suporte a ela (como a estrutura UII do MS Dynamics). Às vezes, você precisa se perguntar se vale a pena "invadir" a outra tecnologia apenas para trabalhar com o MVVM.
Se você estiver misturando e combinando algo como o Win Forms à sua solução WPF, essa parte da solução provavelmente também não será adequada para o MVVM. Espero que isso lhe dê uma idéia de algumas áreas sobre onde o MVVM não é aplicável.
fonte
O uso do MVVM não faz sentido quando:
É isso aí. Realmente não consigo pensar por que você NÃO usaria o MVVM ao trabalhar com o WPF / Silverlight, a menos que esteja testando ou depurando algo em um projeto separado. Acho o padrão de design ideal para o desenvolvimento do WPF / Silverlight devido à maneira como as ligações XAML funcionam.
O ponto principal do MVVM é que todo o seu aplicativo é executado no seu ViewModels, e o seu Views é simplesmente uma camada bonita que os usuários podem usar para interagir com o seu ViewModels. Você quer clicar em um botão, na verdade está executando um método ViewModel. Você deseja alterar a página, na verdade está alterando a propriedade CurrentPage no ViewModel.
Eu uso o MVVM para todo o desenvolvimento do WPF / Silverlight, mesmo projetos pequenos e simples de página única, embora a forma como eu uso o MVVM seja diferente com base no tamanho do projeto e no que eu preciso. A única vez que fiz um aplicativo pequeno sem MVVM, acabei me arrependendo (mais tarde foi refatorado para usar o MVVM ao adicionar uma atualização)
fonte
Eu trabalhei para um cliente em um projeto que era code-behind com uma tentativa de converter para MVVM lançada e era uma bagunça gigante. Isso não quer dizer que todos os projetos code-behind são uma bagunça. As visualizações causariam erro ou travariam o Blend, o que causou problemas para os designers.
Eu brinquei com o RoR e praticamente deixei de fazer qualquer coisa com o ASP.NET Webforms, mas quando o ASP.NET MVC foi lançado, tentei aprender o máximo que pude, geralmente usando os livros do ASP.NET MVC In Action e o codecampserver como referência. . Embora seja um padrão diferente, uso muitas das coisas que aprendi nos livros Em Ação no desenvolvimento do SL / MVVM.
Quando comecei a trabalhar com o Silverlight, fiquei surpreso com o quão nu estava e parecia um requisito escolher uma das muitas estruturas disponíveis para se vestir. Eu vejo isso como um problema com as pessoas desistindo de aprender MVVM.
Comecei com o excelente MVVM-Light, que me ajudou a entender o padrão. Mais tarde, comecei a trabalhar com o Caliburn Micro e as luzes se acenderam. Para mim, o Caliburn Micro é como usar o ASP.NET MVC sobre o ASP.NET Webforms. Portanto, mesmo para aplicativos de amostra pequenos, crio o projeto, NuGet CM, e estou em execução. Sigo a primeira abordagem do ViewModel e mantenho meu Views burro e fácil de trabalhar no Blend. Minhas VMs são testáveis se você gosta disso e, com o CM, é muito fácil escolher layouts de tela complexos.
fonte
Você pode conferir essa alternativa. Essa opção contorna a maneira WPF de ligação de dados, datatemplates e MVVM. Essa opção é mais parecida com a abordagem antiga, simples e confiável do WinForms designer.cs.
Compósitos WPF
http://wpfcomposites.codeplex.com/
Aqui, o code-behind C # conciso para o WPF é produzido sobrepondo uma matriz simples na parte superior da interface do usuário por meio de compostos baseados em grade. Se uma interface de usuário XAML moderna contém apenas alguns rótulos de texto e uma caixa de listagem de fotos, por que isso não pode ser definido pela simples linha de código: grid.AddText (guid, x coordenada e coordenada y)? Observe que isso não está em uma tela, mas ainda dentro dos contêineres do WPF: grade, painel de encaixe etc. O WPF é extremamente poderoso. Essa abordagem apenas aproveita isso.
Os desenvolvedores normalmente não se importam com matrizes e índices. Comece com um nível de contêiner de granulação grossa definido pelos GUIDs dos objetos de transferência de dados (DTOs) ou POCOs e, em seguida, complemente esses contêineres com chave por uma matriz de granulação mais fina de linhas e colunas em potencial?
O projeto acima do codeplex introduz essa abordagem, iniciando com o controle ListBox, mas está se expandindo para abranger todos os controles compostos do WPF. Por exemplo, cada listboxitem é um composto adicionado com um GUID (um composto vincula um a um a uma classe); então, dentro deste item, há uma Grade na qual os elementos filho da interface do usuário (os filhos vinculam um a um às propriedades em uma classe) pode ser adicionado à vontade via code-behind. As crianças podem ser blocos de texto ou imagens.
A idéia é aproveitar os índices e uma estrutura de elemento de interface do usuário bem definida (força um ListBox com o tema PresentationFramework.Aero como base para começar) em vez de placas de dados. Essa nova abordagem limita propositadamente o que pode ser feito, mas, ao fazê-lo, gera um código C # robusto e conciso, intuitivo. Não é necessário procurar soluções de modelo de controle para estilizar uma barra de rolagem ou desorganizar uma solução com vários modelos de dados para tarefas simples.
fonte
O MVVM é baseado principalmente na estrutura da interface do usuário que estou usando. Portanto, com algo como o WPF ou o Silverlight que possui um paradigma baseado na ligação a um datacontext, esse datacontext sempre pode ser chamado de modelo de exibição.
Portanto, a pergunta para mim é quando você cria uma classe separada grande e gorda, que é o modelo de visualização para esse controle / janela / aplicativo e quando você pode se safar usando os objetos que já foram criados.
Portanto, temos uma pergunta clássica sobre como adicionar uma camada de abstração. a VM adicionará peso, inércia e estrutura a um projeto. Isso tornará mais fácil a construção, mas mais difícil de mudar e mais longo. Nenhuma dessas coisas facilmente quantificáveis.
Para uma resposta barata, o MVVM não é apropriado para aplicativos de linha de comando ou serviços da web :)
fonte