Este artigo foi traduzido por máquina.

Além do MVP

Estendendo o padrão MVP para arquitetura de interface do usuário de aplicativos ENTER-prise-Class

Haozhe MA

Modelo-View-Presenter (MVP) representa um avanço em pensar sobre padrões de interface de usuário e o torna claro que os designers de interface do usuário devem manter a separação de preocupação em seus aplicativos.

No entanto, há várias interpretações diferentes do padrão MVP. Por exemplo, algumas pessoas têm para receber que o padrão MVP explicitamente representa o padrão de arquitetura de interface do usuário. Isso vale não exatamente para aplicativos corporativos. Em comparação com outros tipos de aplicativos de interface do usuário, aplicativos corporativos precisam lidar com muitos requisitos diferentes, mais partes envolvidas, mais complexidade e muitas cross-dependências em outros sistemas, como serviços, outros aplicativos e assim por diante. Essas características específicas têm exigido a arquitetura UI de aplicativos de nível corporativo para ter mais ênfase em flexibilidade, capacidade de manutenção, reutilização, consistência de implementação e decoupling funcionalidades de negócios com a tecnologia subjacente para evitar dependências em fornecedores e produtos específicos.

Se apenas o padrão do MVP é aplicado como padrão de arquitetura de interface do usuário para aplicativos corporativos, algumas perguntas serão geradas. Aqui estão alguns:

Um aplicativo corporativo típico contém vários modos de exibição e eventos que ocorrem em um modo de exibição poderiam ter impacto sobre outros modos de exibição. Por exemplo, clicando em um botão em uma única tela pode causar uma janela pop-up seja exibido e dados da tela podem ser atualizados ao mesmo tempo. Quem é responsável por controlar tal lógica do fluxo de tela? Isso deve ser controlado por cada modo de exibição do emparelhamento apresentador?

Em um Service-Oriented Architecture (SOA), a interface do usuário do aplicativo geralmente obtém informações por meio de serviços. Por exemplo, a interface do usuário precisaria chamar um proxy de cliente de serviço WCF gerado para chamar o serviço do WCF para obter dados. É um bom design para o apresentador chamar esse proxy de cliente de serviço diretamente? Se esses serviços são implementados com diferentes tecnologias ou se os modelos de serviço são alterados, como você projeta a arquitetura de interface do usuário para que o impacto dessas alterações na implementação da interface do usuário pode ser minimizado?

Seguindo esse treinamento de pensamento, algumas implementações podem usar modelos de proxy do cliente de serviço gerados entre o aplicativo. Existem quaisquer riscos de fazer isso? Se for necessário um modelo de interface de usuário dedicado, qual parte será responsável por fornecendo o mapeamento entre o modelo de proxy do cliente de serviço e o modelo de UI?

Estes não são novas perguntas e muitos outros padrões foram introduzidos para preencher a lacuna. Por exemplo, a Padrão de controlador de aplicativo foi introduzido para assumir a responsabilidade de controle do fluxo de navegação.

Achei que seria útil reunir alguns dessas discussões estendendo MVP diferentes e desenhar uma visão abrangente do projeto de arquitetura de interface do usuário. Olhando o problema da perspectiva do aplicativo corporativo, isso vai ajudar arquitetos UI reconhecer quais partes-chave são necessários para o design de interface do usuário e definir um padrão consistente a guia interface do usuário aplicativo implementação.

O termo “ padrão MVP ” será usado em todo o artigo, mas, na verdade, o padrão MVP original tiver sido afastado e duas variações do MVP original no momento estão no lugar. Uma é o padrão de modo passivo e o outro é o padrão Supervising Controller. Embora seja um se encaixa certos cenários e ambas têm vantagens e desvantagens, a arquitetura de interface do usuário descrita em Figura 1 é principalmente baseado em e estendidos do padrão de modo passivo. Certamente, isso não significa arquitetura de interface do usuário não pôde ser constituted com base no padrão Supervising Controller, mas essa é a minha preferência pessoal.

Architecture Based on the Passive View Pattern
Figura 1 Arquitetura de interface do usuário conforme o padrão de modo passivo

Let’s começar a discussão com um entendimento claro do que constitui a arquitetura de interface do usuário, estendendo o padrão MVP. Figura 1 mostra quais partes-chave forem necessários a uma visão de alto nível da arquitetura de interface do usuário. Neste diagrama, são introduzidos sete partes principais: Modo de exibição, Presenter, modelo de UI, controladora de fluxo de processo, agente de serviço, modelo de serviço do cliente proxy e proxy de serviço de cliente.

Exibição

Modo de exibição segue basicamente a função View no padrão de modo passivo. Modo de exibição assume uma lista simples de responsabilidades, começando com a manipulação de layout de exibição da interface do usuário e a lógica de apresentação específicas.

Responsabilidade segunda do modo de exibição é para eventos para o apresentador e requer várias implementações para lidar com essa responsabilidade. Primeiro, exibir precisa implementar a interface IView. Para garantir que há alguns se qualquer implementação impactos na lógica do apresentador e para fornecer capacidade de teste de unidade, o apresentador deve interagir com exibição por meio de uma interface IView.

Segundo, exibir precisa definir propriedades públicas Presenter pode interagir com. No padrão de modo passivo, a exibição não passa dados ao apresentador. Em vez disso, ele é Presenter para escolher dados tem interesse em Exibir. Além disso, esse tipo de implementação reduz a ligação do contrato entre View e Presenter e separa mais claramente as responsabilidades deles.

Uma pergunta, no entanto, é sobre que tipo de dados devem usar as propriedades de exibição. A maneira ideal é ter Exibir definir essas propriedades usando somente simples tipos, como string, inteiro e assim por diante. No entanto, em uma implementação do mundo real, pode se tornar tedioso se o modo de exibição define dados dessa maneira. É aceitável para expor uma propriedade de dados fazendo referência a tipos complexos, tais como definições de modelo de interface de usuário. Isso equilibra a pureza arquitetura com considerações de implementação.

Em terceiro lugar, exibir também precisa chamar operações Presenter quando ocorrerem eventos em modo de exibição de. O modo de exibição pode chamar o Presenter diretamente sem passar todos os dados. Não há nenhum projeto rígido entre View e Presenter porque View e Presenter sempre estão emparelhados. É um bom design de ter uma operação de apresentador dedicada a um evento de exibição.

Responsabilidade última do modo de exibição é responder a atualizações de valor de propriedade. Apresentador atualiza exibir propriedades para indicar uma alteração. O modo de exibição tem o conhecimento para decidir como responder a essas alterações. Ele poderia Vá adiante para atualizar a exibição para refletir a alteração de dados ou ele pode decidir não realizar qualquer ação.

Apresentador

Apresentador essencialmente segue a função de apresentador no padrão de modo passivo. No entanto, aqui, o apresentador não determina fluxo do processo. Presenter leva as solicitações de evento a partir da tela de e publica evento solicitações ao controlador para permitir que o controlador decide a próxima etapa. Como apresentador não irá lidar com lógica de fluxo de processo, ele não é possível saber se as solicitações de eventos na tela de terá qualquer impacto nos outros modos de exibição. Portanto, quando o apresentador recebe solicitações de evento do modo de exibição, ele imediatamente publicar eventos correspondentes para que aquele controlador possa responder a essas solicitações de evento e decidir sobre a próxima etapa do processo. Apresentador nunca supõe que ele pode vá em frente e executar algumas ações até é instruído pelo controlador.

Controlador decide se as operações de apresentador precisam ocorrer. Quando uma operação Presenter é chamada pelo controlador, o apresentador, em seguida, executa as ações, como para recuperar dados através de um agente de serviço. Se precisar tomar ações com os serviços de apresentador, ele faz isso através do agente do serviço. Ele irá passar os parâmetros necessários para o agente de serviço e obter resultados de agente de serviço.

Quando o apresentador estiver pronto para notificar a exibição sobre as alterações de dados, ele fará isso atualizando valores de propriedade da tela. É para exibir para decidir como exibi-los. Como mencionado anteriormente, Presenter interage com exibição por meio da interface do IView em vez de acessar diretamente o objeto de exibição. Desde que a instância de exibição já foi passada ao apresentador ao iniciar o apresentador, Presenter já possui uma instância de exibição para lidar com.

Por fim, o apresentador pode acessar o modelo de UI e poderá colocá-lo em um cache se dados de modelo de UI seja acessado mais tarde.

Processo de fluxo controlador

O controlador de fluxo de processo é quase o padrão Application Controller. A diferença é de responsabilidade exclusiva do controlador de fluxo de processo discutidos aqui controlar o fluxo de processo com base em eventos tipados gerados pelo apresentador. Fluxo de processo não está limitado ao fluxo de navegação de tela. Ele também inclui controlar a ordem de apresentador ações relacionadas a solicitações de evento.

Processo de fluxo controlador assina eventos publicados pelo apresentador e responde apenas eventos publicados pelo apresentador. Esses eventos são eventos tipados. Em outras palavras, processo de fluxo controlador não responder a um evento geral.

Porque o controlador de fluxo de processo só responde a um evento digitado, um fluxo de processo realmente tem já foi predeterminado quando ocorre um evento. Isso simplifica a lógica do processo de fluxo do controlador. Cada evento publicado pelo apresentador contém os dados necessários para Process Flow Controller transportar em ao iniciar a outras operações do apresentador.

Processo de fluxo controlador iniciará Presenter e instâncias de exibição relacionadas se não tiver sido iniciadas ainda. Inversão de controle (IoC) serão necessárias devido a preocupações com referência cruzada. Isso também fornece um design menos rígido entre Presenter e Process Flow Controller.

Modelo de UI

Modelo de UI segue basicamente a função do modelo no padrão de modo passivo. No modo passivo, modelo não realmente está fazendo muito trabalho e simplesmente fornece a definição de estrutura de modelo. Além disso, conforme descrito na seção Presenter, Presenter é responsável por manter o estado do modelo.

O motivo que chame esse modelo de UI em vez de simplesmente modelo é para diferenciá-la de modelo de proxy do serviço cliente, será descrito mais adiante no artigo.

Modelo de UI define a estrutura do modelo é adequada para a manipulação de lógica do aplicativo da interface do usuário. A definição do modelo UI pode exatamente a mesma aparência como modelo de serviço do cliente proxy. No entanto, em algumas situações — especialmente se a interface do usuário precisa exibir dados de várias origens de serviço — é necessário um modelo de UI reestruturado que será diferente do modelo do serviço cliente proxy.

Agente de serviço

Agente de serviço desempenha uma função intermediária entre o apresentador e proxy de serviço de cliente. O serviço o nome não é necessariamente um serviço da Web. Ele representa quaisquer recursos que irão fornecer dados ou executar lógica de negócios. Isso poderia ser um serviço da Web, mas também pode ser simplesmente E/s de arquivo.

Proxy de serviço de cliente tem significado específico na tecnologia de serviço da Web. Aqui, uso proxy de serviço de cliente para representar o gateway para um serviço.

A implementação de proxy de serviço de cliente é tecnicamente específica. Da perspectiva do apresentador, ele em vez disso, não saberia como os dados são transmitidos ou fornecidos. Esses detalhes específicos tecnicamente podem estar oculto dentro do agente do serviço. Portanto, a camada de serviço de agente protege Presenter de sendo afetados pelas alterações de tecnologia de implementação do serviço, controle de versão de serviço, alterações do modelo de serviço e assim por diante.

Agente de serviço fornece operações para Presenter interagir com. Se precisar de um tipo complexo a ser passada para essas operações, você precisa definir um tipo complexo em modelo de UI. Isso também vale para o tipo de retorno de operação. Você então passar essas chamadas de operação para operações de proxy de serviço de cliente correspondentes. Em alguns casos, uma operação de agente de serviço pode iniciar várias chamadas de operação de proxy de serviço de cliente.

Como as operações de agente de serviço levam em tipos complexos definidos em modelo de UI, operações de agente de serviço precisam mapear do modelo de UI para modelo de serviço do cliente proxy ao chamar operações de proxy de serviço de cliente. Quando as operações de agente de serviço precisam retornar resultados de volta ao apresentador, eles seriam mapear do modelo de serviço do cliente proxy para modelo de UI depois de obter os resultados das operações de proxy de serviço de cliente.

Isso pode ser um trabalho tedioso. No entanto, existem ferramentas disponíveis para mapear de uma estrutura do modelo outra estrutura de modelo com facilidade, para que isso se torne mais de um trabalho de design ocasional.

Proxy de serviço de cliente e o modelo de serviço do cliente proxy

Proxy de serviço de cliente na tecnologia de serviço da Web fornece acesso local para o cliente do serviço, mesmo que o serviço é hospedado remotamente. Neste artigo, faria descrevo proxy de serviço de cliente como o gateway para o serviço. Modelo de serviço do cliente proxy representa a definição do modelo de contrato de serviço.

Proxy de serviço de cliente passa chamadas para serviços e retorna respostas de serviço. Se o serviço for implementado com serviços de Web do ASP.NET (ASMX) ou tecnologias Windows Communication Foundation (WCF), o proxy de cliente do serviço pode ser gerado automaticamente.

Modelo de serviço do cliente proxy irá refletir a definição de estrutura de modelo de contrato serviço.

Exemplo de implementação

Ilustrar a arquitetura de interface do usuário descrita em Figura 1, let’s dar uma olhada em um aplicativo Windows Forms que demonstra a implementação. Este aplicativo de exemplo é incluído no download deste artigo.

Esse aplicativo de exemplo primeiro precisa carregar uma lista de regiões. Quando uma região é selecionada, os clientes que pertencem à região são exibidos. Quando um cliente é selecionado, uma janela de consulta de intervalo de tempo será exibida. Depois que uma hora de início e término são inseridas, será exibida uma lista de ordens de cliente selecionado na grade de dados na tela principal.

Usarei o cenário de exibir uma lista de regiões para explicar como a arquitetura de interface do usuário em Figura 1 é implementado no aplicativo de exemplo. Figura 2 mostra a seqüência de chamada para esse cenário.


Figura 2 Seqüência de chamada da interface do usuário

Quando a tela principal é carregado, ele primeiro inicia a interface do apresentador e passa a instância de exibição atual ao construtor do apresentador:

private void MainView_Load(

  object sender, EventArgs e) {



  _presenter = new MainPresenter(this);

  ...

}

A inicialização de instância MainPresenter causará construtor do MainPresenter para atribuir primeiro a passado no MainView instância a uma variável particular do tipo IMainView. Em seguida, inicia uma instância do controlador e passa a instância atual do Presenter para o construtor Controller:

public MainPresenter(IMainView view) {

  _view = view;

  _controller = new Controller(this);

}

Controlador instância iniciação fará com que o construtor atribuir a instância de MainPresenter passado a uma variável do tipo IMainPresenter particular. Esse construtor também define o manipulador de eventos para estar preparado para responder a eventos publicados do MainPresenter, como RetrieveRegions:

public Controller(IMainPresenter presenter) {

  _mainPresenter = presenter;

  ...

  _mainPresenter.RetrieveRegions += (OnRetrieveRegions);

}

Voltar no evento de carregamento tela principal do formulário, Presenter é chamado para recuperar regiões depois que o objeto Presenter é iniciado:

Private void MainView_Load(object sender, EventArgs e) {

  ...

  _presenter.OnRetrieveRegionCandidates();

}

Quando o apresentador recebe essa chamada, publica primeiro evento RetrieveRegions em vez de Indo adiante para recuperar as regiões. O evento RetrieveRegions foi definido na interface do IMainPresenter e é implementado no MainPresenter:

public event EventHandler<RetrieveRegionsEventArgs> 

  RetrieveRegions;

  ...



public void OnRetrieveRegionCandidates() {

  if (RetrieveRegions != null) {

    RetrieveRegions(this, 

      new RetrieveRegionsEventArgs());

  }

}

Na classe Controller, desde que um manipulador de eventos para RetrieveEvents tiver sido registrado, ele pode responder ao evento RetrieveRegions:

private void OnRetrieveRegions(

  object sender, RetrieveRegionsEventArgs e) {



  _mainPresenter.HandleRetrieveRegionsEvent();

}

Controlador decide que o fluxo do processo deve retornar o controle ao MainPresenter e pede para continuar recuperar as regiões. Se precisar de controlador iniciar os apresentadores sejam MainPresenter, ele pode empregar Unity Framework para executar essa tarefa.

Do MainPresenter HandleRetrieveRegionsEvent operação, ele chama agente de serviço para recuperar as regiões. Para simplificar, meu exemplo, na verdade, não implementa o serviço. Ele apenas grava alguns dados fictícios para que o aplicativo funcione. Depois de um resultado é retornado pelo agente do serviço, observe que MainPresenter não passar dados para MainView. Em vez disso, ele atualiza RegionCandidates propriedade do MainView:

public void HandleRetrieveRegionsEvent() {

  RegionAdminServiceAgent agent = 

    new RegionAdminServiceAgent();

  List<Region> regionCandidates = agent.RetriveRegions();

  _view.RegionCandidates = regionCandidates;

}

Na propriedade de RegionCandidates do MainView, ele lida com a exibição das regiões:

public List<UIModel.Region> RegionCandidates {

  set {

    _regionCandidates = value;

    PopulateRegionCandidates();

  }

}

Esta é a seqüência inteira de recuperar regiões e exibi-los no MainView. Ela envolve definitivamente mais etapas que simplesmente chamando o agente de serviço para obter as regiões. No entanto, quando pensando da perspectiva de um aplicativo corporativo, ele não só apresenta um design menos rígido, ele também promove um padrão de implementação consistente. Isso pode simplificar muito manutenção e transferência de Conhecimento para uma equipe de desenvolvimento.

Apenas um comentário mais sobre este exemplo de código: toda a seqüência inicia com o primeiro evento de carga do Windows Forms. Uma implementação mais avançada poderia começar com controlador e permitem Controller decidir o que é o primeiro formulário a ser carregado.

Resumo

Neste artigo, apresentei uma abordagem ao design de arquitetura da interface do usuário com base em extensão o padrão MVP. Aplicativos de interface do usuário podem ser complicados e há muitos tipos diferentes do design de aplicativo de interface do usuário. A técnica apresentadas neste artigo representa uma das muitas dessas soluções. Essa é uma técnica útil em muitas situações, mas certifique-se de que ele adequada às suas necessidades antes de implementá-la.

Já existem muitas estruturas de interface do usuário no mercado e muitas são baseadas no MVP, Model-View-Controller ou padrões desenvolvidos como extensões desses dois. Uma boa primeira etapa é ver quais partes principais são implementados por estas estruturas — por exemplo, como a maneira como eu abstraídos a arquitetura de interface do usuário neste artigo. Não é bom pensar arquitetura caindo em detalhes de implementação sem primeiro considerar a visão geral. Começando com uma compreensão ampla arquitetura de problema que temos em mãos garante não só que os problemas básicos da arquitetura do sistema forem resolvidos, mas também que pode ser seguido de um design bem planejado-out e replicáveis.

Finalmente, no exemplo deste artigo, a implementação do controlador foi criada com translation from VPE for Csharp. Pode ser uma abordagem melhor para usar uma tecnologia de fluxo de processo, como o Windows Workflow Foundation, que pode permitir que um projeto e implementação mais flexível. No entanto, esse detalhe de implementação técnica não afetará os princípios fundamentais por trás da arquitetura de interface do usuário descrito neste artigo.

Para mais informações sobre do padrão MVP, consulte o Edição de agosto de 2006 da MSDN Magazine.

Zhe Ma é arquiteto técnico da Enterprise Architecture Group Unum, sediada em Portland, Maine. Ele pode ser contatado em zma@Unum.com.

Graças ao especialista técnico seguir para revisar este artigo: Don Smith