DEV Community

Clóvis Chakrian
Clóvis Chakrian

Posted on

🚀 SOLID: O Guia Prático para Código Manutenível

Se você já trabalhou em projetos que pareciam fáceis no começo, mas com o tempo viraram um pesadelo de manutenção, provavelmente sofreu com código rígido, acoplado e difícil de escalar.

Os princípios SOLID surgiram exatamente para evitar esse problema. Eles ajudam a escrever um código mais organizado, flexível e fácil de manter. Vamos ver, de forma objetiva e com exemplos práticos, como aplicá-los no dia a dia!


📌 S - Single Responsibility Principle (SRP)

"Uma classe deve ter apenas uma razão para mudar."

Imagine uma classe que faz tudo: processa pedidos, gera nota fiscal e envia e-mails.

public class PedidoService
{
    public void ProcessarPedido() { /* lógica */ }
    public void GerarNotaFiscal() { /* lógica */ }
    public void EnviarEmail() { /* lógica */ }
}
Enter fullscreen mode Exit fullscreen mode

❌ O problema? Sempre que precisar mudar a nota fiscal, pode acabar impactando o envio de e-mails.

Solução: Separar responsabilidades:

public class PedidoProcessor { /* Processa o pedido */ }
public class NotaFiscalService { /* Gera nota fiscal */ }
public class EmailService { /* Envia e-mails */ }
Enter fullscreen mode Exit fullscreen mode

Agora, cada classe tem apenas uma responsabilidade, facilitando a manutenção.


📌 O - Open/Closed Principle (OCP)

"O código deve estar aberto para extensão, mas fechado para modificação."

Imagine um sistema que calcula descontos para diferentes clientes:

public class Pedido
{
    public decimal CalcularDesconto(string tipoCliente)
    {
        if (tipoCliente == "Premium") return 0.2m;
        if (tipoCliente == "Regular") return 0.1m;
        return 0m;
    }
}
Enter fullscreen mode Exit fullscreen mode

❌ O problema? Se surgir um novo tipo de cliente, precisamos modificar essa classe, quebrando o OCP.

Solução: Criar uma estrutura extensível:

public interface IDesconto { decimal Aplicar(); }

public class DescontoPremium : IDesconto { public decimal Aplicar() => 0.2m; }
public class DescontoRegular : IDesconto { public decimal Aplicar() => 0.1m; }

public class Pedido
{
    private readonly IDesconto _desconto;
    public Pedido(IDesconto desconto) { _desconto = desconto; }
    public decimal CalcularDesconto() => _desconto.Aplicar();
}

Enter fullscreen mode Exit fullscreen mode

Agora, podemos adicionar novos descontos sem mexer no código existente.


📌 L - Liskov Substitution Principle (LSP)

"Objetos de uma subclasse devem poder substituir objetos da superclasse sem quebrar o código."

Imagine que temos uma classe Ave que define um comportamento comum para todas as aves:

public class Ave
{
    public virtual void Voar()
    {
        Console.WriteLine("A ave está voando!");
    }
}
Enter fullscreen mode Exit fullscreen mode

Agora, queremos adicionar um pinguim ao nosso código, mas ele não pode voar! Se criarmos uma subclasse Pinguim e sobrescrevermos Voar(), teremos um problema:

public class Pinguim : Ave
{
    public override void Voar()
    {
        throw new NotImplementedException("Pinguins não voam!");
    }
}
Enter fullscreen mode Exit fullscreen mode

O problema? Se um código espera receber uma Ave, mas recebe um Pinguim, pode acabar chamando Voar() e quebrando a aplicação.

Solução: Precisamos reorganizar a hierarquia e evitar forçar um comportamento que nem todas as subclasses podem ter.

public abstract class Ave { }

public interface IVoavel
{
    void Voar();
}

public class Andorinha : Ave, IVoavel
{
    public void Voar() => Console.WriteLine("A andorinha está voando!");
}

public class Pinguim : Ave
{
    public void Nadar() => Console.WriteLine("O pinguim está nadando!");
}
Enter fullscreen mode Exit fullscreen mode

Agora, garantimos que somente as aves que realmente voam implementem IVoavel, e evitamos problemas de substituição.


📌 I - Interface Segregation Principle (ISP)

"Uma interface não deve forçar uma classe a implementar métodos que ela não usa."

Suponha que temos uma interface IAcaoFuncionario que obriga todas as classes a implementarem métodos desnecessários:

public interface IAcaoFuncionario
{
    void Trabalhar();
    void Gerenciar();
}

public class Desenvolvedor : IAcaoFuncionario
{
    public void Trabalhar() { /* lógica */ }
    public void Gerenciar() { throw new NotImplementedException(); }

Enter fullscreen mode Exit fullscreen mode

❌ O problema? Um desenvolvedor não gerencia, mas é obrigado a implementar o método.

Solução: Criar interfaces menores e específicas:

public interface ITrabalho { void Trabalhar(); }
public interface IGerencia { void Gerenciar(); }

public class Desenvolvedor : ITrabalho
{
    public void Trabalhar() { /* lógica */ }
}
Enter fullscreen mode Exit fullscreen mode

Agora, cada classe implementa apenas o que precisa.


📌 D - Dependency Inversion Principle (DIP)

"Módulos de alto nível não devem depender de módulos de baixo nível, mas sim de abstrações."

Suponha que um serviço de pedidos dependa diretamente de um serviço de pagamento:

public class PedidoService
{
    private readonly PagamentoService _pagamentoService = new PagamentoService();

    public void ProcessarPedido() { _pagamentoService.RealizarPagamento(); }
}
Enter fullscreen mode Exit fullscreen mode

❌ O problema? Se PagamentoService mudar, PedidoService pode quebrar.

Solução: Usar injeção de dependência e abstrações:

public interface IPagamentoService { void RealizarPagamento(); }

public class PedidoService
{
    private readonly IPagamentoService _pagamentoService;

    public PedidoService(IPagamentoService pagamentoService)
    {
        _pagamentoService = pagamentoService;
    }

    public void ProcessarPedido() { _pagamentoService.RealizarPagamento(); }
}
Enter fullscreen mode Exit fullscreen mode

Agora, podemos trocar a implementação de pagamento sem alterar PedidoService.


🎯 Conclusão

Os princípios SOLID não são apenas teoria — eles ajudam a criar código mais flexível e de fácil manutenção. Mas lembre-se: nem tudo precisa ser superabstraído. O segredo é aplicar SOLID com equilíbrio, garantindo um código limpo sem exageros.

E você, já enfrentou código que ignorava SOLID? Comenta aqui sua experiência! 🚀

AWS GenAI LIVE image

Real challenges. Real solutions. Real talk.

From technical discussions to philosophical debates, AWS and AWS Partners examine the impact and evolution of gen AI.

Learn more

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

DEV is better (more customized, reading settings like dark mode etc) when you're signed in!

Okay