Na semana passada, eu estava lendo alguns artigos sobre o uso do WCF, a estrutura da entidade e como transportar entidades por todo o limite de serviço. Um dos artigos que encontrei incluía um projeto de demonstração que fez uso do padrão de apresentador de exibição de modelo (MVP).
Tendo explorado o projeto de demonstração, achei interessante escrever um artigo sobre esse padrão. Seu mecanismo de pesquisa favorito fornecerá felizmente você uma infinidade de links para outros artigos que explicam esse padrão completamente.
Para este artigo, decidi fornecer uma implementação concreta e me concentrar menos na teoria por trás do padrão.
Vamos rolar ...
É claro que é necessário um pouco de teoria, então vamos tirar do caminho.
Como você pode deduzir do nome, o padrão MVP consiste em três partes distintas: o modelo, a visualização e o apresentador. Cada uma dessas peças desempenha seu próprio papel no estabelecimento de uma separação de preocupações entre a apresentação, a camada de acesso a negócios e dados.
O modelo é responsável por lidar com o acesso aos dados, o apresentador se comunica com o modelo e passa dados de e para ele. A exibição recebe dados do apresentador e passa os dados de volta, ele nunca se comunica diretamente com o modelo. O apresentador é o intermediário para a visualização e o modelo.
Figura 1 - Interação do padrão MVP

A imagem acima mostra a exibição como implementando uma interface. A camada de apresentação, seja um aplicativo ASP.NET, WINFORMS ou WPF, para implementar uma ou mais interfaces de exibição. O apresentador, por sua vez, se comunica com a implementação do View através dessa interface, não sabe nada sobre a própria implementação real.
Isso fornece um acoplamento frouxo e impede que seu código seja dependente da tecnologia usada para a camada de apresentação. A idéia é que você possa substituir essa camada sem que ela afete sua lógica de negócios e acesso a dados.
Tudo isso pode parecer um pouco vago, mas as coisas devem esclarecer quando passarmos para os seguintes pontos que fornecem uma implementação real desse padrão. Para o exemplo, um aplicativo ASP.NET será usado.
O local mais lógico para começar é o modelo. Como é responsável por lidar com acesso e armazenamento de dados, primeiro precisamos estabelecer um armazenamento de dados físicos. Para isso, usei o SQL Server 2005 Express, criei um novo banco de dados chamado Southwind e adicionei uma tabela intitulada Cliente. A tabela tem 3 campos, a saber:
É isso para o banco de dados. Vamos iniciar o Visual Studio 2008 e criar uma nova solução em branco chamada Avppattern. Em seguida, adicione uma biblioteca de classes intitulada "Database".
Normalmente, eu daria ao projeto um nome que segue o padrão "Company.product.library", mas, por uma questão de simplicidade, vamos mantê -lo curto e simples. Exclua também o arquivo Autogenerado Class1.CS após a adição do projeto à solução.
Vamos criar um modelo a partir do banco de dados usando a estrutura da entidade (EF). Portanto, certifique -se de estar usando o Visual Studio 2008 e tenha o Service Pack 1 instalado para o Visual Studio e o .NET Framework 3.5. Você pode baixar os pacotes de serviços aqui.
Adicione um modelo de dados da estrutura da entidade à biblioteca de classes selecionando Add, novo item, modelo de dados da entidade ADO.NET no menu de contexto do projeto no Solution Explorer. O Visual Studio agora mostrará o Assistente de Modelo de Dados da Entidade. Nomeie o modelo Southwind e deixe o Visual Studio gerar o modelo para você. Quando perguntado quais objetos de banco de dados você deseja incluir no seu modelo, basta selecionar a tabela de clientes no nó das tabelas.
Observação : se você não está familiarizado com a geração de modelos de dados com a estrutura da entidade, sugiro altamente este vídeo de treinamento de Alex James. Ele mostra como criar um modelo de dados de entidade simples a partir do zero.
A Figura 2 mostra o modelo de dados da entidade resultante. O modelo não pode ficar muito mais simples que isso. Isso é feito de propósito para manter as coisas o mais simples possível e manter o foco no padrão MVP.
Figura 2 - Modelo de dados da entidade

Certifique -se de renomear seus entityTypes e EntitySets para um nome apropriado após a geração do modelo de dados. A regra geral é usar um único substantivo para o EntityType e um plural para os entidade. Portanto, neste caso, nosso EntityType deve ser nomeado Cliente e os clientes da EntitySet. O nome do EntityType já está ok, pois o herda da tabela do cliente; portanto, selecione o cliente EntityType e ajustar sua propriedade de nome do conjunto de entidades.
Figura 3 - Nome do conjunto de entidades

A estrutura da entidade gerará automaticamente uma classe parcial para a tabela de clientes. Você pode optar por estender esta classe de cliente parcial, se quiser. Para impedir que suas adições personalizadas sejam apagadas ao regenerar o modelo, coloque esse código em um arquivo de classe separado. Isso é simular para trabalhar com conjuntos de dados fortemente digitados. Para o artigo, isso não é necessário, no entanto.
Figura 4 - Solution Explorer

Agora que o modelo está no lugar, vamos adicionar uma camada de negócios sobre ele, onde podemos definir nossa lógica de negócios personalizada. Por uma questão de simplicidade, manterei a quantidade de código limitada nessa camada.
Observe que essas camadas aplicam apenas uma separação lógica (camada N), não física (n-camada). Todas as camadas residem na mesma máquina, embora você certamente possa optar por desbrible-as em várias máquinas / camadas e criar um aplicativo verdadeiramente distribuído ou n-camada.
Para configurar a camada de negócios, adicione uma nova biblioteca de classes à solução e chame de negócios. Em seguida, renomeie o arquivo Class.cs padrão para CustomerManager.cs. Adicione também referências ao banco de dados da biblioteca de classes criado anteriormente e ao conjunto do sistema.data.entity.
A Listagem 1 exibe a classe CustomerManager, que contém alguma lógica de negócios para trabalhar com a entidade do cliente no Entity Data Model (EDM). O código é praticamente auto-explicativo.
Listagem 1 - CustomerManager Class
using System . Collections . Generic ;
using System . Linq ;
using Database ;
namespace Business
{
public class CustomerManager
{
private readonly SouthwindEntities context ;
#region Constructor(s)
public CustomerManager ( )
{
context = new SouthwindEntities ( ) ;
}
#endregion
#region Methods
// Retrieve a generic list of Customer entities.
// This method will return all the customers found in the Customer table.
public List < Customer > GetCustomers ( )
{
var q = from c in context . Customers
select c ;
return q . ToList ( ) ;
}
#endregion
}
}Figura 5 - Solução atualizada

Observação : A configuração de um aplicativo N-Tier incluiria a introdução de uma camada de serviço na qual a camada de apresentação chama. A camada de serviço utiliza os objetos de negócios encontrados na camada de negócios. Não há mais associação direta entre a apresentação e a camada de negócios, a camada de serviço atua como intermediária. Em um artigo futuro, abordarei isso mostrando como transportar entidades de EF através do limite de serviço.
A camada de negócios contém apenas um método útil, a saber "List GetCustomers ()". O apresentador do padrão MVP chamará esse método no objeto de negócios do cliente -gerente para entregar os dados à visualização.
O aplicativo de exemplo mostra apenas uma lista de clientes usando o padrão MVP. Isso pode ser um pouco de exagero, mas é mantido tão simples quanto possivelmente pelo design. O principal objetivo deste "Hello World!" O tipo de aplicativo é obter a ideia de como implementar esse padrão. A funcionalidade real oferecida pelo aplicativo não é tão importante.
A implementação da exibição real (página aspx, winforms, wpf ... etc.) precisará implementar uma interface de visualização. A implementação do View precisa criar uma instância do apresentador e passar como um parâmetro em seu construtor. O construtor do apresentador possui um parâmetro que é o tipo de interface de exibição.
A Listagem 2 lista a interface IView que implementaremos em breve em uma página ASP.NET ASPX. Ele tem um evento chamado preparewView. O evento Preparview usa um delegado cuja assinatura especifica que não retorna nada e não leva parâmetros.
A visão deve aumentar apenas esse tipo de "eventos vazios" para significar ao apresentador que alguma ação deve ser executada. A ação neste caso significa que o apresentador deve atualizar a lista de clientes que a implementação da visualização mantém. O apresentador pode acessar esta lista de clientes através da propriedade Ilist Clients, que é declarada como parte da interface.
Listagem 2 - Visualizar interface
public delegate void VoidEventHandler ( ) ;
public interface IView
{
event VoidEventHandler PrepareView ;
IList < Customer > Customers { set ; }
}Para adicionar a interface de visualização ao seu projeto primeiro, adicione um novo projeto de biblioteca de classes à solução chamada apresentação. Em seguida, adicione uma nova interface e copie e cole o código mostrado na listagem acima. A biblioteca de código de apresentação também conterá os apresentadores e as interfaces que eles implementam.
Eu separo as interfaces e apresentadores de visualização em uma biblioteca de código separada, para que você possa compartilhá -las facilmente entre vários "estruturas de exibição", como asp.net, winforms, wpf ... etc. A Figura 6 mostra como escolhi organizar esta biblioteca de código.
Figura 6 - Solução atualizada

Não se esqueça de adicionar referências ao banco de dados e projetos de negócios e à montagem do sistema.data.entity.
Para a parte final do padrão MVP, precisamos fornecer um apresentador. A classe CustomerPresenter mostrada na Listagem 3 exige uma referência a uma implementação do IView em seu construtor. Dessa forma, ele pode se comunicar com a visualização sem realmente saber nada sobre a implementação real. É esse acoplamento solto que torna o padrão MVP tão adequado para diferentes "estruturas de exibição".
Também no construtor, todos os eventos da interface de visualização estão conectados a um manipulador de eventos. Nesse caso, existe apenas um evento. O evento Prepareview está conectado ao manipulador de eventos View_PrepareView. Por sua vez, isso chama o método privado do apresentador getCustomers (), que retorna uma coleção de clientes "atualizada" e a atribui à coleção de clientes mantida pela implementação do View.
Listagem 3 - CLASTERSPRESTER CLASS
public class CustomersPresenter : ICustomersPresenter
{
#region Fields
private readonly IView view ;
#endregion
#region Constructor(s)
public CustomersPresenter ( IView view )
{
// Save a reference to the view
this . view = view ;
// Hook up an event handler for the events of the view
view . PrepareView += view_PrepareView ;
}
#endregion
#region Private methods
private List < Customer > GetCustomers ( )
{
return new CustomerManager ( ) . GetCustomers ( ) ;
}
#endregion
#region ICustomersPresenter Members
public virtual void view_PrepareView ( )
{
view . Customers = GetCustomers ( ) ;
}
#endregion
}O apresentador acima também implementa uma interface. No código -fonte da amostra, essa interface é deixada vazia. Eu só coloquei lá para fins ilustrativos. Você pode desenvolver esta interface, se desejar. Você pode precisar para sua estrutura de teste de unidade favorita para zombar dos apresentadores, por exemplo.
Portanto, a página de visualização ou ASPX em nosso caso só precisa implementar a interface de visualização e acionar o evento PrepareView para receber uma lista de clientes atualizados do apresentador. A vista em si não se comunica diretamente com o banco de dados ou camada de negócios. O apresentador lida com a comunicação com a camada de negócios que recupera os dados abordando o modelo (ou camada de acesso a dados, se você quiser).
Para terminar este artigo, vamos ver como tudo isso se reúne em um projeto de demonstração do ASP.NET. Adicione um novo projeto usando o modelo de projeto de aplicativo da Web ASP.NET à solução. Adicione referências aos projetos de apresentação e banco de dados e à montagem do sistema.data.entity.
Adicione um GridView chamado "GridView1" e um botão chamado "BtnRefresh" à página Default.aspx. Adicione o código na Listagem 4 ao código atrás da página.
Listagem 4 - CÓDIGO DESFAULT.aspx atrás
public partial class _Default : System . Web . UI . Page , IView
{
private CustomersPresenter presenter ;
protected override void OnInit ( EventArgs e )
{
presenter = new CustomersPresenter ( this ) ;
}
protected void Page_Load ( object sender , EventArgs e )
{
if ( ! IsPostBack )
{
PrepareView ( ) ;
}
}
protected void btnRefresh_Click ( object sender , EventArgs e )
{
PrepareView ( ) ;
}
#region IView Members
public event VoidEventHandler PrepareView ;
public IList < Database . Customer > Customers
{
set
{
GridView1 . DataSource = value ;
GridView1 . DataBind ( ) ;
}
}
#endregion
}Todo o código por trás da página ASPX faz é implementar a interface IView, criar um apresentador e passar uma implementação do IView, sendo por si mesma em seu construtor. Então tudo o que resta é acionar o evento prepareView () da interface IView nos momentos apropriados.
Durante a criação do apresentador, um manipulador de eventos é automaticamente atribuído a este evento, que garante que, quando acionado, o apresentador saiba como atualizar a coleção de clientes mantida pela página. A própria página não sabe nada de onde esses dados estão provenientes ou como são recuperados. Quanto mais idiota uma visão, melhor.
Ao visualizar esta página em um navegador, este é o resultado:
Figura 7 - Demoção do site do ASP.NET

Observação : não se esqueça de adicionar o ConnectionString exigido pela estrutura da entidade ao arquivo de configuração do Web.config. Você pode encontrar o ConnectionString no arquivo app.config do projeto da biblioteca da classe de banco de dados. Foi inserido lá automaticamente quando o Visual Studio gerou o modelo de dados da entidade.
É claro que as seqüências de conexão fornecidas no código -fonte da amostra não funcionam no seu computador, pois foram construídas em um banco de dados local meu. Portanto, certifique -se de ajustá -los de acordo.
Figura 8 - Solução atualizada

Como a etapa final deste artigo, vamos criar uma exibição usando um aplicativo Windows Forms apenas para mostrar o quão flexível é o padrão MVP. As etapas para fazer isso são quase idênticas ao exemplo anterior de criação de um site do ASP.NET. Adicione um novo aplicativo Windows Forms à sua solução e adicione referências ao projeto de banco de dados e apresentação e à montagem do sistema.data.entity.
Em seguida, adicione um DataGridView e controle de botões ao formulário. O código para o formulário é exibido na listagem abaixo. É quase idêntico ao da página padrão.aspx. Também não se esqueça de adicionar a string de conexão da estrutura da entidade ao arquivo de configuração app.config.
Listagem 5 - Form1.cs Código
using System ;
using System . Windows . Forms ;
using Presentation . Presenters ;
using Presentation . ViewInterfaces ;
namespace WindowsFormsApplication
{
public partial class Form1 : Form , IView
{
private CustomersPresenter presenter ;
public Form1 ( )
{
InitializeComponent ( ) ;
presenter = new CustomersPresenter ( this ) ;
}
private void Form1_Load ( object sender , EventArgs e )
{
PrepareView ( ) ;
}
private void btnRefresh_Click ( object sender , EventArgs e )
{
PrepareView ( ) ;
}
#region IView Members
public event VoidEventHandler PrepareView ;
public System . Collections . Generic . IList < Database . Customer > Customers
{
set
{
dataGridView1 . DataSource = value ;
}
}
#endregion
}
}Voila, finalmente terminamos. Observe que, embora o exemplo ASP.NET e WINFORMS seja quase idêntico no código, esse pode não ser o caso em aplicativos mais complexos. Os tipos de interações nesses dois tipos de interfaces de usuário são muito diferentes e, com um apresentador que você pode usar em todas as situações, pode não ser um corte claro.
Figura 9 - Demonstração de aplicativos do Windows Forms

Figura 10 - Solução atualizada

Para este artigo, a interpretação clássica do padrão MVP foi usada e demonstrada implementando -o uma solução ASP.NET. O padrão MVP original é considerado "aposentado" desde que Martin Fowler anunciou So. O padrão pode ser dividido em dois campos agora, sendo:
Leia este artigo da Microsoft Patterns & Practices para obter mais informações.
Escrevi este artigo para fornecer uma implementação concreta do padrão MVP enquanto o explorava na época. No entanto, pode ser aconselhável ficar longe disso e esperar até que a Microsoft libere a estrutura do ASP.NET MVC. Para aqueles que desejam imitar uma estrutura de Model-View-Controller (MVC) agora, sugiro ler este artigo da Microsoft Patterns & Practices.
Observação : O padrão MVP é um derivado do padrão do controlador de exibição do modelo (MVC). No momento da redação deste artigo, a Microsoft está ocupada desenvolvendo a estrutura do ASP.NET MVC. Ao mapear a arquitetura para um novo projeto de site, sugiro conferir essa estrutura.
A principal diferença entre os padrões MVP e MVC pode ser identificada para quem é responsável por lidar com a entrada do usuário, como eventos de teclado e MOUE. No padrão MVP, a própria GUI é responsável e precisa delegá -los ao apresentador por meio de eventos. No padrão MVC, o controlador é responsável por lidar com esses eventos.