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
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.
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.
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.
Sempre que um valor na visualização é alterado, ou seja,
TextChanged
evento em .Net / C #. Isso aciona umaDataChangedEvent
passagem 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 aDataChangedEvent
. Quando um usuário deseja confirmar alterações,SaveEvent
ele é acionado, passando para o modelo para salvar. Nesse caso, o modelo imita os dados da visualização e processa ações.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)
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.Animals
expõe umaIEnumerable<IAnimal>
propriedade, mesmo que a exibição as processe em Nós em um TreeView. Então, para o animal selecionado, ele seria expostoSelectedAnimal
comoIAnimal
propriedade.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.
Você cria uma visão enorme, que contém todos os controles individuais expostos pela interface de visualizações.
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.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.
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.
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.
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.
fonte
Respostas:
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.
fonte
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.
fonte
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:
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".
fonte