O Padrão Model-GUI-Mediator

Veja nesse artigo de Paulo Quicoli, como trabalhar com o padrão Model-GUI-Mediator.

A primeira dificuldade que um desenvolvedor RAD que está começando a mudar seu pensamento para o OO enfrenta é sobre a persistência dos objetos, a segunda é como exibir os objetos nos formulários, Figura 1.

A facilidade com que podemos ligar componentes data-aware  à tabelas de um SGBD e sua sincronização automática leva os desenvolvedores a permitir, sem questionamento algum, que tecnologias relacionais modelem a estrutura de seus sistemas. Isso conduz a sistemas dependentes de certos bancos de dados, difíceis de manter e que não conseguem representar toda a complexidade de uma regra de negócios.

O padrão Model-Gui-Mediator, ou simplesmente MGM, oferece uma solução bastante simples para essa dificuldade permitindo a criação de componentes “object-aware”, fazendo com o que o desenvolvedor possa criar um modelo de negócios e apresentá-lo em componentes GUI de forma eficiente.

Figura 1. Como exibir os objetos nos controles visuais?

Apresentação

O padrão MGM é composto por dois padrões de projeto, o Observer e o Mediator. Ele se utiliza desses dois padrões para criar “mediating views”, que nada mais são do que classes que vão intermediar a comunicação entre os objetos que compõe seu modelo de negócios e os componentes visuais que os representarão ao usuário de maneira que estarão desacoplados ao ponto de podermos ter uma mesma propriedade  de um objeto sendo vista  em um TEdit e em um TLabel por exemplo.

Terminologia

O MGM apresenta os seguintes termos:

 
Nota: Existem outros padrões que tratam deste mesmo problema, como o MVC (Model-View-Controller) e MVP (Model-View-Presenter), para cada um deles o termo “View” significa algo diferente. Fazendo uma comparação entre MGM e MVP, uma “View” no MGM equivale a um “Presenter” do MVP.

Na Figura 2 vemos como que se dá o funcionamento do padrão MGM.

Figura 2. Funcionamento do MGM (fonte: andypatterns)

Implementação

Todo padrão possui seu diagrama de classes, na Figura 3 temos uma visão conceitual do MGM.

Figura 3. Diagrama de classes (fonte: andypatterns)

Inicialmente os objetos de negócio devem implementar o Subject do padrão Observer. Posteriormente nossas classes que atuarão como “Medianting Views” deverão implementar o Observer e terão o código responsável para exibir os atributos dos objetos em componentes GUI.

Tendo construído um Mediating View para cada propriedade de um objeto que se deseja exibir, os adicionamos à lista de observers do objeto, lembrando que um medianting view possui uma referência ao controle GUI que será utilizado.

Um exemplo prático

Temos a classe TContato que já implementa o Subject do padrão Observer, conforme visto na listagem 1, e queremos visualizar seus dados em um formulário como na figura 4.

TContato = class(TSubject)    private     FNome: string;     FEndereco: string;     FTelefone: string;     procedure SetNome(const Value: string);     procedure SetEndereco(const Value: string);     procedure SetTelefone(const Value: string);    protected    public      property Nome: string read FNome write SetNome;      property Endereco: string read FEndereco write SetEndereco;      property Telefone: string read FTelefone write SetTelefone;    end;   implementation   { TContato }   procedure TContato.SetEndereco(const Value: string); begin   FEndereco := Value;   Notify; end;   procedure TContato.SetNome(const Value: string); begin   FNome := Value;   Notify; end;   procedure TContato.SetTelefone(const Value: string); begin   FTelefone := Value;   Notify; end;
Listagem 1. Classe TContato
Figura 4. Formulário que exibirá um TContato

Como o padrão sugere temos que criar para cada propriedade que se deseja exibir uma classe mediadora. Essa classe mediadora deve implementar o Observer do padrão observer e é a responsável em saber como atualizar o controle GUI.

Sendo assim, acredito que seja melhor criar uma unit que conterá todas as implementações básicas para cada controle GUI que vamos utilizar. Na Listagem 2 temos a implementação para três controles básicos, um TLabel, um TEdit e um TMakEdit.

TEditMediatorView = class(TObserver)    private      FEdit: TEdit;     procedure SetEdit(const Value: TEdit);    public      constructor Create(const Edit: TEdit); virtual;      destructor Destroy; override;      property Edit: TEdit read FEdit write SetEdit;      procedure OnTextChanged(sender: TObject); virtual; abstract;    end;      TMaskEditMediatorView = class(TObserver)    private     FMaskEdit: TMaskEdit;     procedure SetMaskEdit(const Value: TMaskEdit);    public      constructor Create(const MaskEdit: TMaskEdit); virtual;      destructor Destroy; override;      property MaskEdit: TMaskEdit read FMaskEdit write SetMaskEdit;      procedure OnTextChanged(sender: TObject); virtual; abstract;    end;      TLabelStringMediatorView = class(TObserver)    private     FaLabel: TLabel;     procedure SetaLabel(const Value: TLabel);    public      constructor Create(const aLabel: TLabel); virtual;      destructor Destroy; override;      property aLabel: TLabel read FaLabel write SetaLabel;    end;   implementation   { TEditMediatorView }   constructor TEditMediatorView.Create(const Edit: TEdit); begin   inherited Create;   FEdit := Edit;   FEdit.OnChange := OnTextChanged; end;   destructor TEditMediatorView.Destroy; begin   Edit.OnChange := nil;   Edit := nil;   inherited; end;   procedure TEditMediatorView.SetEdit(const Value: TEdit); begin   FEdit := Value; end;   { TMaskEditMediatorView }   constructor TMaskEditMediatorView.Create(const MaskEdit: TMaskEdit); begin   FMaskEdit := MaskEdit;   FMaskEdit.OnChange := OnTextChanged; end;   destructor TMaskEditMediatorView.Destroy; begin   MaskEdit.OnChange  := nil;   MaskEdit := nil;   inherited; end;   procedure TMaskEditMediatorView.SetMaskEdit(const Value: TMaskEdit); begin   FMaskEdit := Value; end;   { TLabelStringMediatorView }   constructor TLabelStringMediatorView.Create(const aLabel: TLabel); begin   FaLabel := aLabel; end;   destructor TLabelStringMediatorView.Destroy; begin   FaLabel := nil;   inherited; end;   procedure TLabelStringMediatorView.SetaLabel(const Value: TLabel); begin   FaLabel := Value; end;
Listagem 2. Unit Patterns.MGM

Essas classes serão utilizadas como base para as classes que cuidarão de cada propriedade dos objetos TContato. Veja a Listagem 3.

TContatoNomeView = class(TEditMediatorView)   public     procedure Update; override;     procedure OnTextChanged(Sender: TObject); override;   end;     TContatoNomeLabelView = class(TLabelStringMediatorView)   public     procedure Update; override;   end;     TContatoEnderecoView = class(TEditMediatorView)   public     procedure Update; override;     procedure OnTextChanged(Sender: TObject); override;   end;     TContatoTelefoneView = class(TMaskEditMediatorView)   public     procedure Update; override;     procedure OnTextChanged(Sender: TObject); override;   end;   implementation   { TContatoNomeView }   procedure TContatoNomeView.OnTextChanged(Sender: TObject); begin   TContato(FSubject).Nome := Edit.Text; end;   procedure TContatoNomeView.Update; begin   Edit.Text:= TContato(FSubject).Nome; end;   { TContatoEnderecoView }   procedure TContatoEnderecoView.OnTextChanged(Sender: TObject); begin   TContato(FSubject).Endereco := Edit.Text; end;   procedure TContatoEnderecoView.Update; begin   Edit.Text := TContato(FSubject).Endereco; end;   { TContatoTelefoneView }   procedure TContatoTelefoneView.OnTextChanged(Sender: TObject); begin   TContato(FSubject).Telefone := MaskEdit.Text; end;   procedure TContatoTelefoneView.Update; begin   MaskEdit.Text := TContato(FSubject).Telefone; end;   { TContatoNomeLabelView }   procedure TContatoNomeLabelView.Update; begin   aLabel.Caption := TContato(FSubject).Nome; end;
Listagem 3. Unit BusinessView

Com o núcleo do nosso MGM podemos agora ativa-lo em nosso formulário. No evento onCreate do formulário visto na Figura 4 vamos criar os objetos mediators e relaciona-los aos componentes GUI e ao objeto TContato. Veja Listagem 4.

FContato := TContato.Create;   FContatoNome := TContatoNomeView.Create(EditNome); FContatoNomeLabel := TContatoNomeLabelView.Create(Label5); FContatoEndereco := TContatoEnderecoView.Create(EditEndereco); FContatoTelefone := TContatoTelefoneView.Create(EditTelefone);   FContato.Attach(FContatoNome); FContato.Attach(FContatoNomeLabel); FContato.Attach(FContatoEndereco); FContato.Attach(FContatoTelefone);
Listagem 4. Evento onCreate

Feito isso ao executar o exemplo nossos componentes estarão ligados às propriedades do objeto como se fossem componentes db-aware. O usuário ao digitará nos TEdit e o padrão MGM se encarregará de atualizar o objeto TContato. Se alterarmos via programação alguma propriedade do objeto, essa mudança será refletida nos controles GUI associados a ele.

Conclusão

A utilização do padrão MGM oferece de maneira simples uma forma de se separar os controles de interface  dos objetos de negócio. Com isso pode-se criar classes “Mediator View” para as mais diversas tecnologias de GUI, como componentes ASP.NET, IntraWeb, controles VCL e qualquer controle de terceiro que se deseja utilizar.

Claro que bastante código deve ser implementado, mas tendo esse código implementado uma vez, sua reutilização é total, além de se permitir que testes possam ser executados contra as interfaces.

Ebook exclusivo
Dê um upgrade no início da sua jornada. Crie sua conta grátis e baixe o e-book

Artigos relacionados