Патерн проектування Proxy є структурним патерном, який широко використовується в розробці програмного забезпечення для надання замінника, що контролює доступ до іншого об'єкта, забезпечуючи інкапсуляцію, безпеку та ефективність. У цій статті ми розглянемо, як реалізувати, вдосконалювати та застосовувати передові практики для патерну Proxy, використовуючи сучасні можливості C# .NET 8.
Що таке патерн Proxy?
Proxy є структурним патерном, який виступає як посередник між клієнтом та реальним об'єктом. Він корисний у таких сценаріях, як:
- Контроль доступу: Захист критичних операцій від несанкціонованих користувачів.
- Кешування: Збереження результатів для зменшення навантаження при повторюваних операціях.
- Устойчивість: Управління збоїв у розподілених системах з повторними спробами.
- Лінива ініціалізація: Створення об'єктів тільки тоді, коли це необхідно.
Основна реалізація Proxy
Розглянемо базову реалізацію для контролю доступу до критичної служби.
public interface IService
{
void PerformOperation();
}
public class CriticalService : IService
{
public void PerformOperation()
{
Console.WriteLine("Критична операція виконана.");
}
}
public class ProxyService : IService
{
private readonly CriticalService _realService;
private readonly string _userRole;
public ProxyService(string userRole)
{
_realService = new CriticalService();
_userRole = userRole;
}
public void PerformOperation()
{
if (_userRole == "Admin")
{
_realService.PerformOperation();
}
else
{
Console.WriteLine("Доступ заборонено! У вас немає дозволу на виконання цієї операції.");
}
}
}
Рефакторинг для передових сценаріїв
1. Модульна конфігурація
Додайте гнучкість, дозволяючи або вимикаючи поведінки, як кешування та логування, динамічно.
public class ConfigurableProxy : IService
{
private readonly IService _realService;
private readonly bool _enableLogging;
private readonly bool _enableCaching;
private readonly Dictionary _cache = new();
public ConfigurableProxy(IService realService, bool enableLogging, bool enableCaching)
{
_realService = realService;
_enableLogging = enableLogging;
_enableCaching = enableCaching;
}
public void PerformOperation()
{
if (_enableCaching && _cache.ContainsKey("operation_result"))
{
if (_enableLogging)
Console.WriteLine("Кешування: Результат знайдений у кеші.");
Console.WriteLine(_cache["operation_result"]);
return;
}
if (_enableLogging)
Console.WriteLine("Виконання операції...");
_realService.PerformOperation();
if (_enableCaching)
{
_cache["operation_result"] = "Результат критичної операції";
if (_enableLogging)
Console.WriteLine("Результат збережений у кеші.");
}
}
}
2. Динамічні Proxy з AOP
Використовуйте Castle.DynamicProxy для додавання поперечних турбот, таких як логування та валідація, динамічно.
using Castle.DynamicProxy;
public class LoggingInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine($"Початок методу: {invocation.Method.Name}");
invocation.Proceed();
Console.WriteLine($"Метод завершено: {invocation.Method.Name}");
}
}
public static class ProxyGeneratorFactory
{
private static readonly ProxyGenerator Generator = new();
public static T CreateProxy(T target) where T : class
{
return Generator.CreateInterfaceProxyWithTarget(target, new LoggingInterceptor());
}
}
Використання:
var criticalService = new CriticalService();
var proxy = ProxyGeneratorFactory.CreateProxy(criticalService);
proxy.PerformOperation();
Висновок
Патерн Proxy є потужним інструментом для реалізації доступу до критичних операцій, зберігання результатів і забезпечення безпеки в розподілених системах. Використання цього патерну дозволяє створювати чистий та ефективний код, який легко адаптується до різноманітних сценаріїв і потреб.
Устойчивість за допомогою Polly
Автоматичне оброблення тимчасових збоїв за допомогою політик повтору з Polly.
using Polly;
public class ResilientProxy : IService
{
private readonly IService _realService;
private readonly RetryPolicy _retryPolicy;
public ResilientProxy(IService realService)
{
_realService = realService;
_retryPolicy = Policy
.Handle()
.Retry(3, (exception, retryCount) =>
{
Console.WriteLine($"Повторна спроба {retryCount}: {exception.Message}");
});
}
public void PerformOperation()
{
_retryPolicy.Execute(() => _realService.PerformOperation());
}
}
4. Інтеграція з JWT
Модернізуйте аутентифікацію за допомогою JSON Web Tokens (JWT).
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
public class JwtAuthorizationService
{
public bool IsAuthorized(string token, string requiredRole)
{
var handler = new JwtSecurityTokenHandler();
var jwtToken = handler.ReadJwtToken(token);
var roleClaim = jwtToken.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Role)?.Value;
return roleClaim == requiredRole;
}
}
Інтегрований Proxy:
public class JwtProxyService : IService
{
private readonly IService _realService;
private readonly JwtAuthorizationService _authService;
private readonly string _token;
public JwtProxyService(IService realService, JwtAuthorizationService authService, string token)
{
_realService = realService;
_authService = authService;
_token = token;
}
public void PerformOperation()
{
if (_authService.IsAuthorized(_token, "Admin"))
{
_realService.PerformOperation();
}
else
{
Console.WriteLine("Доступ заборонено! Невірний токен або недостатньо прав.");
}
}
}
5. Спостережуваність з OpenTelemetry
Додайте детальне моніторинг і трасування для розподілених систем.
using OpenTelemetry;
using OpenTelemetry.Trace;
public class ObservabilityProxy : IService
{
private readonly IService _realService;
private readonly Tracer _tracer;
public ObservabilityProxy(IService realService, Tracer tracer)
{
_realService = realService;
_tracer = tracer;
}
public void PerformOperation()
{
using var span = _tracer.StartActiveSpan("PerformOperation");
try
{
_realService.PerformOperation();
span.SetStatus(Status.Ok);
}
catch (Exception ex)
{
span.SetStatus(Status.Error.WithDescription(ex.Message));
throw;
}
}
}
Тестування Proxy
Використовуйте xUnit та Moq для перевірки поведінки Proxy.
public class ProxyTests
{
[Fact]
public void LoggingProxy_ShouldLogMessages()
{
var mockService = new Mock();
var proxy = new LoggingProxy(mockService.Object);
proxy.PerformOperation();
mockService.Verify(service => service.PerformOperation(), Times.Once);
}
}
Висновок
Патерн Proxy, в поєднанні з сучасними інструментами, такими як Polly, OpenTelemetry та JWT, може перетворити вашу архітектуру на щось безпечне, стійке та легко спостережуване.
Практики, продемонстровані в цій статті, показують, як удосконалити патерн Proxy для задоволення вимог складних, розподілених систем, зберігаючи при цьому простоту та модульність.
Якщо ця стаття була корисною, поділіться нею зі своїми колегами, щоб допомогти іншим розробникам досліджувати можливості патерну Proxy в C# .NET 8! 🚀
Перекладено з: Implementing and Refining the Proxy Pattern in C# .NET 8