Como configuro o MVP para uma solução Winforms?

14

Eu usei MVP e MVC no passado, e prefiro o MVP, pois ele controla o fluxo de execução muito melhor na minha opinião.

Criei minha infraestrutura (classes de armazenamento de dados / repositório) e as utilizo sem problemas ao codificar dados de amostra, agora estou migrando para a GUI e preparando meu MVP.

Seção a

  1. Vi o MVP usando a visualização como ponto de entrada, ou seja, no método construtor de visualizações, ele cria o apresentador, que por sua vez cria o modelo, conectando os eventos conforme necessário.

  2. Eu também vi o apresentador como o ponto de entrada, onde uma visão, modelo e apresentador são criados; esse apresentador recebe uma visão e um objeto de modelo em seu construtor para conectar os eventos.

  3. Como em 2, mas o modelo não é passado para o apresentador. Em vez disso, o modelo é uma classe estática na qual os métodos são chamados e as respostas retornadas diretamente.

Seção B

Em termos de manter a visão e o modelo sincronizados, eu já vi.

  1. Sempre que um valor na visualização é alterado, ou seja, TextChangedevento em .Net / C #. Isso aciona uma DataChangedEventpassagem pelo modelo, para mantê-lo sincronizado o tempo todo. E onde o modelo muda, ou seja, um evento em segundo plano que ele escuta, a visualização é atualizada com a mesma idéia de aumentar a DataChangedEvent. Quando um usuário deseja confirmar alterações, SaveEventele é acionado, passando para o modelo para salvar. Nesse caso, o modelo imita os dados da visualização e processa ações.

  2. Semelhante ao # b1, no entanto, a visualização não é sincronizada com o modelo o tempo todo. Em vez disso, quando o usuário deseja confirmar as alterações, SaveEventé demitido e o apresentador pega os detalhes mais recentes e os passa para o modelo. nesse caso, o modelo não conhece os dados das visualizações até que seja necessário agir sobre ele; nesse caso, são transmitidos todos os detalhes necessários.

Seção C

Exibição de objetos de negócios na exibição, ou seja, um objeto (MyClass) não dados primitivos (int, double)

  1. A visualização possui campos de propriedades para todos os dados que serão exibidos como objetos de domínio / negócios. Tal como view.Animalsexpõe uma IEnumerable<IAnimal>propriedade, mesmo que a exibição as processe em Nós em um TreeView. Então, para o animal selecionado, ele seria exposto SelectedAnimalcomo IAnimalpropriedade.

  2. A exibição não tem conhecimento de objetos de domínio, ela expõe propriedades apenas para tipos de objetos incluídos primitivos / framework (.Net / Java). Nesse caso, o apresentador passará um objeto do adaptador para o objeto de domínio, e o adaptador converterá um determinado objeto de negócios nos controles visíveis na exibição. Nesse caso, o adaptador deve ter acesso aos controles reais na visualização, e não apenas qualquer visualização, tornando-se mais acoplado.

Seção D

Várias visualizações usadas para criar um único controle. ou seja, você tem uma visão complexa com um modelo simples, como salvar objetos de diferentes tipos. Você pode ter um sistema de menus ao lado a cada clique em um item, os controles apropriados são mostrados.

  1. Você cria uma visão enorme, que contém todos os controles individuais expostos pela interface de visualizações.

  2. Você tem várias visualizações. Você tem uma visão para o menu e um painel em branco. Essa visão cria as outras visões necessárias, mas não as exibe (visible = false); essa visão também implementa a interface para cada visão que ela contém (ou seja, visões filhas) para que possa ser exposta a um apresentador. O painel em branco é preenchido com outras visualizações ( Controls.Add(myview)) e ( (myview.visible = true). Os eventos gerados nessas visualizações "filho" são tratados pela visualização pai, que por sua vez passa o evento para o apresentador e vice-versa para fornecer eventos de volta aos elementos filhos.

  3. Cada visualização, seja a pai principal ou a filha menor, é conectada a cada apresentador e modelo. Você pode literalmente soltar um controle de exibição em um formulário existente e ele terá a funcionalidade pronta, basta conectar o apresentador nos bastidores.

Seção E

Se tudo tiver uma interface, agora, com base em como o MVP é feito nos exemplos acima, afetará esta resposta, pois eles podem não ser compatíveis.

  1. Tudo tem uma interface, o View, Presenter e Model. Cada uma delas obviamente tem uma implementação concreta. Mesmo se você tiver apenas uma visão concreta, modelo e apresentador.

  2. A Vista e o Modelo têm uma interface. Isso permite que as visualizações e os modelos sejam diferentes. O apresentador cria / recebe objetos de visualização e modelo e serve apenas para transmitir mensagens entre eles.

  3. Somente o View tem uma interface. O Modelo possui métodos estáticos e não é criado, portanto, não há necessidade de uma interface. Se você deseja um modelo diferente, o apresentador chama um conjunto diferente de métodos de classe estática. Sendo estático, o Modelo não tem link para o apresentador.

Pensamentos pessoais

De todas as diferentes variações que apresentei (a maioria provavelmente usei de alguma forma), das quais tenho certeza de que existem mais. Prefiro A3 como manter a lógica de negócios reutilizável fora do MVP, B2, para menos duplicação de dados e menos eventos sendo disparados. C1 por não adicionar em outra classe, certifique-se de colocar uma pequena quantidade de lógica não testável por unidade em uma exibição (como um objeto de domínio é visualizado), mas isso pode ser revisado por código ou simplesmente visualizado no aplicativo. Se a lógica fosse complexa, eu concordaria com uma classe de adaptador, mas não em todos os casos. Para a seção D, sinto que o D1 cria uma exibição muito grande pelo menos para um exemplo de menu. Eu usei D2 e ​​D3 antes. O problema com o D2 é que você precisa escrever muito código para rotear eventos de e para o apresentador para a exibição filho correta e não é compatível com arrastar / soltar cada novo controle precisa de mais fiação para suportar o único apresentador. D3 é minha escolha preferida, mas adiciona ainda mais classes como apresentadores e modelos para lidar com a visualização, mesmo que a visualização seja muito simples ou não precise ser reutilizada. Eu acho que uma mistura de D2 e ​​D3 é melhor com base nas circunstâncias. Quanto à seção E, acho que tudo que tem uma interface pode ser um exagero. Eu já faço isso para objetos de domínio / negócios e geralmente não vejo vantagem no "design" ao fazê-lo, mas ajuda a zombar de objetos em testes. Pessoalmente, eu consideraria o E2 uma solução clássica, embora tenha visto o E3 usado em 2 projetos nos quais trabalhei anteriormente. Eu acho que uma mistura de D2 e ​​D3 é melhor com base nas circunstâncias. Quanto à seção E, acho que tudo que tem uma interface pode ser um exagero. Eu já faço isso para objetos de domínio / negócios e geralmente não vejo vantagem no "design" ao fazê-lo, mas ajuda a zombar de objetos em testes. Pessoalmente, eu consideraria o E2 uma solução clássica, embora tenha visto o E3 usado em 2 projetos nos quais trabalhei anteriormente. Eu acho que uma mistura de D2 e ​​D3 é melhor com base nas circunstâncias. Quanto à seção E, acho que tudo que tem uma interface pode ser um exagero. Eu já faço isso para objetos de domínio / negócios e geralmente não vejo vantagem no "design" ao fazê-lo, mas ajuda a zombar de objetos em testes. Pessoalmente, eu consideraria o E2 uma solução clássica, embora tenha visto o E3 usado em 2 projetos nos quais trabalhei anteriormente.

Questão

Estou implementando o MVP corretamente? Existe uma maneira correta de fazer isso?

Eu li o trabalho de Martin Fowler que tem variações, e eu lembro que quando comecei a MVC, entendi o conceito, mas não conseguia descobrir onde é o ponto de entrada, tudo tem sua própria função, mas o que controla e cria o original conjunto de objetos MVC.

JonWillis
fonte
2
O motivo para fazer esta pergunta é que estou procurando acertar quase na primeira tentativa. Eu preferiria ter um MVP padrão a seguir, em vez de criar 6 aplicativos usando variações diferentes para o mesmo padrão.
21411 JonWillis
Perfeito ... Eu queria pedi-lo por um longo tempo
The King

Respostas:

4

Muito do que você apresenta aqui é muito razoável e sólido. Algumas das opções dependerão de detalhes específicos do aplicativo e de qual deles "parecerá" certo. Como é o caso na maioria das vezes, não haverá uma resposta certa. Algumas das opções farão sentido aqui e podem estar completamente erradas para a próxima aplicação e circunstâncias. Sem conhecer algumas das especificidades do aplicativo, acho que você está no caminho certo e tomou algumas decisões bem fundamentadas.

Para mim, sinto que o apresentador quase sempre deve ser o ponto de entrada. Ter a interface do usuário como ponto de entrada coloca muita lógica na interface e diminui a capacidade de substituir uma nova interface sem grandes alterações de codificação. E realmente esse é o trabalho do apresentador.

Walter
fonte
Talvez a pergunta que eu deva fazer seja uma das maneiras de usar o MVP completamente errado. Concordo que as variações se adequam a diferentes cenários, mas considero que deve haver uma abordagem geralmente aceita. Os exemplos de MVP são exemplos simples e quase todos com a mesma necessidade de editar um objeto de domínio e salvar as alterações. No entanto, a partir desses exemplos com o mesmo objetivo, as variações acima foram produzidas. Acabei de codificar parte de um aplicativo usando eventos disparados na exibição para serem manipulados no apresentador, mas eu poderia ter a exibição mantendo uma referência ao apresentador e chamando métodos diretamente.
21411 JonWillis
Eu concordo com todo o esforço para simplificar a exibição para justificar que não seja testado em unidade, então o apresentador deve ter controle. Minha única preocupação com isso (pensando que poderia estar errado) é que digamos winforms / usercontrols talvez precise ser vinculado aos elementos pai e me perguntei se é necessária lógica adicional em um apresentador para escrevê-lo.
21411 JonWillis
@ Jon - maneiras MVP estão erradas? No meu livro, seria quando a visão conheceria o apresentador. Pode não estar "errado" no sentido de que funciona, mas simplesmente não seria MVP, ele se transformou em outra coisa naquele momento. O problema com os padrões de design é que os exemplos são sempre o mais limpos e simples possível. Então, quando você vai implementá-los pela primeira vez, o mundo real salta e diz 'ei, aplicativos reais são muito mais complicados que esse exemplo insignificante'. É aí que o seu crescimento começa. Descubra o que funciona para você e as circunstâncias do aplicativo.
1400 Walter Walter
Obrigado pelo conselho. Lembro-me de aprender MVC na universidade. Parecia ótimo, mas com uma tela em branco você se pergunta por onde começar e como realmente funciona. Em termos de MVP estar errado, quero dizer que alguma das idéias / variações do MVC que eu publiquei acima estava errada, isto é, quando a visualização sabe como o apresentador funciona. Ou a visualização deve ter uma referência a uma interface de um apresentador ou do tipo concreto etc. Apenas muitas variações diferentes que podem funcionar para o mesmo objetivo.
21411 JonWillis
1
@ Jon - Suas variações estão de acordo com o espírito do MVP. Quanto a trabalhar com interfaces ou tipos concretos, isso dependerá das circunstâncias do aplicativo. Se o aplicativo for relativamente pequeno, não muito complexo, talvez não seja necessário adicionar interfaces. Gosto de manter as coisas o mais simples possível, até ficar bem claro que o aplicativo precisa absolutamente implementar a arquitetura X. Veja esta resposta para obter mais detalhes: programmers.stackexchange.com/questions/34547/…
Walter
4

Usamos uma forma modificada de MVP em nosso aplicativo .NET 2.0 Winforms. As duas partes que estavam faltando eram um adaptador modificado do WPF ViewModel e a adição de ligação de dados. Nosso padrão específico é MVPVM.

Nós ligamos como apresentador primeiro em quase todos os casos, exceto nos controles personalizados do usuário, que são conectados primeiro para facilitar a visualização do designer. Usamos injeção de dependência, ViewModels gerados por código, BDD para os apresentadores e TDD / TED para o modelo.

As VMs são apenas uma enorme quantidade de propriedades que aumentam o PropertyChanged quando são alteradas. Era muito fácil gerá-las por código (e seus testes de unidade de exercício associados). Nós os usamos para leitura e gravação em controles interativos pelo usuário e controle de status ativado. O ViewModel é acoplado ao View, já que usamos a ligação de dados para quase tudo.

Ocasionalmente, o View possui métodos para realizar tarefas que a VM não pode. Isso geralmente controla a visibilidade dos itens (o WinForms pode ser exigente quanto a isso) e coisas que se recusam a ser vinculadas a dados. A Visualização sempre expõe eventos como "Login" ou "Reiniciar", com EventArgs apropriados para atuar no comportamento do usuário. A menos que tenhamos que usar um hack como "View.ShowLoginBox", o View é completamente intercambiável desde que atenda aos requisitos gerais de design.

Demoramos cerca de 6 a 8 meses para definir esse padrão. Ele tem muitas peças, mas é muito flexível e extremamente poderoso. Nossa implementação específica é muito assíncrona e orientada a eventos, que pode ser apenas um artefato de outros requisitos, e não um efeito colateral do comportamento do design. Por exemplo, adicionei a sincronização de threads à classe base da qual nossas VMs (que simplesmente expuseram um método OnPropertyChanged para gerar o evento) - e poof, agora podemos ter apresentadores e modelos multithread.

Bryan Boettcher
fonte
Sei que seu MVPVM pode ser de interesse comercial, mas se estiver tudo bem, você poderia fornecer algum código de amostra? Em uma nota lateral, onde você desenha a linha do que faz no modelo e do que se passa no apresentador. Eu já vi o apresentador ser tão básico que eles manipulam os eventos de visualização e apenas chamam o modelo, para o apresentador acessando as camadas de dados para passar objetos de negócios para um modelo, até o modelo sendo substituído pelo apresentador.
21411 JonWillis
Sim, deixe-me terminar um exemplo. Ele será adaptado levemente aos nossos negócios, pois estou sendo usado como uma ferramenta de treinamento interna.
21711 Bryan Boettcher
Obrigado, vou olhar sobre ele no fim de semana para ver se eu entendi
JonWillis
@insta - o link está inoperante, você pode carregá-lo novamente em algum lugar?
Yves Schelpe
1
Merda, eu apenas a apaguei há uma semana. Figuras :(
Bryan Boettcher
2

Estou usando uma versão do PureMvc que foi modificada para .Net e depois estendida por mim.

Eu estava acostumado ao PureMvc de usá-lo em aplicativos Flex. É um tipo de estrutura simples, portanto, é fácil de adaptar se você deseja personalizá-lo.

Eu levei as seguintes liberdades com ele:

  • Usando a reflexão, fui capaz de obter propriedades privadas no mediador de exibição para entrar na classe de formulário e pegar a referência (privada) a cada controle que eu estava mediando.
  • Usando a reflexão, eu posso conectar automaticamente os controles às assinaturas de evento no meu mediador, que são genéricas, portanto, basta ativar o parâmetro sender.
  • Normalmente, o PureMvc deseja que mediadores derivados definam seus interesses de notificação em uma função que retornará uma matriz de seqüências de caracteres. Como meus interesses são principalmente estáticos e eu queria ter uma maneira mais fácil de ver os interesses dos mediadores, fiz uma modificação para que o mediador também possa declarar seus interesses por um conjunto de variáveis-membro com uma assinatura específica: _ _ _ <classname> _ <nome da notificação>. Dessa forma, posso ver o que está acontecendo usando a árvore de membros do IDE em vez de olhar dentro de uma função.

No PureMvc, você pode usar um Comando como ponto de entrada; um comando Iniciar típico configuraria o Modelo na medida do possível, criaria o MainForm, criaria e registraria o mediador para e com esse formulário e, em seguida, execute Application.Run No formulário.

O mediador do formulário seria responsável pela configuração de todos os submediadores; parte disso pode ser automatizada usando novamente truques de reflexão.

O sistema que eu estou usando é compatível com arrastar / soltar, se eu entendo o seu significado. O formulário real é todo criado no VS, mas minha experiência é apenas com formulários que criaram controles estaticamente. Coisas como itens de menu criados dinamicamente parecem viáveis ​​com alguns ajustes no mediador para esse menu ou submenu. O ponto em que ficava peludo é quando o mediador não tinha um elemento raiz estático ao qual se conectar e você começou a criar mediadores dinâmicos de "instância".

Marca
fonte