O MVVM é inútil? [fechadas]

91

A implementação do MVVM ortodoxa é inútil? Estou criando um novo aplicativo e considerei Windows Forms e WPF. Escolhi o WPF porque é à prova de futuro e oferece muita flexibilidade. Há menos código e é mais fácil fazer alterações significativas em sua interface do usuário usando XAML.

Uma vez que a escolha do WPF é óbvia, percebi que também posso ir até o fim usando MVVM como minha arquitetura de aplicativo, uma vez que oferece capacidade de mistura, questões de separação e testabilidade de unidade. Teoricamente, parece bonito como o Santo Graal da programação da IU. Esta breve aventura; no entanto, tornou-se uma verdadeira dor de cabeça. Como esperado na prática, estou descobrindo que troquei um problema por outro. Tendo a ser um programador obsessivo, quero fazer as coisas da maneira certa para obter os resultados certos e, possivelmente, me tornar um programador melhor. O padrão MVVM acabou de ser reprovado em meu teste de produtividade e acabou de se tornar um grande hack nojento!

O caso claro é adicionar suporte para uma caixa de diálogo Modal. A maneira correta é abrir uma caixa de diálogo e vinculá-la a um modelo de vista. Fazer isso funcionar é difícil. Para se beneficiar do padrão MVVM, você deve distribuir o código em vários lugares através das camadas de seu aplicativo. Você também deve usar construções de programação esotéricas, como modelos e expressões de lamba. Coisas que fazem você ficar olhando para a tela e coçando a cabeça. Isso torna a manutenção e a depuração um pesadelo esperando para acontecer, como descobri recentemente. Eu tinha uma caixa sobre funcionando bem até que recebi uma exceção na segunda vez que a invoquei, dizendo que ela não poderia mostrar a caixa de diálogo novamente depois de fechada. Tive que adicionar um manipulador de eventos para a funcionalidade de fechamento da janela de diálogo, outro na implementação IDialogView dele e finalmente outro no IDialogViewModel. Achei que o MVVM nos salvaria de tal hackeagem extravagante!

Existem várias pessoas por aí com soluções concorrentes para este problema e todas são hacks e não fornecem uma solução limpa, facilmente reutilizável e elegante. A maioria dos kits de ferramentas MVVM encobrem os diálogos e, quando os abordam, são apenas caixas de alerta que não requerem interfaces personalizadas ou modelos de visualização.

Estou planejando desistir do padrão de visualização MVVM, pelo menos sua implementação ortodoxa dele. O que você acha? Valeu a pena se você tivesse algum? Sou apenas um programador incompetente ou o MVVM não é o que foi anunciado?

Joel Rodgers
fonte
6
Sempre me perguntei se o MVVM é um excesso de engenharia ou não. Pergunta interessante.
Taylor Leese
11
Padrões como MVVM e MVC parecem engenharia exagerada, até que você precise realizar algumas modificações ou alterar um componente. A primeira vez que você tem que fazer isso, toda a cerimônia se paga.
Robert Harvey
41
Lambdas são esotéricos? novidade para mim.
Ray Booysen
5
@Ray - Haha, +1 por esse comentário! : D
Venemo
7
Como Alan Cooper apontou há mais de uma década em About Face , se você está projetando interfaces de usuário e diálogos modais não são um caso extremo, provavelmente você está fazendo algo errado.
Robert Rossney

Respostas:

61

Desculpe se minha resposta ficou um pouco longa, mas não me culpe! Sua pergunta também é longa.

Em resumo, o MVVM não é inútil.

O caso claro é adicionar suporte para uma caixa de diálogo Modal. A maneira correta é abrir uma caixa de diálogo e vinculá-la a um modelo de vista. Fazer isso funcionar é difícil.

Sim, realmente é.
No entanto, o MVVM fornece uma maneira de separar a aparência da IU de sua lógica. Ninguém o força a usá-lo em todos os lugares e ninguém está segurando uma arma contra sua testa para fazer com que você crie um ViewModel separado para tudo.

Aqui está minha solução para este exemplo específico:
Como a IU lida com uma determinada entrada não é da conta do ViewModel. Eu adicionaria código ao arquivo .xaml.cs do View, que instancia a caixa de diálogo e define a mesma instância de ViewModel (ou outra coisa, se necessário) como seu DataContext.

Para se beneficiar do padrão MVVM, você deve distribuir o código em vários lugares através das camadas de seu aplicativo. Você também deve usar construções de programação esotéricas, como modelos e expressões de lamba.

Bem, você não precisa usá-lo em vários lugares. É assim que eu resolveria:

  • Adicione o XAML ao modo de exibição e nada no .xaml.cs
  • Escreva cada lógica de aplicativo (exceto as coisas que operariam diretamente com os elementos da IU) dentro de um ViewModel
  • Todo o código que deve ser feito pela IU, mas não tem nada a ver com a lógica de negócios, vai para os arquivos .xaml.cs

Acho que o objetivo do MVVM é principalmente separar a lógica do aplicativo e a IU concreta, portanto, permitindo modificações fáceis (ou substituição completa) da IU.
Eu uso o seguinte princípio: o View pode saber e assumir qualquer coisa que quiser do ViewModel, mas o ViewModel não pode saber NADA sobre o View.
O WPF fornece um bom modelo de ligação que você pode usar para conseguir exatamente isso.

(Aliás, os modelos e as expressões lambda não são esotéricos se usados ​​corretamente. Mas se você não quiser, não os use.)

Coisas que fazem você ficar olhando para a tela e coçando a cabeça.

Sim, eu conheço o sentimento. Exatamente o que eu estava sentindo quando vi o MVVM pela primeira vez. Mas depois que você pegar o jeito, não vai se sentir mal mais.

Eu tinha uma caixa sobre funcionando bem ...

Por que você colocaria um ViewModel atrás de uma caixa sobre? Não há sentido nisso.

A maioria dos kits de ferramentas MVVM encobrem os diálogos e, quando os abordam, são apenas caixas de alerta que não requerem interfaces personalizadas ou modelos de visualização.

Sim, porque o próprio fato de um elemento da interface do usuário estar na mesma janela, ou em outra janela, ou orbitando Marte no momento, não é da preocupação dos ViewModels.
Separação de preocupações

EDITAR:

Aqui está um vídeo muito bom, cujo título é Construa seu próprio framework MVVM . Vale a pena assistir.

Venemo
fonte
2
+1 nas últimas três palavras. Mas o resto da resposta também é bom. :)
Robert Harvey
12
+1 para o conselho sobre como usar code-behind. É um equívoco comum que é "ruim" usar code-behind no MVVM ... mas para coisas puramente relacionadas à IU, esse é o caminho a percorrer.
Thomas Levesque
@Thomas: Sim, eu não poderia concordar mais. Eu vi várias implementações onde as pessoas colocam todo o código (mesmo relacionado à IU) no ViewModel, porque (de acordo com eles) "é onde o código está". Foi bastante hacky.
Venemo
3
@Venemo, eu realmente acho que você pode encapsular muitas das coisas que gostaria de colocar no code-behind usando técnicas como Behaviors personalizados, o que é útil se você se pega escrevendo código cola repetidamente. Em geral, porém, acho que é melhor usar code-behind para cola do que hackear XAML estranho. A principal preocupação, em minha opinião, é ter certeza de que não há nada no code-behind sofisticado o suficiente para justificar o teste de unidade. Qualquer coisa suficientemente complexa é melhor encapsulada no ViewModel ou em uma classe de extensão, como Behavior ou MarkupExtension.
Dan Bryant
7
@ Thomas: Você está certo, o maior mito sobre o MVVM é que o propósito do MVVM é se livrar do code-behind. O objetivo é obter o ocde Não-IU do código subjacente. Colocar código somente da IU no ViewModel é tão ruim quanto colocar o código do domínio do problema no code-behind.
Jim Reineri
8

Fazer isso funcionar é difícil. Para se beneficiar do padrão MVVM, você deve distribuir o código em vários lugares através das camadas de seu aplicativo. Você também deve usar construções de programação esotéricas, como modelos e expressões de lamba.

Para uma caixa de diálogo modal comum? Você certamente está fazendo algo errado aí - a implementação do MVVM não precisa ser tão complexa.

Considerando que você é novo no MVVM e no WPF, é provável que esteja usando soluções abaixo do ideal em todos os lugares e complicando as coisas desnecessariamente - pelo menos eu fiz isso quando comecei o WPF. Certifique-se de que o problema seja realmente MVVM e não sua implementação antes de desistir.

MVVM, MVC, Document-View, etc. é uma velha família de padrões. Existem desvantagens, mas nenhuma falha fatal do tipo que você descreve.

Eu sou um
fonte
5

Estou no meio de um desenvolvimento bastante complexo do MVVM usando PRISM, então já tive que lidar com esse tipo de preocupação.

Minhas conclusões pessoais:

MVVM vs MVC / PopUps & co

  • MVVM é realmente um ótimo padrão e na maioria dos casos ele substitui completamente o MVC graças à poderosa ligação de dados no WPF
  • Chamar sua camada de serviço diretamente do apresentador é uma implementação legítima na maioria dos casos
  • Mesmo cenários bastante complexos de Lista / Detalhe podem ser implementados por MVVM puro graças à sintaxe {Binding Path = /}
  • No entanto, quando a coordenação complexa entre múltiplas visualizações precisa ser implementada, um controlador é obrigatório
  • Os eventos podem ser usados; o padrão antigo que implica o armazenamento de instâncias IView (ou AbstractObserver) no controlador é obsoleto
  • O controlador pode ser injetado em cada apresentador por contêiner IOC
  • O serviço IEventAggregator da Prism é outra solução possível se o único uso do controlador for o envio de eventos (neste caso, ele pode substituir completamente o controlador)
  • Se as visualizações devem ser criadas dinamicamente, este é um trabalho muito adequado para o controlador (no prisma, o controlador será injetado (IOC) um IRegionManager)
  • As caixas de diálogo modais são em sua maioria obsoletas em aplicativos compostos modernos, exceto para realmente bloquear operações como confirmações obrigatórias; nesses casos, a ativação modal pode ser abstraída como um serviço chamado dentro do controlador e implementado por uma classe especializada, o que também permite testes de unidade de nível de apresentação avançados. O controlador irá, por exemplo, chamar IConfirmationService.RequestConfirmation (“tem certeza”), que irá acionar uma exibição de diálogo modal em tempo de execução e pode ser facilmente simulado durante o teste de unidade
dan ionescu
fonte
5

Eu lido com o problema dos diálogos trapaceando. Minha MainWindow implementa uma interface IWindowServices que expõe todas as caixas de diálogo específicas do aplicativo. Meus outros ViewModels podem importar a interface de serviços (eu uso o MEF, mas você poderia simplesmente passar a interface pelos construtores manualmente) e usá-la para realizar o que for necessário. Por exemplo, aqui está a aparência da interface para um pequeno aplicativo utilitário meu:

//Wrapper interface for dialog functionality to allow for mocking during tests
public interface IWindowServices
{
    bool ExecuteNewProject(NewProjectViewModel model);

    bool ExecuteImportSymbols(ImportSymbolsViewModel model);

    bool ExecuteOpenDialog(OpenFileDialog dialog);

    bool ExecuteSaveDialog(SaveFileDialog dialog);

    bool ExecuteWarningConfirmation(string text, string caption);

    void ExitApplication();
}

Isso coloca todas as execuções do Dialog em um só lugar e pode ser facilmente removido para teste de unidade. Eu sigo o padrão que o cliente da caixa de diálogo tem para criar o ViewModel apropriado, que ele pode configurar conforme necessário. A chamada Execute bloqueia e depois o cliente pode olhar o conteúdo do ViewModel para ver os resultados do Dialog.

Um design MVVM mais 'puro' pode ser importante para um aplicativo grande, onde você precisa de um isolamento mais limpo e uma composição mais complexa, mas para aplicativos de pequeno e médio porte, acho que uma abordagem prática, com serviços apropriados para expor os ganchos necessários, é suficiente .

Dan Bryant
fonte
Mas de onde você chama isso? Não seria melhor apenas construir o diálogo dentro das funções da classe IWindowServices em vez de transmiti-lo. Dessa forma, a visualização do modelo de chamada não teria que saber nada sobre a implementação de diálogo particular.
Joel Rodgers
A interface é injetada em qualquer uma das minhas instâncias de ViewModel que precise acessar os diálogos do aplicativo. Na maioria dos casos, estou passando ViewModel para a caixa de diálogo, mas fiquei um pouco preguiçoso e usei o WPF OpenFileDialog e SaveFileDialog para as chamadas de diálogo de arquivo. Meu objetivo principal era o isolamento para fins de teste de unidade, portanto, isso é suficiente para esse objetivo. Se você quisesse um isolamento melhor, provavelmente desejaria criar um OpenFileViewModel e SaveFileViewModel, o que duplicaria as propriedades necessárias para os diálogos.
Dan Bryant
Observe que esta definitivamente não é uma abordagem pura, pois o ViewModel que está usando as caixas de diálogo conhece o ViewModel específico para cada caixa de diálogo que deseja abrir. Eu sinto que isso é bastante claro, mas você sempre pode adicionar uma camada adicional de isolamento com uma classe que expõe puramente os parâmetros necessários para o uso do diálogo, ocultando qualquer visibilidade desnecessária das propriedades ViewModel usadas durante a vinculação. Para aplicações menores, acho que esse isolamento adicional é um exagero.
Dan Bryant
5

Os padrões de design existem para ajudá-lo, não para atrapalhar. Uma pequena parte de ser um bom desenvolvedor é saber quando "quebrar as regras". Se o MVVM for complicado para uma tarefa e você determinou que o valor futuro não vale o esforço, não use o padrão. Por exemplo, como outros participantes comentaram, por que você examinaria todo o overhead para implementar uma caixa sobre simples?

Os padrões de projeto nunca foram planejados para serem seguidos dogmaticamente.

RMart
fonte
2
Corrigir. Uma caixa sobre simples deve ser simples, mas e se você tiver que exibir informações como versão, licenciamento, processo em execução, nome da empresa, etc. Todas essas informações estão em algum lugar. Em formulários padrão, você pode apenas vincular todas as informações e pronto. O MVVM diz que você deve criar um modelo de visualização para ele, que é redundante.
ATL_DEV
1

Como o próprio padrão, o MVVM é ótimo. Mas a biblioteca de controle do WPF enviada com suporte a vinculação de dados NET 4.0 é muito limitada, é muito melhor que o WinForm, mas ainda não é suficiente para MVVM vinculável, eu diria que sua potência é cerca de 30% do que é necessário para MVVM vinculável.
MVVM vinculável: é a IU em que ViewModel é conectado com View apenas usando vinculação de dados.
O padrão MVVM é sobre a representação de objeto do ViewState, ele não descreve como você mantém a sincronização entre View e ViewModel, no WPF é vinculação de dados, mas pode ser qualquer coisa. E, na verdade, você pode usar o padrão MVVM em qualquer kit de ferramentas de IU que suporte eventos \ retornos de chamada, você pode usá-lo em WinAPI puro em WinForms (eu usei, e não é muito mais trabalho com eventos \ retornos de chamada), e você pode até mesmo usá-lo em Texto Console, como reescrever o Norton Commander do DoS usando o padrão MVVM.

Resumindo: o MVVM não é inútil, é ótimo. A biblioteca de controle do NET 4.0 WPF é um lixo.

Aqui está a simples prova de conceito ViewModel, que você não pode vincular com dados no modo MVVM puro usando WPF.

public class PersonsViewModel
{
    public IList<Person> PersonList;
    public IList<ColumnDescription> TableColumns;
    public IList<Person> SelectedPersons;
    public Person ActivePerson;
    public ColumnDescription SortedColumn;
}

Você não pode vincular os cabeçalhos de coluna DataGrid do WPF, você não pode vincular as linhas selecionadas, etc etc, você vai fazer isso de forma simples de código ou escrever 200 linhas de código de hack XAML para essas 5 linhas do ViewModel mais simples. Você só pode imaginar como as coisas pioram com ViewModels complexos.
Portanto, a resposta é simples, a menos que você esteja escrevendo um aplicativo Hello World; usar MVVM vinculável no WPF é inútil. Você passará a maior parte do tempo pensando em hackear seu ViewModel. A vinculação de dados é boa, mas esteja pronto para retornar ao evento 70% do tempo.

Alex Burtsev
fonte
Você poderia vincular isso, com conversores, a um DataGrid.
Cameron MacFarland
@CameronMacFarland: Nem todos, algumas propriedades são somente leitura e não vinculáveis, algumas simplesmente não existem e existem apenas eventos que relatam mudança de estado.
Alex Burtsev
Admito que não tenho muita experiência com o WPF DataGrid. Eu tendo a evitá-lo porque é feio e não se encaixa mais no WPF. Dito isto, uma combinação de conversores e AttachedProperties para lidar com eventos deve fornecer o que você precisa.
Cameron MacFarland
1
Alex, os problemas que você está tendo são com o design do DataGrid, não com o MVVM. É simplesmente incorreto dizer que "A vinculação de dados é boa, mas esteja pronto para retornar ao evento 70% do tempo." Eu escrevi alguns aplicativos WPF objetivamente grandes nos quais não há manipuladores de eventos na IU de qualquer espécie - com exceção do manipulador de eventos que a grade de dados (Telerik) precisa para inicialização.
Robert Rossney
3
Acho que você teria mais sucesso se, em vez de adotar a atitude: "Isso foi mal planejado e não funciona", você tentasse: "Por que isso está funcionando para outras pessoas, mas não para mim?" Você pode descobrir que a razão pela qual as coisas são difíceis de fazer é que você ainda não sabe como fazê-las.
Robert Rossney
0

Não, não é inútil, mas é difícil entender, embora o padrão em si seja ridiculamente simples. Existem toneladas de desinformação por aí e vários grupos que lutam pela maneira adequada. Eu acho que com WPF e Silverlight você deve usar MVVM ou você estará codificando demais e tentando resolver problemas em um novo modelo, a metodologia “antiga” de formulários de ganho que apenas o leva a problemas. Este é mais o caso no Silverlight, uma vez que tudo deve ser assíncrono (hacks em torno disso são possíveis, mas você deve apenas escolher outra plataforma).

Eu sugiro ler este artigo Simplificando o WPF TreeView usando o padrão ViewModel cuidadosamente para ver como o MVVM pode ser bem implementado e permitir que você mude sua mentalidade de win forms para a nova maneira de pensar em MVVM. Em suma, quando você quiser fazer algo, aplique a lógica ao ViewModel primeiro, não à View. Você quer selecionar um item? Alterar um ícone? Não itere sobre os elementos da interface do usuário, apenas atualize as propriedades do modelo e deixe a vinculação de dados resolver o problema.

TugboatCaptain
fonte
-1

Tenho visto o mesmo problema com muitas implementações de MVVM quando se trata de diálogos (modais). Quando olho para os participantes do Padrão MVVM, tenho a sensação de que falta algo para construir um aplicativo coerente.

  • View contém os controles GUI específicos e define a aparência da interface do usuário.
  • ViewModel representa o estado e o comportamento da apresentação.
  • O modelo pode ser um objeto de negócios da camada de domínio ou um serviço que fornece os dados necessários.

Mas falta:

  • Quem cria os ViewModels?
  • Quem é responsável pelo fluxo de trabalho do aplicativo?
  • Quem faz a mediação entre os ViewModels quando eles precisam se comunicar?

Minha abordagem é apresentar um Controlador (caso de uso) que é responsável pelos pontos ausentes. Como isso funciona pode ser visto nos aplicativos de exemplo WPF Application Framework (WAF) .

jbe
fonte
O padrão Mediator implementado por Josh Smith resolveu todos os meus problemas de comunicação do View Model. Messenger.NotifyColleagues forneceu uma maneira de ter modelos de visão completamente independentes que sabiam como responder a eventos globais (se importassem) sem ter dois modelos de visão se conhecendo. Já salvou nosso bacon algumas vezes.
JasonD