C# -  Padrões Estruturais Gof - Proxy


 Neste artigo vou apresentar o padrão estrutural Gof Proxy.

O padrão Proxy é um padrão de projeto estrutural que fornece um objeto que atua como um substituto para um objeto de serviço real usado por um cliente para controlar o acesso a ele.

Este padrão permite encapsular a instância de uma classe complexa usando uma instância de outra classe que possui a mesma interface. Com isso podemos controlar a criação e acesso aos objetos complexos usando objetos mais simples.

Proxy significa 'no lugar de' ou 'em nome de'.

Assim o Proxy recebe solicitações do cliente, realiza alguma tarefa (controle de acesso, armazenamento em cache etc.) e passa a solicitação para um objeto de serviço.

Este padrão é muito usado quando precisamos criar um invólucro para ocultar a complexidade do objeto principal do cliente.

Exemplo de uso do padrão Proxy

Vejamos um exemplo simples e prático do mundo real para entender o Proxy.

Quando emitimos um cheque ou usamos o cartão de crédito em uma compra ou pagamento :


Neste contexto o cheque e/ou o cartão de crédito atuam como um Proxy da nossa conta bancária :

Ou seja, um cheque ou cartão de credito ou ordem de pagamento é uma procuração para fundos em uma conta.



Assim podemos usar o cheque/cartão no lugar do dinheiro que esta na conta e eles fornecem uma maneira de acessar o dinheiro na conta quando isso for preciso.

É exatamente assim que o padrão Proxy faz:  Controla e gerencia o acesso ao objeto que ele esta representando ou substituindo.

Tipos de Proxy

Temos 3 tipos principais de Proxy

1 - Proxy Virtual :  Um proxy virtual é um espaço reservado para objetos que consomem muitos recursos para serem criados
Neste contexto o objeto real só é criado quando um cliente primeiro solicita ou acessa o objeto.

2 - Proxy Remoto:  Um proxy remoto fornece representação local para um objeto que reside em um espaço de endereço diferente.  O conceito de proxy remoto é semelhante ao conceito de uso de serviços da web em que o cliente deve acessar o recurso remoto em outra rede, usando a instância da classe proxy.

3 - Proxy de proteção:  Controla o acesso a um objeto principal. Podemos assim criar um proxy seguro de um recurso adicionando algum tipo de autenticação para o código do cliente fornecer. O objeto proxy verifica se o chamador tem as permissões de acesso necessárias antes de encaminhar a solicitação.

Isso é conhecido como Proxy de proteção, pois os recursos são protegidos pelo proxy e o código do cliente deve passar pelo processo de autenticação.

Diagrama UML

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


1- Subject : Define a interface comum para RealSubject e Proxy para que um Proxy possa ser usado em qualquer lugar em que um RealSubject for esperado.

2- Proxy : Mantém uma referência que permite ao proxy acessar RealSubject. O proxy pode referir-se a um Subject se as interfaces RealSubject e Subject forem iguais. Fornece uma interface idêntica à do Subject para que um proxy possa ser substituído pelo RealSubject.

3- RealSubjcet : Define o objeto real que o Proxy representa.

Embora o padrão Proxy não seja muito usado nas aplicações C#, ele ainda é muito útil em alguns casos especiais. Como exemplo ele pode ser muito útil quando você deseja adicionar alguns comportamentos adicionais a um objeto de alguma classe existente sem alterar o código cliente.   

Quando podemos usar o padrão Proxy

podemos usar o padrão Proxy :

- Quando precisarmos criar objetos sob demanda quando suas operações forem solicitadas;

- Quando temos uma classe que com dados sensíveis (que deveriam ser de acesso restrito) e é necessário fornecer o controle de acesso para o objeto original;

- Quando temos que usar classes onde a criação dos objetos é muito demorada e consome muitos recursos;

- Quando devemos permitir o acesso a um objeto remoto usando um objeto local (referenciando o objeto remoto);

- Para adicionar um recurso thread-safe a uma classe existente sem alterar o código de classe existente;

-  Fornecer interfaces para recursos remotos, como serviço da web ou recursos REST;

Vantagens do padrão Proxy

Como vantagens deste padrão temos que :

- Evita a duplicação de objetos grandes e que usam muita memória. E Isso aumenta o desempenho da aplicação através do uso de cache para acessar os objetos mais pesados;

- O Proxy Remoto também garante a segurança instalando o proxy de código local (stub) na máquina do cliente e acessando o servidor com a ajuda do código remoto

- O Proxy Virtual nos ajuda a adiar a criação do Real Subject até que seja totalmente necessário;

- O Proxy Remoto ajuda a fazer o trabalho pesado de empacotar e desembrulhar, fazendo chamadas remotas para o objeto real para o cliente. O cliente não sabe onde reside o Sujeito Real;

- O Proxy Cache ajuda a melhorar o desempenho do aplicativo, fornecendo os recursos acessados com frequência.

- O Proxy de Proteção nos permite adicionar uma camada de segurança adicional para evitar o acesso não autorizado aos recursos;

Desvantagem

Como desvantagens deste padrão temos que :

- Ele introduz outra camada de abstração que às vezes pode ser um problema se o código do RealSubject for acessado por alguns dos clientes diretamente e alguns deles puderem acessar as classes Proxy.  Isso pode causar comportamentos díspares.

- Outra desvantagem é que em certos cenários a sua implementação pode ser complexa;

Aplicação prática do padrão Proxy

Vejamos a seguir um exemplo pratico de implementação do padrão Proxy.

Neste exemplo o objetivo é controlar o acesso a uma pasta compartilhada a usuários a área de TI de uma empresa.

Vamos supor que em uma empresa temos um computador compartilhado que possui uma pasta compartilhada que contem informações confidenciais.

Este computador pode ser acessado pelos usuários que são funcionários da área de TI da empresa. Temos 3 perfis de usuários que acessar o computador compartilhado e podem acessar a pasta compartilhada :

1- Ceo
2- Programador
3- Usuario

Agora, existe uma restrição de acesso onde somente o perfil Ceo pode acessar as informações confidenciais.



Dessa forma teremos que proteger o acesso à pasta compartilhada no computador.  Para fazer isso vamos implementar o padrão Proxy e criar uma pasta Proxy onde iremos verificar o perfil de quem esta acessando o computador. Se o perfil for igual a Ceo então o usuário poderá acessar o conteúdo caso contrario terá o acesso negado.

Levando em conta este cenário e estas considerações vamos implementar o padrão Proxy 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:

1 - IPastaCompartilhada:  Representa o Subject e é uma interface que define os membros que serão implementados pelas classes RealSubject e Proxy para que o Proxy possa ser usado em qualquer lugar em que o RealSubject seja esperado;

2 - PastaCompartilhada:  Representa RealSubject e é uma classe concreta que queremos usar de forma mais eficiente usando a classe proxy.

3- PastaCompartilhadaProxy : Representa o Proxy e é uma classe concreta que contém uma referência à classe RealSubject e pode acessar os membros da classe RealSubject conforme necessário.  Ela deve implementar a mesma interface de RealSubject para que os dois possam ser usados de forma intercambiável.

4- Program - A classe Program representa o Client e usa a implementação definida;

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

A classe Funcionario é definida para ser usada na implementação do padrão e possui o seguinte código:

    public class Funcionario
    {
        public string Nome { get; set; }
        public string Senha { get; set; }
        public string Perfil { get; set; }
        public Funcionario(string nome, string senha, string perfil)
        {
            Nome = nome;
            Senha = senha;
            Perfil = perfil;
        }
    }

1- Interface IPastaCompartilhada (Subject)

   public interface IPastaCompartilhada
    {
        void OperacaoDeLeituraGravacao();
    }

2- Classe PastaCompartilhada (RealSubject)

    public class PastaCompartilhada : IPastaCompartilhada
    {
        public void OperacaoDeLeituraGravacao()
        {
            //define operação a ser realizada na pasta Real
            Console.WriteLine("### Operação de Leitura e Escrita " +
                "na pasta compartilhada ###");
        }
    }

3- Classe PastaCompartilhadaProxy (Proxy)

using System;

namespace Proxy1
{
    //Proxy
    //contém uma referência à classe RealSubject e pode acessar
    //os membros da classe RealSubject conforme necessário.

    public class PastaCompartilhadaProxy : IPastaCompartilhada
    {
        private IPastaCompartilhada pasta;
        private Funcionario funcionario;

        public PastaCompartilhadaProxy(Funcionario funci)
        {
            funcionario = funci;
        }

        public void OperacaoDeLeituraGravacao()
        {
            if (funcionario.Perfil.ToUpper() == "CEO")
            {
                pasta = new PastaCompartilhada();
                Console.WriteLine("O proxy 'Pasta Compartilhada' invoca a pasta Real" +
                    " : 'método usado : OperacaoDeLeituraGravacao()\n");
                pasta.OperacaoDeLeituraGravacao();
            }
            else
            {
                Console.WriteLine("O proxy 'Pasta Compartilhada'  avisa : " +
                    "'Você não tem permissão para acessar esta pasta'\n");
            }
        }
    }
}

4- Program

using System;

namespace Proxy1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("### Exemplo de implementação do padrão Proxy ###\n");

            //acesso Programador
            Console.WriteLine("\nFuncionário com perfil 'Programador' acessando " +
                "a Pasta Compartilhada Proxy");

            Funcionario funci1 = new Funcionario("Macoratti", "123456", "Programador");
            PastaCompartilhadaProxy pastaProxy1 = new PastaCompartilhadaProxy(funci1);
            pastaProxy1.OperacaoDeLeituraGravacao();

            //acesso Usuario
            Console.WriteLine("\nFuncionário com perfil 'Usuario' acessando a Pasta " +
                "Compartilhada Proxy");

            Funcionario funci2 = new Funcionario("Amanda", "123456", "Usuario");
            PastaCompartilhadaProxy pastaProxy2 = new PastaCompartilhadaProxy(funci2);
            pastaProxy2.OperacaoDeLeituraGravacao();

            //acesso Ceo
            Console.WriteLine("\nFuncionário com perfil 'Ceo' acessando a " +
                "Pasta Compartilhada Proxy");

            Funcionario funci3 = new Funcionario("Bill Gates", "876543", "Ceo");
            PastaCompartilhadaProxy pastaProxy3 = new PastaCompartilhadaProxy(funci3);
            pastaProxy3.OperacaoDeLeituraGravacao();

            Console.Read();
        }
    }
}

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

Com isso temos uma implementação do padrão Proxy onde o tipo de proxy usado é o Proxy de Proteção.

Com base no que foi apresentando podemos notar que o padrão Proxy e o padrão Adapter possuem estruturas semelhantes mas finalidades diferentes.  Ambos descrevem como fornecer um nível de indireção a outro objeto, e as implementações mantêm uma referência ao objeto para o qual encaminham solicitações. O Adapter fornece uma interface diferente para seu sujeito. O Proxy fornece a mesma interface.


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

"(Disse Jesus) Eu sou a videira verdadeira, e meu Pai é o agricultor. Todo ramo que, estando em mim, não dá fruto, ele corta; e todo que dá fruto ele poda, para que dê mais fruto ainda. "
Joao 15:1

Referências:


José Carlos Macoratti