Ao desenvolver software, muitas vezes precisamos integrar sistemas legados e adicionar funcionalidades sem modificar diretamente o código existente. Para isso, podemos usar padrÔes de design como Adapter e Decorator, que nos ajudam a estruturar o código de forma modular e reutilizåvel.
Neste artigo, vamos explorar como usar esses padrĂ”es no C# com injeção de dependĂȘncia (DI) e a biblioteca Scrutor para simplificar o registro dos decorators.
đŻ CenĂĄrio do Problema
Imagine que temos um serviço legado que processa pagamentos, mas ele não segue a interface moderna que queremos usar em nosso sistema. Além disso, queremos adicionar logs ao processamento de pagamentos sem modificar diretamente o serviço existente.
Vamos resolver isso combinando Adapter e Decorator!
đ Passo 1: Definindo a Interface PadrĂŁo
Criamos a interface IPaymentProcessor para definir um contrato padrĂŁo para processadores de pagamento:
public interface IPaymentProcessor
{
void ProcessPayment(decimal amount);
}
Essa interface serĂĄ usada tanto pelo Adapter quanto pelo Decorator.
đ Passo 2: Criando o Serviço Legado
Nosso serviço legado (LegacyPaymentService) não segue a interface moderna. Ele espera um string em vez de um decimal:
public sealed class LegacyPaymentService
{
public void MakePayment(string amount)
{
Console.WriteLine($"Processing payment of {amount} via legacy system.");
}
}
Para integrĂĄ-lo ao sistema moderno, usaremos o Adapter Pattern.
đ Passo 3: Criando o Adapter
O Adapter faz a conversĂŁo entre IPaymentProcessor e LegacyPaymentService:
public sealed class PaymentAdapter : IPaymentProcessor
{
private readonly LegacyPaymentService _legacyPaymentService;
public PaymentAdapter(LegacyPaymentService legacyPaymentService)
{
_legacyPaymentService = legacyPaymentService;
}
public void ProcessPayment(decimal amount)
{
string amountString = amount.ToString("F2");
_legacyPaymentService.MakePayment(amountString);
}
}
Agora podemos usar PaymentAdapter em qualquer lugar que espera IPaymentProcessor.
đ Passo 4: Criando o Decorator para Logging
Agora adicionamos um Decorator (LoggingDecorator) para registrar logs sempre que um pagamento for processado:
using Microsoft.Extensions.Logging;
public class LoggingDecorator : IPaymentProcessor
{
private readonly IPaymentProcessor _innerProcessor;
private readonly ILogger<LoggingDecorator> _logger;
public LoggingDecorator(IPaymentProcessor innerProcessor, ILogger<LoggingDecorator> logger)
{
_innerProcessor = innerProcessor;
_logger = logger;
}
public void ProcessPayment(decimal amount)
{
_logger.LogInformation($"Iniciando processamento do pagamento de {amount:C}");
_innerProcessor.ProcessPayment(amount);
_logger.LogInformation($"Pagamento de {amount:C} processado com sucesso.");
}
}
Esse Decorator adiciona logs antes e depois da chamada real ao PaymentProcessor, sem modificar sua implementação.
đ Passo 5: Criando o Worker Service
Para simular um sistema em execução contĂnua, criamos um Worker Service que chama IPaymentProcessor:
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
public class PaymentWorker : BackgroundService
{
private readonly ILogger<PaymentWorker> _logger;
private readonly IPaymentProcessor _paymentProcessor;
public PaymentWorker(ILogger<PaymentWorker> logger, IPaymentProcessor paymentProcessor)
{
_logger = logger;
_paymentProcessor = paymentProcessor;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Iniciando ciclo do Worker...");
_paymentProcessor.ProcessPayment(100.50m); // Simulando pagamento
await Task.Delay(5000, stoppingToken); // Executa a cada 5 segundos
}
}
}
đ Passo 6: Configurando a Injeção de DependĂȘncia (DI) com Scrutor
Podemos usar a biblioteca Scrutor para simplificar o registro do Decorator.
Primeiro, instalamos a dependĂȘncia via NuGet:
dotnet add package Scrutor
Agora, configuramos os serviços no Program.cs:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Scrutor;
class Program
{
static void Main(string[] args)
{
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddSingleton<LegacyPaymentService>(); // Serviço legado
// Registra o Adapter
services.AddSingleton<PaymentAdapter>();
services.AddSingleton<IPaymentProcessor>(provider =>
provider.GetRequiredService<PaymentAdapter>());
// Adiciona o LoggingDecorator automaticamente com Scrutor
services.Decorate<IPaymentProcessor, LoggingDecorator>();
// Registra o Worker Service
services.AddHostedService<PaymentWorker>();
services.AddLogging();
})
.Build();
host.Run();
}
}
đ Estrutura de Pastas
Aqui estĂĄ a estrutura do projeto para manter tudo organizado:
đ PaymentProcessorApp
âââ đ Adapters
â âââ PaymentAdapter.cs
âââ đ Decorators
â âââ LoggingDecorator.cs
âââ đ Interfaces
â âââ IPaymentProcessor.cs
âââ đ Services
â âââ LegacyPaymentService.cs
âââ đ Workers
â âââ PaymentWorker.cs
âââ Program.cs
âââ PaymentProcessorApp.csproj
đ BenefĂcios dessa Abordagem
â
CĂłdigo modular e reutilizĂĄvel â Podemos adicionar novos IPaymentProcessor ou decorators sem alterar a implementação principal.
â
Extensibilidade â Podemos criar mais decorators, como um validador ou auditoria.
â
Baixo acoplamento â Usando DI e Scrutor, evitamos dependĂȘncias rĂgidas.
â
Facilidade de Testes â Podemos testar LoggingDecorator separadamente.
Essa abordagem combina Adapter e Decorator Patterns com Injeção de DependĂȘncia, mantendo o cĂłdigo limpo, flexĂvel e escalĂĄvel.
Se vocĂȘ gostou, deixe um comentĂĄrio ou compartilhe! đđ„
Top comments (0)