Estou criando um aplicativo WPF usando o padrão MVVM. No momento, meus viewmodels chamam a camada de serviço para recuperar modelos (como não é relevante para o viewmodel) e convertê-los em viewmodels. Estou usando a injeção de construtor para passar o serviço necessário para o viewmodel.
É facilmente testável e funciona bem para viewmodels com poucas dependências, mas assim que tento criar viewModels para modelos complexos, tenho um construtor com MUITOS serviços injetados (um para recuperar cada dependência e uma lista de todos os valores disponíveis para vincular a um itemsSource, por exemplo). Eu estou querendo saber como lidar com vários serviços como esse e ainda tenho um modelo de exibição que eu possa testar facilmente.
Estou pensando em algumas soluções:
Criando um singleton de serviços (IServices) contendo todos os serviços disponíveis como interfaces. Exemplo: Services.Current.XXXService.Retrieve (), Services.Current.YYYService.Retrieve (). Dessa forma, não tenho um construtor enorme com muitos parâmetros de serviços.
Criando uma fachada para os serviços usados pelo viewModel e passando esse objeto no ctor do meu viewmodel. Mas então, terei que criar uma fachada para cada um dos meus modelos de visualização complexos, e pode ser um pouco demais ...
O que você acha que é a maneira "certa" de implementar esse tipo de arquitetura?
fonte
new
para criar outros modelos de vista, mas pense em algo tão simples quanto um aplicativo MDI em que clicar no botão ou menu "novo documento" adicionará uma nova guia ou abrirá uma nova janela. O shell / condutor deve ser capaz de criar novas instâncias de algo , mesmo se estiver oculto atrás de uma ou algumas camadas de indireção.Respostas:
De fato, essas duas soluções são ruins.
Este é essencialmente o Service Locator Pattern , que é um antipadrão. Se você fizer isso, não poderá mais entender do que o modelo de visualização realmente depende sem examinar sua implementação privada, o que tornará muito difícil testar ou refatorar.
Isso não é tanto um anti-padrão, mas é um cheiro de código. Essencialmente, você está criando um objeto de parâmetro , mas o objetivo do padrão de refatoração do pedido é lidar com conjuntos de parâmetros que são usados com frequência e em muitos lugares diferentes , enquanto esse parâmetro só seria usado uma vez. Como você mencionou, isso criaria um grande número de códigos sem nenhum benefício real e não funcionaria bem com muitos contêineres de IoC.
De fato, ambas as estratégias acima estão negligenciando a questão geral, que é que o acoplamento é muito alto entre os modelos e serviços de exibição . Simplesmente ocultar essas dependências em um localizador de serviço ou objeto de parâmetro na verdade não altera de quantos outros objetos o modelo de exibição depende.
Pense em como você testaria em unidade um desses modelos de exibição. Qual será o tamanho do seu código de configuração? Quantas coisas precisam ser inicializadas para que funcione?
Muitas pessoas que começam com o MVVM tentam criar modelos de exibição para uma tela inteira , o que é fundamentalmente a abordagem errada. O MVVM tem tudo a ver com composição , e uma tela com muitas funções deve ser composta por vários modelos de vista diferentes, cada um dos quais depende de apenas um ou alguns modelos / serviços internos. Se eles precisam se comunicar, você o faz através de pub / sub (intermediário de mensagens, barramento de eventos, etc.)
O que você realmente precisa fazer é refatorar seus modelos de exibição para que eles tenham menos dependências . Então, se você precisar ter uma "tela" agregada, crie outro modelo de vista para agregar os modelos de vista menores. Esse modelo de visualização agregado não precisa fazer muito por si só, portanto, por sua vez, também é bastante fácil de entender e testar.
Se você fez isso corretamente, deve ser óbvio apenas olhando o código, porque você terá modelos de visualização curtos, sucintos, específicos e testáveis.
fonte
Eu poderia escrever um livro sobre isso ... na verdade eu sou;)
Primeiro, não existe uma maneira universalmente "correta" de fazer as coisas. Você precisa levar outros fatores em consideração.
É possível que seus serviços sejam granulados demais. Agrupar os serviços com fachadas que fornecem a interface que um Viewmodel específico ou mesmo um cluster de ViewModels relacionados usaria pode ser uma solução melhor.
Mais simples ainda seria agrupar os serviços em uma única fachada que todos os modelos de exibição usam. É claro que isso pode ser uma interface muito grande com muitas funções desnecessárias para o cenário médio. Mas eu diria que não é diferente de um roteador de mensagens que lida com todas as mensagens no seu sistema.
De fato, o que vi muitas arquiteturas eventualmente evoluirem é um barramento de mensagens construído em torno de algo como o padrão Agregador de Eventos. O teste é fácil porque a classe de teste apenas registra um ouvinte no EA e dispara o evento apropriado em resposta. Mas esse é um cenário avançado que leva tempo para crescer. Eu digo começar com a fachada unificadora e partir daí.
fonte
Por que não combinar os dois?
Crie uma fachada e coloque todos os serviços que seus modelos de exibição usam. Então você pode ter uma fachada única para todos os seus modelos de exibição sem a palavra S. ruim.
Ou você pode usar injeção de propriedade em vez de injeção de construtor. Mas, então, você precisa garantir que eles estejam sendo injetados corretamente.
fonte