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)