Padrões de Design Avançados em C#: da Teoria à Prática
Padrões de Design (Design Patterns) são soluções comprovadas para problemas típicos de arquitetura de software. No nível iniciante, os desenvolvedores conhecem Singleton, Factory ou Adapter. No entanto, para criar sistemas flexíveis, sustentáveis e escaláveis na plataforma .NET, é necessário dominar padrões comportamentais e estruturais mais complexos. Neste artigo, abordaremos três padrões avançados: Chain of Responsibility, State e Visitor. Cada padrão será ilustrado com um exemplo prático em C# usando recursos modernos da linguagem (records, pattern matching, generics).
1. Chain of Responsibility (Cadeia de Responsabilidade)
O padrão Chain of Responsibility permite passar uma solicitação sequencialmente por uma cadeia de manipuladores até que um deles a processe. Isso elimina o acoplamento rígido entre o remetente e o destinatário. Em C#, é frequentemente usado para middleware no ASP.NET Core ou para validação de dados.
Exemplo: sistema de verificação de permissões de acesso
Suponha que temos uma solicitação para executar uma operação. Cada manipulador verifica uma condição (autenticação, autorização, limites).
// Classe base do manipuladorpublic abstract class Handler{ protected Handler? NextHandler;
public void SetNext(Handler handler) => NextHandler = handler;
public abstract void Handle(RequestContext context);}
public class AuthenticationHandler : Handler{ public override void Handle(RequestContext context) { if (context.IsAuthenticated) { Console.WriteLine("Autenticação bem-sucedida."); NextHandler?.Handle(context); } else { Console.WriteLine("Erro: usuário não autenticado."); } }}
public class AuthorizationHandler : Handler{ public override void Handle(RequestContext context) { if (context.Role == "Admin") { Console.WriteLine("Autorização bem-sucedida."); NextHandler?.Handle(context); } else { Console.WriteLine("Erro: permissões insuficientes."); } }}
// Contexto da solicitaçãopublic record RequestContext(bool IsAuthenticated, string Role);
// Usovar authHandler = new AuthenticationHandler();var authzHandler = new AuthorizationHandler();authHandler.SetNext(authzHandler);
var context = new RequestContext(true, "Admin");authHandler.Handle(context);// Saída:// Autenticação bem-sucedida.// Autorização bem-sucedida.Este padrão é ideal para criar pipelines de processamento, onde cada etapa pode interromper a execução ou passar o controle adiante.
2. State (Estado)
O padrão State permite que um objeto altere seu comportamento quando seu estado interno muda. Externamente, parece que o objeto muda de classe. Em C#, é conveniente implementá-lo por meio de interfaces e composição.
Exemplo: gerenciamento de pedidos em uma loja online
Um pedido pode estar nos estados: Novo, Pago, Enviado, Entregue. Cada estado define quais ações são permitidas.
// Interface de estadopublic interface IOrderState{ void Pay(Order order); void Ship(Order order); void Deliver(Order order);}
// Estados concretospublic class NewOrderState : IOrderState{ public void Pay(Order order) { Console.WriteLine("Pedido pago."); order.State = new PaidOrderState(); }
public void Ship(Order order) => Console.WriteLine("Erro: pedido ainda não foi pago."); public void Deliver(Order order) => Console.WriteLine("Erro: pedido ainda não foi enviado.");}
public class PaidOrderState : IOrde
```