Proxy C#

Resumo : neste tutorial, você aprenderá sobre o padrão de design C# Proxy e como usar um objeto proxy para controlar o acesso a um objeto real.

Introdução ao padrão de design do proxy C#

O padrão Proxy é um padrão de design estrutural que permite usar um objeto proxy para controlar o acesso a um objeto real e fornecer funcionalidade adicional sem modificar o objeto real.

Por exemplo, o objeto proxy pode adicionar cache, registro ou serviço de autorização ao objeto real. Em alguns casos, a criação do objeto real é cara, o objeto proxy pode ajudar a melhorar o desempenho atrasando a criação do objeto real até que seja realmente necessário.

O diagrama UML a seguir ilustra o padrão de design do Proxy:

Proxy C#

Aqui estão os participantes do padrão Proxy:

  • Subjectdefine a interface comum para ambos RealSubjecte Proxypara que você possa substituir o RealSubjectpelo Proxy.
  • RealSubjectdefine o objeto real que o Proxysubstitui.
  • Proxymantém um RealSubjectobjeto para que possa chamar os métodos de RealSubjectquando necessário. Antes ou depois de chamar os métodos do RealSubjectobjeto, Proxypode adicionar mais funcionalidades, como cache ou registro.

Exemplo de padrão de proxy C#

Este programa C# demonstra como usar o padrão Proxy para armazenar solicitações de API em cache usando HttpClient:

using System.Text.Json;

namespace ProxyPattern;

public class Post
{
    public int UserId
    {
        get; set;
    }
    public int Id
    {
        get; set;
    }
    public string? Title
    {
        get; set;
    }
    public string? Body
    {
        get; set;
    }

    public override string ToString() => $"<{Id}> - {Title}";
}

public interface IPostAPI
{
    Task<Post?> GetPost(int postId);
}

public class JSONPlaceholderAPI : IPostAPI
{
    private readonly HttpClient _httpClient;

    public JSONPlaceholderAPI()
    {
        _httpClient = new HttpClient();
    }

    public async Task<Post?> GetPost(int postId)
    {
        var response = await _httpClient.GetAsync($"https://jsonplaceholder.typicode.com/posts/{postId}");

        if (!response.IsSuccessStatusCode)
        {
            throw new Exception($"Failed to retrieve post {postId} from API: {response.ReasonPhrase}");
        }

        var json = await response.Content.ReadAsStringAsync();

        var options = new JsonSerializerOptions()
        {
            PropertyNameCaseInsensitive = true
        };

        var post = JsonSerializer.Deserialize<Post?>(json, options);
        return post;
    }
}

public class JSONPlaceholderAPIProxy : IPostAPI
{
    private readonly IPostAPI _postAPI;
    private readonly Dictionary<int, Post> _postCache;

    public JSONPlaceholderAPIProxy(IPostAPI postAPI)
    {
        _postAPI = postAPI;
        _postCache = new Dictionary<int, Post>();
    }

    public async Task<Post?> GetPost(int postId)
    {
        if (_postCache.ContainsKey(postId))
        {
            Console.WriteLine($"Retrieving post {postId} from cache.");
            return _postCache[postId];
        }

        var post = await _postAPI.GetPost(postId);
        _postCache[postId] = post;
        return post;
    }
}


public static class Program
{
    public static async Task Main()
    {
        var api = new JSONPlaceholderAPI();
        var apiProxy = new JSONPlaceholderAPIProxy(api);

        // Get post 1
        var post1 = await apiProxy.GetPost(1);
        Console.WriteLine($"Post 1: {post1}");

        // Get post 1 again - should retrieve from cache
        var post1Cached = await apiProxy.GetPost(1);
        Console.WriteLine($"Post 1 (cached): {post1Cached}");
    }
}Linguagem de código:  C#  ( cs )

Saída:

Post 1: <1> - sunt aut facere repellat provident occaecati excepturi optio reprehenderit
Retrieving post 1 from cache.
Post 1 (cached): <1> - sunt aut facere repellat provident occaecati excepturi optio reprehenderitLinguagem de código:  texto simples  ( texto simples )

Como funciona.

Primeiro, defina a Postclasse que possui as propriedades UserId, Id, Titlee Body. O ToString()método retorna a representação em string de uma postagem:

public class Post
{
    public int UserId
    {
        get; set;
    }
    public int Id
    {
        get; set;
    }
    public string? Title
    {
        get; set;
    }
    public string? Body
    {
        get; set;
    }

    public override string ToString() => $"<{Id}> - {Title}";
}Linguagem de código:  C#  ( cs )

A seguir, defina a IPostAPIinterface que possui um GetPost()método que retorna um Post by PostId:

public interface IPostAPI
{
    Task<Post?> GetPost(int postId);
}Linguagem de código:  C#  ( cs )

O IPostAPIserve como no Subjectdiagrama UML acima.

Em seguida, defina o JSONPlaceholderAPIque implementa a IPostAPIinterface. O JSONPlaceholderAPIusa HttpClientpara chamar a API de https://jsonplaceholder.typicode.com/posts/1/, onde 1 é o ID da postagem:

public class JSONPlaceholderAPI : IPostAPI
{
    private readonly HttpClient _httpClient;

    public JSONPlaceholderAPI()
    {
        _httpClient = new HttpClient();
    }

    public async Task<Post?> GetPost(int postId)
    {
        var response = await _httpClient.GetAsync($"https://jsonplaceholder.typicode.com/posts/{postId}");

        if (!response.IsSuccessStatusCode)
        {
            throw new Exception($"Failed to retrieve post {postId} from API: {response.ReasonPhrase}");
        }

        var json = await response.Content.ReadAsStringAsync();

        var options = new JsonSerializerOptions()
        {
            PropertyNameCaseInsensitive = true
        };

        var post = JsonSerializer.Deserialize<Post?>(json, options);
        return post;
    }
}Linguagem de código:  C#  ( cs )

A JSONPlaceholderAPIclasse serve como o RealSubject.

Depois disso, defina a JSONPlaceholderAPIProxyclasse que implementa o IPostAPI. O JSONPlaceholderAPIProxyatua como um Proxyobjeto do JSONPlaceholderAPI. Ele armazena em cache um Postby postIdpara que, se você solicitar o mesmo postId novamente, ele retorne o Postdo cache em vez de chamar a API:

public class JSONPlaceholderAPIProxy : IPostAPI
{
    private readonly IPostAPI _postAPI;
    private readonly Dictionary<int, Post> _postCache;

    public JSONPlaceholderAPIProxy(IPostAPI postAPI)
    {
        _postAPI = postAPI;
        _postCache = new Dictionary<int, Post>();
    }

    public async Task<Post?> GetPost(int postId)
    {
        if (_postCache.ContainsKey(postId))
        {
            Console.WriteLine($"Retrieving post {postId} from cache.");
            return _postCache[postId];
        }

        var post = await _postAPI.GetPost(postId);
        _postCache[postId] = post;
        return post;
    }
}Linguagem de código:  C#  ( cs )

Por fim, crie o JSONPlaceholderAPIobjeto e passe-o para o construtor do JSONPlaceholderAPIProxyobjeto. O programa solicita duas vezes a postagem com id 1 do JSONPlaceholderAPIProxyobjeto. Por causa do cache, a segunda solicitação não chama a API, mas retorna a postagem do cache.

public static class Program
{
    public static async Task Main()
    {
        var api = new JSONPlaceholderAPI();
        var apiProxy = new JSONPlaceholderAPIProxy(api);

        // Get post 1
        var post1 = await apiProxy.GetPost(1);
        Console.WriteLine($"Post 1: {post1}");

        // Get post 1 again - should retrieve from cache
        var post1Cached = await apiProxy.GetPost(1);
        Console.WriteLine($"Post 1 (cached): {post1Cached}");
    }
}Linguagem de código:  C#  ( cs )

Resumo

  • Use o padrão de design Proxy para controlar o acesso a um objeto real e estender sua funcionalidade sem modificar diretamente o objeto real.

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *