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:
Aqui estão os participantes do padrão Proxy:
Subject
define a interface comum para ambos
eRealSubject
Proxy
para que você possa substituir o
peloRealSubject
Proxy
.RealSubject
define o objeto real que oProxy
substitui.Proxy
mantém um
objeto para que possa chamar os métodos deRealSubject
quando necessário. Antes ou depois de chamar os métodos doRealSubject
objeto,RealSubject
Proxy
pode 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 reprehenderit
Linguagem de código: texto simples ( texto simples )
Como funciona.
Primeiro, defina a Post
classe que possui as propriedades UserId
, Id
, Title
e 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 IPostAPI
interface 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 IPostAPI
serve como no Subject
diagrama UML acima.
Em seguida, defina o
que implementa a JSONPlaceholderAPI
IPostAPI
interface. O
usa JSONPlaceholderAPI
HttpClient
para 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 JSONPlaceholderAPI
classe serve como o RealSubject
.
Depois disso, defina a JSONPlaceholderAPIProxy
classe que implementa o IPostAPI
. O JSONPlaceholderAPIProxy
atua como um Proxy
objeto do JSONPlaceholderAPI
. Ele armazena em cache um Post
by postId
para que, se você solicitar o mesmo postId
novamente, ele retorne o Post
do 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 JSONPlaceholderAPI
objeto e passe-o para o construtor do JSONPlaceholderAPIProxy
objeto. O programa solicita duas vezes a postagem com id 1 do JSONPlaceholderAPIProxy
objeto. 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.