Compartilhar via


Este artigo foi traduzido por máquina.

Cutting Edge

Melhores Web Forms com o padrão MVP

Dino Esposito

Dino EspositoO advento do padrão MVC (Model-View-Controller) é uma etapa importante no desenvolvimento de software. Ele mostrou que a criação de aplicativos com separação de preocupações, lembre-se melhor o processo de desenvolvimento e o aplicativo concluído. Ele também oferecido um método reproduzível para colocar esse padrão em prática.

MVC não é perfeito, no entanto, para que diversas variações apareciam ao longo dos anos.

Porque ele foi desenvolvido nos anos 80, um problema que está sendo reproduzido é que o MVC não acomoda diretamente o desenvolvimento para a Web. Adaptar o MVC para Web levou alguns anos mais e levou ao desenvolvimento de padrões MVC mais específicos como, por exemplo, o Model2. (O Model2 é o sabor real do MVC implementado pelo castelo MonoRail e o asp.net MVC).

Em um contexto mais geral, o padrão MVP (Model-View-Presenter) é uma evolução do MVC que separa o modo de exibição e o modelo organizado, colocando o controlador entre como um mediador. A Figura 1 ilustra o comportamento de um aplicativo desenvolvido com o padrão MVP.

Figure 1 Using the MVP Pattern

Figura 1 de usar o padrão MVP

Neste artigo, eu irá primeiro apresentar uma implementação possível (e relativamente padronizada) do padrão MVP para Web Forms do asp.net e, em seguida, discutir a aplicação do padrão, seus benefícios para a equipe e compará-lo com o asp.net MVC e Model-View-ViewModel (MVVM), como foi implementada no Windows Presentation Foundation (WPF) e o Silverlight.

MVP em uma visão geral

O MVP é um derivado do padrão MVC original desenvolvido em Taligent (agora parte da IBM), na década de 1990. O papel disponível para download em wildcrest.com/Potel/Portfolio/mvp.pdf de oferece uma boa introdução aos MVP e as idéias por trás dele.

Os criadores do MVP claramente separado do modelo (os que está sendo trabalhados no modo de exibição de dados) do par de modo de exibição/controlador. Eles também renomear o controlador como orador para reforçar a idéia de que o padrão, a função do controlador é de um mediador entre o usuário e o aplicativo. O apresentador é o componente que “ apresenta ” a interface do usuário para o usuário e aceita comandos do usuário. O apresentador contém a maior parte da lógica de apresentação e sabe como lidar com o modo de exibição e o restante do sistema, incluindo as camadas de serviços e dados de back-end.

Uma chave inovação no MVP é o fato de que os detalhes do modo de exibição são abstraídos para uma interface (ou a classe base). O apresentador fala com uma abstração do modo de exibição, que faz com que o apresentador-se uma classe altamente reutilizável e altamente testável. Isso permite que os dois cenários interessantes.

Em primeiro lugar, a lógica de apresentação é independente da tecnologia de interface do usuário que está sendo usada. Subseqüentemente, o mesmo controlador pode ser reutilizado nas camadas de apresentação do Windows e Web. No final, o apresentador é codificado em relação a uma interface e pode conversar com qualquer objeto que expõe a interface — se um objeto do Windows Forms, um objeto de página asp.net ou um objeto Window do WPF.

Em segundo lugar, o mesmo apresentador pode funcionar com diferentes modos de exibição do mesmo aplicativo. Esta é uma conquista importante em relação ao software como um cenários (SaaS) do serviço em que um aplicativo é hospedado em um servidor Web e oferecido como um serviço aos clientes, cada um exigindo sua própria interface do usuário personalizada.

Nem é preciso dizer que os dois benefícios não são necessariamente se aplicam a todas as situações. Se esses benefícios que depende em grande parte do aplicativo e a lógica de navegação que você espera que empregam na frente do Windows e Web fim. No entanto, quando a lógica é a mesma, você poderá reutilizá-la por meio do modelo do MVP.

MVP em ação

Ao implementar o padrão MVP, a primeira etapa é definir a abstração para cada modo de exibição de obrigatório. Cada página em um aplicativo asp.net e cada aplicativo de formulário no Windows (ou WPF/Silverlight) terá sua própria interface de se comunicar com o restante da camada de apresentação. A interface identifica o modelo de dados que ofereça suporte ao modo de exibição. Cada modo de exibição logicamente equivalente terão a mesma interface independentemente da plataforma.

A abstração do modo de exibição incorpora o modelo reconhece o modo de exibição e funciona com e estendê-lo com alguns métodos ad hoc e útil para favorecer uma suave interação entre o apresentador e o modo de exibição de eventos. A Figura 2 mostra uma abstração possível para o modo de exibição renderizada no Figura 3 que está sendo usado por um aplicativo de lista de tarefas pendentes simples.

De um exemplo de uma abstração do modo de exibição, a Figura 2

public interface IMemoFormView {
  String Title { get; set; }
  String Summary { get; set; }
  String Location { get; set; }
  String Tags { get; set; }
  DateTime BeginWithin { get; set; }
  DateTime DueBy { get; set; }
  String Message { get; set; }

  Int32 GetSelectedPriorityValue();
  void FillPriorityList(Int32 selectedIndex);
  Boolean Confirm(String message, String title);
  void SetErrorMessage(String controlName);
}

Do Figura 3 também ver como os membros na interface de correspondência entre os elementos visuais no formulário.

Figure 3 Binding Members of the Interface to Visual Elements

De membros de interface visual de elementos de ligação, a Figura 3

O ponto fundamental é que qualquer interação entre o apresentador e a interface do usuário deve ocorrer por meio do contrato do modo de exibição. Qualquer digitando qualquer botão clicando em qualquer seleção deve ser encaminhado para o apresentador e manipulado lá. Se o apresentador precisa para consultar alguns dados no modo de exibição ou passar dados para o modo de exibição, deve haver um método na interface para considerar que.

Implementando o contrato do modo de exibição

A interface que representa o modo de exibição deve ser implementada pela classe que representa o modo de exibição. Como mencionado, o modo de exibição classe é a página no asp.net, o formulário no Windows Forms, a janela no WPF e o controle de usuário no Silverlight. Figura 4 mostra um exemplo para o Windows Forms.

Figura 4 de de uma possível implementação da classe modo de exibição

public partial class MemoForm : Form, IMemoFormView {
  public string Title {
    get { return memoForm_Text.Text; }
    set { memoForm_Text.Text = value; }
    ...
}

  public DateTime DueBy {
    get { return memoForm_DueBy.Value; }
    set { memoForm_DueBy.Value = value; }
  }

  public int GetSelectedPriorityValue() {
    var priority = 
      memoForm_Priority.SelectedItem as PriorityItem;
    if (priority == null)
      return PriorityItem.Default;
    return priority.Value;
  }

  public void FillPriorityList(int selectedIndex) {
    memoForm_Priority.DataSource = 
      PriorityItem.GetStandardList();
    memoForm_Priority.ValueMember = "Value";
    memoForm_Priority.DisplayMember = "Text";
    memoForm_Priority.SelectedIndex = selectedIndex;
  }

  public void SetErrorMessage(string controlName) {
    var control = this.GetControlFromId(controlName);
    if (control == null)
      throw new NullReferenceException(
        "Unexpected null reference for a form control."); 

    memoForm_ErrorManager.SetError(control, 
      ErrorMessages.RequiredField);
  }

  ...
}

Como você pode ver, as propriedades são implementadas como wrappers para algumas propriedades de controles visuais. Por exemplo, a propriedade Title é um wrapper para a propriedade Text de um controle TextBox. Da mesma forma, a propriedade DueBy quebra a propriedade Value de um controle de selecionador de data. Além disso, a interface protege a classe do apresentador dos detalhes da interface do usuário para uma determinada plataforma. Da mesma classe de apresentador criada para interagir com a interface IMemoFormView pode lidar com qualquer objeto que implementa a interface, tranqüilamente ignorar os detalhes da interface de programação de controles subjacentes.

Como você lida com elementos de interface do usuário que necessitam de uma coleção de dados, como, por exemplo, uma lista drop-down? Você deve usar ligação de dados (como em do Figura 4) ou você deve optar por uma abordagem mais simples que mantém o modo de exibição passiva e sem qualquer lógica de apresentação?

Isso ocorre você. Em resposta a esses tipos de perguntas, o padrão MVP foi dividido em dois padrões separadas — Supervising Controller e modo passivo — cuja principal diferença é a quantidade de código no modo de exibição. Usando a ligação de dados para preencher a interface do usuário (consulte do Figura 4) adiciona uma lógica de apresentação no modo de exibição e poderia torná-lo a obter o sabor de um controlador supervising.

Mais lógica você tem o modo de exibição, mais cuidado sobre o teste. E uma parte da interface do usuário de teste é uma tarefa que não pode ser automatizada com facilidade. Indo para um controlador supervising ou Cancelar para uma visão mais fina e dumber é simplesmente uma chamada de bom senso.

A classe do apresentador

Os controles no modo de exibição de capturar os gestos do usuário e disparam um evento para o modo de exibição, como um clique de botão ou uma alteração do índice selecionada. O modo de exibição contém manipuladores de eventos simples que expedir a chamada para o apresentador é encarregada do modo de exibição. Quando o modo de exibição for carregado pela primeira vez, ele cria uma instância da sua classe de apresentador e salva que internamente como um membro particular. A Figura 5 mostra o construtor típico de um Windows Form.

De criação de um formulário do MVP, a Figura 5

public partial class Form1 : 
  Form, ICustomerDetailsView {

  private MemoFormPresenter presenter;

  public Form1() {
    // Framework initialization stuff
    InitializeComponent();
    // Instantiate the presenter
    presenter = new MemoFormPresenter(this);
    // Attach event handlers
    ...
}

  private void Form1_Load(
    object sender, EventArgs e) {

    presenter.Initialize();
  }
  ...
}

A classe do apresentador geralmente recebe uma referência ao modo de exibição por meio de seu construtor. O modo de exibição contém uma referência para o apresentador e o apresentador mantém uma referência ao modo de exibição. No entanto, o apresentador sabe que o modo de exibição somente por meio do contrato. O apresentador funciona, separar a qualquer objeto de modo que recebe a sua interface do modo de exibição contratado. Figura 6 mostra as noções básicas de uma classe do apresentador.

Figura 6 de de uma classe Presenter de exemplo

public class MemoFormPresenter {
  private readonly IMemoFormView view;

  public MemoFormPresenter(IMemoFormView theView) {
    view = theView;
    context = AppContext.Navigator.Argument 
      as MemoFormContext;
    if (_context == null)
      return;
  }
 
  public void Initialize() {
    InitializeInternal();
  }

  private void InitializeInternal() {
    int priorityIndex = _context.Memo.Priority;
    if (priorityIndex >= 1 && priorityIndex <= 5)
      priorityIndex--;
    else
      priorityIndex = 2;

    if (_context.Memo.BeginDate.HasValue)
      _view.BeginWithin = _context.Memo.BeginDate.Value;
    if (_context.Memo.EndDate.HasValue)
      _view.DueBy = _context.Memo.EndDate.Value;
      _view.FillPriorityList(priorityIndex);
      _view.Title = _context.Memo.Title;
      _view.Summary = _context.Memo.Summary;
      _view.Tags = _context.Memo.Tags;
      _view.MemoLocation = _context.Memo.Location;
  }
  ...
}

O construtor recebe e armazena em cache a referência para o modo de exibição e inicializa o modo de exibição usando a interface pública representada pelo contrato. O objeto de contexto é ver usado no código de do Figura 6 é qualquer o apresentador precisa receber do chamador para inicializar o modo de exibição de dados de entrada. Essas informações não são necessárias em todos os casos, mas na verdade ser necessária quando você usar o formulário para editar alguns dados ou quando você tem as caixas de diálogo para exibir algumas informações.

Inicializar o modo de exibição é tão simples quanto a atribuição de valores para os membros de uma classe, exceto que agora qualquer atribuição resulta em uma atualização para a interface do usuário.

A classe do apresentador também contém uma série de métodos que são executados em resposta a todas as solicitações de interface do usuário. Qualquer ação de clicar ou de usuário está acoplada a um método na classe do apresentador:

private void memoForm_OK_Click(
  object sender, EventArgs e) {
  presenter.Ok();
}

O método de apresentador usa a modo de exibição referência para acessar valores de entrada e atualiza a interface do usuário da mesma maneira.

O apresentador também é responsável pela navegação dentro do aplicativo. Em particular, o apresentador é responsável por sub-views ativação (ou desativar) e navegação de comando para a exibição seguinte.

Um sub-view é essencialmente um subconjunto do modo de exibição. Geralmente, é um painel que pode ser expandido ou recolhido de acordo com o contexto ou talvez uma janela filho – janela restrita ou sem janela restrita. O apresentador controla a visibilidade do sub-views por meio de membros (principalmente booleanos membros) na interface do modo de exibição.

E quanto a transferência de controle para outro modo de exibição (e apresentador)? Você cria uma classe estática que representa o controlador de aplicativo — ou seja, o console central que contém toda a lógica para determinar a exibição seguinte. A Figura 7 mostra o diagrama do aplicativo controlador.

Figure 7 The Application Controller

A Figura 7 do controlador a aplicativos

A classe de controlador de aplicativo representa o shell que os apresentadores de invocação para navegar em outro lugar. Essa classe terá um método NavigateTo que implementa o fluxo de trabalho que determina o modo de exibição próximo ou que simplesmente se move para o modo de exibição especificado. O fluxo de trabalho pode ser qualquer coisa — tão complexa quanto um fluxo de trabalho real ou simplesmente uma seqüência de instruções IF. A lógica do fluxo de trabalho pode ser codificada no controlador de aplicativo ou importada de um componente externo e conectável estaticamente (consulte do Figura 8).

De de implementação de um controlador de aplicativo, a Figura 8

public static class ApplicationController {
  private static INavigationWorkflow instance;
  private static object navigationArgument;

  public static void Register(
    INavigationWorkflow service) {
    if (service == null)
      throw new ArgumentNullException();
    instance = service;
  }

  public static void NavigateTo(string view) {
    if (instance == null)
      throw new InvalidOperationException();
    instance.NavigateTo(view);      
  }
 
  public static void NavigateTo(
    string view, object argument) { 
    if (instance == null)
      throw new InvalidOperationException();
    navigationArgument = argument;
    NavigateTo(view);
 }

 public static object Argument {
   get { return navigationArgument; }
 }
}

A lógica de navegação reais no componente de fluxo de trabalho usará uma solução específica de plataforma para alternar para outro modo de exibição. Para Windows Forms, ele usará o métodos para abrir e exibir formulários;no asp.net, ele usará o método de redirecionamento do objeto Response.

MVP e o asp.net MVC

ASP.net MVC baseia-se em um tipo do padrão MVC que tem algumas coisas em comum com o MVP. O controlador de MVC é um mediador entre o modo de exibição e o back-end. O controlador não mantenha uma referência ao modo de exibição, mas encher a um objeto de modelo e passa que para o modo de exibição usando os serviços de componente de intermediário, o mecanismo de exibição.

Uma forma, o modo de exibição é abstraído por meio do modelo, cuja ture de ­ estru reflete as características do modo de exibição e sua interface do usuário. Navegação é gerenciada pelo controlador, seleciona o próximo modo de exibição do contexto de cada ação. Ele faz isso usando alguma lógica interna. A lógica seria particularmente complexa para um controlador de determinado método, francamente, não algo que irá acontecer a cada dia — você sempre pode introduzir um componente de fluxo de trabalho determina o modo de exibição próximo para selecionar.

E quanto a Web Forms? Os Web Forms se presta bem para o host de uma implementação do MVP. No entanto, ele deve estar claro que tudo o que está adicionando camadas no contexto do evento postback. Qualquer coisa antes que o postback não pode ser incorporado, nem pode tudo o que acontece após o evento de postback. Uma implementação completa do MVP que se expande para abranger o ciclo de vida completo não é possível nos Web Forms, mas até mesmo adicionar MVP em torno de postback é uma coisa boa e aumentará significativamente o nível de capacidade de teste
páginas de Web Forms.

MVP e MVVM

E, em vez disso, MVP e MVVM no contexto dos aplicativos do WPF e Silverlight? MVVM é uma variação do MVP, também conhecido como Presentation Model. A idéia é que o modelo de modo de exibição é incorporado a classe do apresentador e a classe do apresentador expõe membros públicos que o modo de exibição vai ler e gravar. Isso ocorre por meio de ligação de dados bidirecional. No final do dia, você pode chamar MVVM como um tipo especial de MVP especialmente adequado para as interfaces do usuário avançadas e frameworks (como o WPF) que promovem esta capacidade.

Em MVVM, o modo de exibição é vinculado a dados para propriedades da classe do apresentador (o modelo de modo de exibição). Tudo o que o usuário atualiza essas propriedades no apresentador. Todas as solicitações do usuário (comandos no WPF) são tratadas por meio de um método na classe do apresentador. Nenhum resultado, que o método de apresentador calcula é armazenado no modelo do modo de exibição e disponibilizado por meio de ligação de dados no modo de exibição. No WPF e Silverlight, não há nada que impeça o uso de uma implementação manual do padrão MVP. No entanto, acontece que ferramentas como o Blend tornará mais simples ainda eficiente usar MVVM via ligação de dados.

Nova postagem

MVP oferece orientação sobre como gerenciar pilhas de modos de exibição e, bem, obviamente, tem um custo: o custo de maior complexidade no código do aplicativo. Como você pode imaginar, esses custos são mais fáceis absorver em aplicativos grandes que programas simples. MVP, portanto, não é apenas para qualquer aplicativo. Com base em um contrato que representa o modo de exibição, permite que o MVP para desenvolvedores e designers trabalhem em paralelo, o que é sempre uma boa coisa em qualquer cenário de desenvolvimento. MVP mantém a classe do apresentador como independente e isolada do modo de exibição. No Web Forms, MVP representa a forma só razoável de adicionar capacidade de teste pelo menos para o código que executa o postback.

Dino Esposito é o autor do “ Programming ASP.NET MVC ” da Microsoft Press e co-autor de “ .NET da Microsoft: Arquitetura de aplicativos para a empresa ” (Microsoft Press, 2008). Residente na Itália, Esposito é um palestrante sempre presente em eventos do setor no mundo inteiro. Você pode associar o seu blog em /despos de .

Graças aos seguintes especialistas técnicos para revisão deste artigo: de Don Smith e de Josh Smith