C# -  Padrão Comportamental Gof - Mediator


 Neste artigo vou apresentar o padrão comportamental Gof Mediator.

Segundo a definição da Gang Of Four(GoF), o padrão Mediator define um objeto que encapsula como um conjunto de objetos interage.

Este padrão promove o acoplamento fraco evitando que os objetos se refiram explicitamente uns aos outros e permite que você varie sua interação de forma independente.

Podemos dizer que o padrão Mediator gerencia as interações de diferentes objetos, e, usando uma classe mediadora, centraliza todas as interações entre os objetos, visando diminuir o acoplamento e a dependência entre eles.



Desta forma, neste padrão, os objetos não conversam diretamente entre eles, e toda comunicação precisa passar pela classe mediadora. Assim este padrão permite que um grupo de objetos se comuniquem entre si sem que haja acoplamento entre eles através de um objeto mediador.

Podemos dizer que a intenção do padrão Mediator é definir um objeto que encapsula como um conjunto de objetos interagem.

Mediator : Exemplo

Para entender melhor como Mediator age  vamos considerar o seguinte cenário

1- Suponha que temos 4 objetos : A, B , C e D. 

E esses 4 objetos precisam se comunicar uns com os outros :

2- Suponha que o objeto A quer se comunicar com o objeto B.

3- Então o objeto A deve conhecer/ter uma referencia ao Objeto B e, usando essa referência, o Objeto A pode chamar o método do Objeto B e assim se comunicar com ele

4- Da mesma forma se o objeto B quiser enviar uma mensagem para o objeto C então ele deverá conhecer/ter uma referência para o objeto C e usando essa referência ele irá chamar o método do objeto C e enviar uma mensagem

5- Isso se repete para os demais objetos que desejam interagir

- O objeto C com o objeto D
- O objeto D com o objeto A
- O objeto A com o objeto C
- O objeto B com o objeto D

Este cenário apresenta um forte acoplamento entre os objetos :



Pois temos um monte de objetos que precisam conhecer/ter uma referência entre si, ou seja, cada objeto precisa ter conhecer os objetos com os quais deseja se comunicar.

Aqui temos apenas 4 objetos agora imagine um projeto do mundo real podemos ter centenas/milhares de objetos desejando se comunicar uns com os outros.

Como permitir que um grupo de objetos se comunique entre si sem que haja acoplamento entre eles ?

Aqui entre em cena o padrão Mediator.

Podemos então resolver esses problemas introduzindo um objeto Mediator, que será responsável por intermediar a comunicação entre todos os objetos.



Assim agora cada objeto não precisa mais conhecer os demais objetos bastando apenas conhecer o objeto Mediator e enviar uma mensagem para este objeto.   Aqui o objeto Mediator vai atuar recebendo a mensagem de cada objeto e roteando a mensagem para o objeto de destino.

É assim que o padrão Mediator funciona, e , com esse padrão, cada objeto possui uma única responsabilidade e consegue se comunicar com outros objetos sem a necessidade de conhece-los, ou seja, cada objeto, trabalha de forma independente e isolada, não havendo acoplamento entre eles.

Diagrama UML

O diagrama UML do padrão Mediator segundo o Gof apresenta os seguintes participantes :


1- Mediator -

- Define uma interface para comunicação com os objetos Colleague e os métodos que podem ser chamados por objetos Colleague;

2- ConcreteMediator -

- Implementa o contrato definido em Mediator e mantêm uma referência aos objetos Colleague e a comunicação e transferência de mensagens entre as classes Colegue;

3- Colleague

- Mantém uma referência ao seu objeto Mediator e se comunica com o Mediator sempre que necessário;

3- ConcreteCollegue 1 e 2

- Essas classes se comunicam entre si por meio do Mediator, cada uma herda o campo mediator da classe Colleague;

Quando podemos usar o padrão

Podemos usar o padrão Mediator nos seguintes cenários :

- As mudanças no estado de um objeto afetam muitos outros objetos;
- O grande número de interconexões entre os objetos torna o sistema pesado e difícil de mudar;
- Você deseja ser capaz de alterar as partes de um sistema independentemente umas das outras;
- Quando existem muitos relacionamentos entre os objetos e um ponto comum de controle ou comunicação é necessário;


Vantagens do padrão

Como vantagens deste padrão temos que :

- Desacoplamento entre os objetos, pois nenhum objeto se conhece na comunicação;
- O fluxo de comunicação está centralizado, com isso, alterações no mediador não afetam seus ouvintes;
- Mudanças podem ser aplicadas facilmente nos objetos, pois são independentes;
- Eliminação de relacionamentos muitos para muitos (são todos substituídos por relacionamentos um para muitos);

Desvantagem

E como desvantagem deste padrão temos que:

- 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 tornarem mais complexos;

Exemplo de implementação do padrão Mediator

Como exemplo de implementação do padrão Mediator vamos considerar o ambiente do Facebook  No Facebook podemos criar grupos específicos no qual podemos compartilhar mensagens e recursos.

Vamos supor que temos um grupo no Facebook com alguns membros (para o exemplo eu vou considerar apenas 4 membros) :



Assim cada membro poderá compartilhar mensagens com quaisquer outros membros do grupo, e, se um usuário quiser compartilhar um link sobre algum assunto ele irá enviar a mensagem para o mediator que vai repassar a todos os membros do grupo.

Implementação prática

Levando em conta este cenário vamos implementar o padrão Mediator usando uma aplicação Console .NET Core (.NET 5.0) criada no VS 2019 Community.

A seguir temos o diagrama de classes obtido a partir do VS 2019 na implementação do padrão:

Podemos identificar as seguinte classes :

- User - Representa o Colleague;
- ConcreteUser -
 Implementa a interface User;
- FacebookGroupMediator -   Representa o Mediator;
- ConcreteFacebookGroupMediador -  Implementa o Mediator;

A seguir temos o código usado na implementação:

1- A interface User

    public abstract class User
    {
        protected FacebookGroupMediator mediator;
        protected string name;
        public User(FacebookGroupMediator mediator, string name)
        {
            this.mediator = mediator;
            this.name = name;
        }
        public abstract void Send(string message);
        public abstract void Receive(string message);
    }

2- A classe ConcreteUser

    public class ConcreteUser : User
    {
        public ConcreteUser(FacebookGroupMediator mediator, string name)
            : base(mediator, name)
        {}
        public override void Receive(string message)
        {
            Console.WriteLine($"{name} : recebida <= { message}");
        }
        public override void Send(string message)
        {
            Console.WriteLine($"{name} : enviada => {message}\n");
            mediator.SendMessage(message, this);
        }
    }

3- A interface FacebookGroupMediator

    public interface FacebookGroupMediator
    {
        void SendMessage(string msg, User user);
        void RegisterUser(User user);
    }

- SendMessage - envia a mensagem para todos os membros do grupo exceto para quem compartilhou a mensagem;
- RegisterUser - registra um usuário em um grupo;

4- Classe ConcreteFacebookGroupMediator

    public class ConcreteFacebookGroupMediator : FacebookGroupMediator
    {
        private List<User> usersList = new List<User>();
        public void RegisterUser(User user)
        {
            usersList.Add(user);
        }
        public void SendMessage(string message, User user)
        {
            foreach (var u in usersList)
            {
                // mensagem não sera recebida por quem a estiver enviando
                if (u != user)
                {
                    u.Receive(message);
                }
            }
        }
    }

7- Program

using System;

namespace Mediator1
{
    class Program
    {
        static void Main(string[] args)
        {
            FacebookGroupMediator facebookMediator =
                new ConcreteFacebookGroupMediator();

            User macoratti = new ConcreteUser(facebookMediator, "Macoratti");
            User miriam = new ConcreteUser(facebookMediator, "Miriam");
            User jessica = new ConcreteUser(facebookMediator, "Jessica");
            User yuri = new ConcreteUser(facebookMediator, "Yuri");

            facebookMediator.RegisterUser(macoratti);
            facebookMediator.RegisterUser(miriam);
            facebookMediator.RegisterUser(jessica);
            facebookMediator.RegisterUser(yuri);
           

            macoratti.Send("O canal macoratti.net " +
                "Esta apresentando uma serie sobre Padrões de Projetos");

            yuri.Send("Onde posso encontrar os vídeos ?");

            macoratti.Send("Veja a playist 'Design Patterns'");
           

            Console.Read();
        }
    }
}

A execução do projeto irá apresentar o seguinte resultado:

O padrão mediator permite manter o nosso domínio seguro e ajuda a evitar o acoplamento desnecessário. Este padrão vem ganhando espaço com a necessidade de softwares mais robustos demandarem de técnicas como DDD, CQRS e microsserviços.

Padrões relacionados com o padrão Mediator:

Pegue o código do projeto aqui :   Mediator1.zip

"E tu, ó menino, serás chamado profeta do Altíssimo, Porque hás de ir ante a face do Senhor, a preparar os seus caminhos; Para dar ao seu povo conhecimento da salvação, Na remissão dos seus pecados;"
Lucas 1:76,77

Referências:


José Carlos Macoratti