.NET

3 ago, 2011

O padrão de projeto Mediator na prática

Publicidade

Os padrões de projeto são soluções prontas e testadas que podem ser aplicadas a situações específicas em um projeto de software.

Por acaso você tem um problema relacionado aos padrões? Parabéns, seu problema já foi resolvido! Basta encontrar o padrão que se encaixa ao seu problema. Simples assim.

Por que perder tempo tentando escrever código para um problema que você pensa que seja somente seu, quando na verdade ele é um problema genérico que outros programadores já enfrentaram e JÁ resolveram?

Neste artigo, eu vou tratar do padrão Mediator. Imagine a seguinte situação:

  • Você desenvolveu um website para e-commerce bem simples com 4 páginas em que os usuários podem consultar um catálogo de produtos e realizar compras
  • Nesse cenário, um usuário pode navegar entre as páginas e aí começam os problemas
  • O código em cada página precisa saber quando ir para uma nova página bem como ativar essa nova página
  • Dessa forma, e mesmo com apenas 4 páginas, existem muitas possibilidades de conexões e de navegação entre as páginas
  • Esse cenário costuma gerar uma grande quantidade de código duplicado em cada página
  • Para resolver o problema, o padrão Mediator pode ser usado para encapsular todo o código da navegação em um objeto separado de forma
  • Dessa forma, cada página deverá apenas reportar qualquer alteração de estado para o objeto mediator – que saberá qual página deve enviar

Definição formal

Define um objeto que encapsula como um conjunto de objetos interage. O padrão Mediator promove um baixo acoplamento evitando que os objetos façam referência uns aos outros de forma explícita, permitindo a você variar o uso da interação de forma independente. [GoF]

Problema

  1. Como permitir que um grupo de objetos se comunique entre si sem que haja acoplamento entre eles?
  2. Como remover o forte acoplamento presente em relacionamentos muitos para muitos?

Solução

Introduzir um mediator: objetos podem se comunicar sem se conhecer.

  • Um objeto Mediador deve encapsular toda a comunicação entre um grupo de objetos
  1. Cada objeto participante conhece o mediador, mas ignora a existência dos outros objetos;
  2. O mediador conhece cada um dos objetos participantes;
  • A interface do Mediador é usada para iniciar a comunicação e receber notificações
  1. O mediador recebe requisições dos remetentes;
  2. O mediador repassa as requisições aos destinatários.

CheckList

  • Identificar uma coleção de objetos que interagem e que se beneficiariam com o desacoplamento mútuo;
  • Encapsular essas interações na abstração de uma nova classe;
  • Criar uma instância dessa nova classe e refazer todos os ‘colegas’ de objetos para interagir com um único Mediador;
  • Equilibrar o princípio do desacoplamento com o princípio da distribuição de responsabilidade de maneira uniforme;
  • Tomar cuidado para criar um ‘Controlador’ ou um objeto ‘deus’.

Diagrama de Classes UML

(Fonte: Mediator Pattern – Object Oriented Design)

Participantes (Classes)

  1. Mediator: define uma interface para comunicação com os objetos Colleague;
  2. ConcreteMediator: conhece as classes Colleague e mantém uma referência aos objetos Colleague. Ele implementa a comunicação e a transferência de mensagens entre as classes Colleague;
  3. Classes Colleague: mantêm uma referência ao seu objeto Mediator – se comunicam com o Mediator sempre que necessário. De outra forma, se comunicam com um Colleague.

Vantagens

  • Desacoplamento entre os diversos participantes da rede de comunicação (participantes não se conhecem);
  • Eliminação de relacionamentos muitos para muitos (são todos substituídos por relacionamentos um para muitos);
  • A política de comunicações está centralizada no mediador e pode ser alterada sem mexer nos colaboradores.

Desvantagens

  • A centralização pode ser uma fonte de gargalos de desempenho e de risco para o sistema em caso de falha;
  • Na prática, os mediadores tendem a se tornar mais complexos.

(Fonte: 2003, Helder L. S da Rocha)

Padrões Relacionados

  1. Facade: Um mediator simplificado torna-se um padrão Facade se o mediador for a única classe ativa e se as classes Colleagues forem classes passivas;
  2. Adapter: O padrão Mediator apenas media os pedidos entre as classes Colleague;
  3. Observer: Os padrões Mediator e Observer são semelhantes, resolvendo o mesmo problema.

Exemplo de Implementação em VB .NET

O exemplo a seguir demonstra a utilização do padrão Mediator para facilitar a comunicação usando o baixo acoplamento entre os diferentes participantes de um Chat. Este exemplo foi adaptado de original encontrado em Mediator Design Pattern in C# and VB.NET.

Abra o Visual Basic 2010 Express Edition e crie um novo projeto do tipo Console Application com o nome PadraoMediator:

Vamos criar 5 classes em nosso projeto da seguinte forma: no menu Project, clique em Add Class e a seguir informe o nome da classe e clique no botão Add. As classes são:

  1. Participante: representa a classe abstrata Colleague. Mantém uma referência ao seu objeto Mediator e se comunica com o Mediator sempre que necessário, de outra forma se comunica com um participante;
  2. Membro: representa a classe ConcreteColleague e herda de Participante;
  3. NaoMembro: representa a classe ConcreteColleague e herda de Participante;
  4. AbstractChatSala: representa a classe Mediator. Define uma interface para comunicação com os objetos Participante;
  5. ChatSala: representa a classe concreta ConcreteMediator. Conhece as classes Participante, mantém uma referência aos objetos Participante e implementa a comunicação e transferência de mensagens entre os objetos da classes Participante.

Vejamos a seguir o código de cada uma dessas classes:

Participante.vb

''' <summary>
''' A classe 'AbstractColleague'
''' </summary>
MustInherit Class Participante

Private _chatsala As Chatsala
Private _nome As String

' Construtor
Public Sub New(ByVal nome As String)
Me._nome = nome
End Sub

' Pega o nome do participante
Public ReadOnly Property Nome() As String
Get
Return _nome
End Get
End Property

' Pega a sala de chat
Public Property Chatsala() As Chatsala
Get
Return _chatsala
End Get
Set(ByVal value As Chatsala)
_chatsala = value
End Set
End Property

' Envia mensagem para um dado participante
Public Sub Enviar(ByVal [para] As String, ByVal mensagem As String)
_chatsala.Enviar(_nome, [para], mensagem)
End Sub

' Recebe mensagem de um participante
Public Overridable Sub Receber(ByVal [de] As String, ByVal mensagem As String)
Console.WriteLine("{0} para {1}: '{2}'", [de], Nome, mensagem)
End Sub

End Class

Membro.vb

''' <summary>
''' A classe 'ConcreteColleague'
''' </summary>
Class Membro
Inherits Participante

' Construtor
Public Sub New(ByVal nome As String)
MyBase.New(nome)
End Sub
'sobrescreve o método Receber
Public Overrides Sub Receber(ByVal [de] As String, ByVal mensagem As String)
Console.Write("para Membro : ")
MyBase.Receber([de], mensagem)
End Sub
End Class

NaoMembro.vb

''' <summary>
''' A classe 'ConcreteColleague'
''' </summary>
Class NaoMembro
Inherits Participante

' Construtor
Public Sub New(ByVal nome As String)
MyBase.New(nome)
End Sub
'sobrescreve o método Receber
Public Overrides Sub Receber(ByVal [de] As String, ByVal mensagem As String)
Console.Write("Para NaoMembro : ")
MyBase.Receber([de], mensagem)
End Sub
End Class

AbstractChatSala

''' <summary>
''' A classe abstrata 'Mediator'
''' </summary>
MustInherit Class AbstractChatSala
Public MustOverride Sub Registro(ByVal participante As Participante)
Public MustOverride Sub Enviar(ByVal [de] As String, ByVal [para] As String, ByVal message As String)
End Class

ChatSala

''' <summary>
''' A classe concreta 'ConcreteMediator'
''' </summary>
Class Chatsala
Inherits AbstractChatSala

Private _participantes As New Dictionary(Of String, Participante)()

Public Overrides Sub Registro(ByVal _participante As Participante)
If Not _participantes.ContainsValue(_participante) Then
_participantes(_participante.Nome) = _participante
End If

_participante.Chatsala = Me
End Sub

Public Overrides Sub Enviar(ByVal [de] As String, ByVal [para] As String, ByVal mensagem As String)
Dim _participante As Participante = _participantes([para])
If _participante IsNot Nothing Then
_participante.Receber([de], mensagem)
End If
End Sub
End Class

No módulo do projeto, inclua o código abaixo no método Main() que irá testar a nossa pequena aplicação:

Module Module1

Sub Main()
'Cria uma sala de chat (chatsala)
Dim chatsala As New Chatsala()

' cria participantes e faz o registro
Dim Macoratti As Participante = New Membro("Macoratti")
Dim Miriam As Participante = New Membro("Miriam")
Dim Jefferson As Participante = New Membro("Jefferson")
Dim Janice As Participante = New Membro("Janice")
Dim Jessica As Participante = New NaoMembro("Jessica")

'registra os participantes
chatsala.Registro(Macoratti)
chatsala.Registro(Miriam)
chatsala.Registro(Jefferson)
chatsala.Registro(Janice)
chatsala.Registro(Jessica)

' Inicia o chat
Jessica.Enviar("Janice", "Olá, Janice!")
Miriam.Enviar("Jefferson", "Como vai você")
Jefferson.Enviar("Macoratti", "Tudo bem")
Miriam.Enviar("Janice", "Como você esta ?")
Janice.Enviar("Jessica", "Tudo tranquilo...")

' aguarda...
Console.ReadKey()
End Sub

End Module

O diagrama de classes para o nosso exemplo feito no BlueJ é exibido a seguir:

O padrão Mediator é muito útil para programadores Visual Basic (principalmente das versões 5 e 6), pois ele é um atalho para a falta de herança. Ele é tipicamente usado em formulários em que o mesmo media por seus controle e componentes.

Deixa suas impressões, compartilhe ideias nos comentários abaixo e pegue o projeto completo aqui: PadraoMediator.zip

Até a próxima!