Padrões de Projeto em C# com Exemplos: do Início ao Fim
Padrões de projeto (design patterns) são soluções comprovadas para problemas típicos que surgem no desenvolvimento de software. Eles não são código pronto, mas sim templates que podem ser adaptados a uma situação específica. Para um desenvolvedor C#, conhecer padrões não é apenas um diferencial no currículo, mas uma necessidade para criar uma arquitetura flexível, sustentável e escalável.
Neste artigo, abordaremos os três principais grupos de padrões segundo a classificação GoF (Gang of Four): criacionais, estruturais e comportamentais. Cada padrão será analisado com um exemplo prático em C# (.NET).
Padrões Criacionais (Creational Patterns)
Esses padrões gerenciam o processo de criação de objetos, tornando o sistema independente da forma como eles são instanciados. Vamos ver dois dos mais populares.
Singleton (Odiador)
Garante que uma classe tenha apenas uma instância e fornece um ponto de acesso global a ela. Em C#, para operação segura em ambiente multithread, utiliza-se Lazy<T>.
public sealed class Logger{ private static readonly Lazy<Logger> _instance = new Lazy<Logger>(() => new Logger());
public static Logger Instance => _instance.Value;
private Logger() { }
public void Log(string message) { Console.WriteLine($"[{DateTime.Now}] {message}"); }}
// UsoLogger.Instance.Log("Aplicação iniciada");Factory Method (Método Fábrica)
Define uma interface para criar um objeto, mas permite que as subclasses alterem o tipo do objeto criado. É ideal para situações em que o tipo do objeto não é conhecido antecipadamente.
public interface ITransport{ void Deliver();}
public class Truck : ITransport{ public void Deliver() => Console.WriteLine("Entrega por terra");}
public class Ship : ITransport{ public void Deliver() => Console.WriteLine("Entrega por mar");}
public abstract class Logistics{ public abstract ITransport CreateTransport();
public void PlanDelivery() { var transport = CreateTransport(); transport.Deliver(); }}
public class RoadLogistics : Logistics{ public override ITransport CreateTransport() => new Truck();}
// UsoLogistics logistics = new RoadLogistics();logistics.PlanDelivery(); // Saída: Entrega por terraPadrões Estruturais (Structural Patterns)
Eles definem maneiras de construir relações entre objetos, garantindo flexibilidade e eficiência na arquitetura.
Adapter (Adaptador)
Permite que objetos com interfaces incompatíveis trabalhem juntos. É frequentemente usado na integração com código legado ou bibliotecas de terceiros.
// Interface alvo esperada pelo clientepublic interface IModernPrinter{ void Print(string text);}
// Classe antiga com interface incompatívelpublic class OldPrinter{ public void PrintOldStyle(string message) { Console.WriteLine($"Impressora antiga: {message}"); }}
// Adaptadorpublic class PrinterAdapter : IModernPrinter{ private readonly OldPrinter _oldPrinter;
public PrinterAdapter(OldPrinter oldPrinter) { _oldPrinter = oldPrinter; }
public void Print(string text) { _oldPrinter.PrintOldStyle(text); }}
// UsoIModernPrinter printer = new PrinterAdapter(new OldPrinter());printer.Print("Olá, mundo!"); // Saída: Impressora antiga: Olá, mundo!Decorator (Decorador)
Adiciona dinamicamente novas responsabilidades a um objeto sem alterar seu código. É uma alternativa flexível à herança.
public interface ICoffee{ double GetCost(); string GetDescription();}
public class SimpleCoffee : ICoffee{