Como posso aplicar o padrão MVC a um aplicativo C # WinForms?

11

Sou desenvolvedor C ++ que usa o padrão MVC para projetar GUIs desde então.

Recentemente, eu queria voltar ao C # e configurei um aplicativo Windows Forms, mas agora estou um pouco perdido em como enviá-lo para uma estrutura compatível com MVC.

Atualmente, o que estou tentando fazer é "declarar" a classe que recebi para as WinForms como uma exibição e adicionar uma classe para o Model e Controller em segundo plano. No entanto, não tenho muita certeza de como interagir com eventos, como um clique no botão. Normalmente, eu redirecionava esses eventos para o controlador e realizava uma ação no modo de exibição assim que terminava.

Isso parece bastante insatisfatório nesta constelação. Por exemplo, se eu quisesse implementar um botão "Exit", teria que redirecionar o evento da View para o Controller, além de implementar um método público extra na View, que pode ser chamado pelo Controller, enquanto eu poderia simplesmente chamar Close () a partir da exibição em primeira instância.

Você tem algum conselho para mim? Meu entendimento do Windows Forms em C # ainda não é bom o suficiente para experimentar uma implementação MVC? Estou dando à classe de formulários um papel errado? O MVC é simplesmente uma arquitetura inadequada para este caso de uso?

Sossenbinder
fonte
1
Pergunta do AT. Mas por que o WInforms? O WPF foi concebido para substituir o Winforms e suporta o MVC (bem tecnicamente MVVM). Embora eu diga que a curva de aprendizado do WPF pode ser íngreme. (mas se você fizer isso mal você pode fazer WPF olhar um código como WinForms)
Peter M
1
@ PeterM: porque, mesmo depois de 10 anos, o WPF é péssimo e ainda é lento.
Whatsisname
3
Parafraseando o comentário do @ whatsisname, o WPF é voltado para aplicativos maiores. Aplicativos menores podem ser melhor atendidos com o Winforms porque o Winforms é sem dúvida mais simples. No entanto, se você quiser fazer esse argumento, também poderá argumentar que o aplicativo Winforms é pequeno o suficiente para provavelmente não precisar de MVP. MVVM é cozido no WPF. O WPF possui gráficos baseados em vetores, portanto, não sofre os mesmos problemas de dimensionamento que o Winforms. O WPF é muito fácil de compor (você pode facilmente colocar controles dentro de outros controles) e possui uma ligação de dados matadora. Como não gostar?
Robert Harvey
3
@whatsisname WPF pode sugar, mas ele suga muito menos do WinForms para qualquer coisa para além de um programa de brinquedo
Peter M
2
Para programas de desktop internos, eu diria que sim. Mas muitas empresas preferem aplicativos baseados em navegador para suas operações comerciais, simplesmente porque não há instalação necessária.
Robert Harvey

Respostas:

3

Por coincidência, estou trabalhando em um projeto WinForms padronizado após o MVC. Eu não chamaria isso de uma resposta perfeita, mas explicarei meu design geral e espero que isso possa ajudá-lo a criar o seu próprio.

Com base na leitura que fiz antes de iniciar este projeto, parece não haver uma maneira "certa" de implementar isso. Segui os princípios simples de OOP e MVC, e o restante foi tentativa e erro ao desenvolver um fluxo de trabalho.

O MVC é simplesmente uma arquitetura inadequada para este caso de uso?

Não ..? Não há contexto suficiente em sua pergunta para fornecer uma resposta direta a isso. Por que você está usando o MVC em primeiro lugar? Quais são os requisitos não funcionais do seu projeto? Seu projeto terá muita interface do usuário? Você se preocupa mais com segurança e prefere uma arquitetura em camadas? Quais são os principais componentes do seu projeto? Talvez cada componente precise de um padrão de design diferente. Descubra por que você deseja usar esse padrão de design em primeiro lugar e você pode responder sua própria pergunta;)

Minha razão para usar o MVC: é um padrão de design bastante simples de entender na minha opinião e meu design é fortemente baseado na interação do usuário. A maneira como o MVC permite que o desenvolvedor separe preocupações também é suficiente para o meu aplicativo. Isso torna meu código muito mais sustentável e testável.

Suponho também que estou usando mais um design híbrido. Geralmente, o conceito ideal apresentado na engenharia de software não se efetua na prática. Você pode modificar o design para atender às necessidades do seu projeto. Não há necessidade de se envolver no que é certo ou errado. Existem práticas gerais, mas as regras sempre podem ser dobradas ou quebradas, desde que você não atire no próprio pé.

Minha implementação começou com um design de alto nível que me deu uma idéia de quais componentes serão necessários. É melhor começar dessa maneira e avançar na arquitetura. Aqui está o diagrama do pacote para o projeto (criado no StarUML): insira a descrição da imagem aqui

Observe que todas as camadas, exceto a de apresentação, dependem do sistema de mensagens. Essa é uma "linguagem" comum que as camadas inferiores e os subsistemas dessas camadas usam para se comunicar. No meu caso, era uma enumeração simples baseada em operações que podem ser executadas. O que me leva ao próximo ponto...

Pense em operações ou comandos como base para sua implementação. O que você deseja que seu aplicativo faça? Divida-o nas operações mais fundamentais. Por exemplo: CreateProject, WriteNotes, SaveProject, LoadProject etc. A GUI (ou classes de formulário) terá algum evento (como pressionar um botão). Toda operação possui um método de controlador associado. Nesse caso, algo como Exit é muito simples. O aplicativo pode ser simplesmente fechado a partir da classe Form. Mas suponha que eu queira persistir alguns dados do aplicativo em um arquivo primeiro? Vou chamar o método "Salvar" da respectiva classe de controlador no meu método de pressionar o botão.

A partir daí, o controlador chamará o conjunto correto de operações das classes de Serviço. As classes de serviço no meu aplicativo funcionam como uma interface para a camada de domínio. Eles validarão a entrada recebida da chamada do método do controlador (e, portanto, da GUI) e manipularão o modelo de dados.

Após a conclusão da validação e da manipulação do objeto correspondente, o método de serviço retornará um código de mensagem para o controlador. Por exemplo MessageCodes.SaveSuccess,. O controlador e as classes de serviço foram baseadas nos objetos de domínio e / ou no conjunto geral de operações que podem ser agrupadas.

Por exemplo: FileMenuController(operações: NewProject, SaveProject, LoadProject) -> ProjectServices(CreateProject, PersistProjectToFile, LoadProjectFromFile). Onde Projectseria uma classe de domínio no seu modelo de dados. As classes Controller e Service, no meu caso, eram classes não instanciadas com métodos estáticos.

Em seguida, o controlador reconhece a operação como concluída sem êxito. Agora, o controlador possui seu próprio sistema de mensagens que ele usa para interagir com a camada de apresentação, daí a dupla dependência entre as camadas Serviço e Apresentação. Nesse caso, uma classe chamada ViewStateno ViewModelspacote é sempre retornada à GUI pelo controlador. Esse estado contém informações como: " é o estado em que você tentou colocar o aplicativo válido? ", " Uma mensagem legível por humanos sobre a operação que você tentou executar e por que teve ou não êxito (mensagens de erro) " e uma ViewModelclasse.

A ViewModelclasse contém dados relevantes da camada de domínio que a GUI usará para atualizar a exibição. Esses modelos de exibição se parecem com as classes de domínio, mas no meu caso eu usei objetos muito finos . Basicamente, eles praticamente não têm comportamento, apenas retransmitem informações sobre o estado de nível mais baixo do aplicativo. Em outras palavras, NUNCA vou distribuir minhas classes de domínio para a camada de apresentação. É também por isso que os pacotes Controllerse Servicesdividem a camada de serviço em duas partes. Os controladores nunca manipularão classes de domínio ou validarão seu estado. Eles apenas atuam como um limite, convertendo dados relevantes da GUI em dados relevantes do domínio que os serviços podem usar e vice-versa. Incluir a lógica de serviço no controlador levaria a muito gordura controladores, que são mais difíceis de manter.

Espero que isso lhe dê um ponto de partida.

Sedutor Topázio
fonte