O que é SOLID?

O Princípio SOLID é um conjunto de cinco princípios de design de software que foram introduzidos por Robert C. Martin (também conhecido como Uncle Bob) para criar sistemas de software mais flexíveis, sustentáveis e fáceis de manter. Esses princípios são amplamente utilizados na programação orientada a objetos e podem ser aplicados em várias linguagens de programação, incluindo C#.

S

SRP

Single Responsibility Principle

O

OCP

Open/Closed Principle

L

LSP

Liskov Substitution Principle

I

ISP

Interface Segregation Principle

D

DIP

Dependency Inversion Principle

Vamos dar uma olhada em cada um dos princípios SOLID, acompanhados de exemplos práticos em C#:

[S] Princípio da Responsabilidade Única (Single Responsibility Principle - SRP)

Este princípio afirma que uma classe deve ter apenas uma razão para mudar. Em outras palavras, uma classe deve ter uma única responsabilidade e não deve fazer muitas coisas diferentes.

Exemplo em C#:

C#
   public class Customer
   {
       public void AddCustomer()
       {
           // Lógica para adicionar um cliente ao banco de dados
       }

       public void SendEmail()
       {
           // Lógica para enviar um e-mail de boas-vindas ao cliente
       }
   }

Neste exemplo, a classe Customer possui duas responsabilidades diferentes: adicionar um cliente ao banco de dados e enviar um e-mail de boas-vindas. Para seguir o SRP, podemos separar essas responsabilidades em duas classes distintas.

[O] Princípio do Aberto/Fechado (Open/Closed Principle - OCP):

Esse princípio estabelece que as entidades de software (classes, módulos, etc.) devem ser abertas para extensão, mas fechadas para modificação. Ou seja, devemos ser capazes de estender o comportamento de uma classe sem modificar seu código-fonte.

Exemplo em C#:

C#
   public abstract class Shape
   {
       public abstract double CalculateArea();
   }

   public class Circle : Shape
   {
       public double Radius { get; set; }

       public override double CalculateArea()
       {
           return Math.PI * Radius * Radius;
       }
   }

   public class Rectangle : Shape
   {
       public double Width { get; set; }
       public double Height { get; set; }

       public override double CalculateArea()
       {
           return Width * Height;
       }
   }

Neste exemplo, a classe abstrata Shape define um método abstrato CalculateArea(). As classes Circle e Rectangle estendem a classe Shape e implementam o método CalculateArea() de acordo com suas próprias regras de cálculo. Podemos adicionar novas formas geométricas sem modificar o código existente.

[L] Princípio da Substituição de Liskov (Liskov Substitution Principle - LSP):

Esse princípio estabelece que os objetos de uma classe base devem ser substituíveis por instâncias de suas classes derivadas sem alterar a corretude do programa. Em outras palavras, as classes derivadas devem poder ser usadas em qualquer lugar que a classe base seja esperada.

Exemplo em C#:

C#
   public class Rectangle
   {
       public virtual double Width { get; set; }
       public virtual double Height { get; set; }

       public double CalculateArea()
       {
           return Width * Height;
       }
   }

   public class Square : Rectangle
   {
       public override double Width
       {
           get => base.Width;
           set
           {
               base.Width = value;
               base.Height = value;
           }
       }

       public override double Height
       {
           get => base.Height;
           set
           {
               base.Height = value;
               base.Width = value;
           }
       }
   }

Neste exemplo, a classe Square herda da classe Rectangle, mas sobrescreve os setters das propriedades Width e Height para garantir que ambas tenham o mesmo valor. Embora isso pareça correto, a hierarquia viola o LSP, pois podemos ter comportamentos inesperados se tratarmos um objeto Square como um Rectangle, pois as restrições de largura e altura são diferentes.

[I] Princípio da Segregação de Interface (Interface Segregation Principle - ISP):

Esse princípio afirma que uma classe não deve ser forçada a depender de interfaces que ela não utiliza por completo. Em vez disso, as interfaces devem ser segregadas em partes menores e específicas.

Exemplo em C#:

C#
   public interface IShape
   {
       void Draw();
   }

   public interface IEditableShape
   {
       void Resize();
   }

   public class Circle : IShape, IEditableShape
   {
       public void Draw()
       {
           // Lógica para desenhar um círculo
       }

       public void Resize()
       {
           // Lógica para redimensionar um círculo
       }
   }

Neste exemplo, temos duas interfaces segregadas: IShape para desenho e IEditableShape para recursos de edição. A classe Circle implementa ambas as interfaces e fornece implementações adequadas para cada uma delas. Isso permite que os clientes usem apenas as interfaces relevantes para suas necessidades.

[D] Princípio da Inversão de Dependência (Dependency Inversion Principle - DIP):

Esse princípio estabelece que os módulos de alto nível não devem depender diretamente de módulos de baixo nível. Em vez disso, ambos devem depender de abstrações. Além disso, as abstrações não devem depender dos detalhes, mas os detalhes devem depender das abstrações.

Exemplo em C#:

C#
   public interface ILogger
   {
       void Log(string message);
   }

   public class FileLogger : ILogger
   {
       public void Log(string message)
       {
           // Lógica para gravar uma mensagem em um arquivo de log
       }
   }

   public class DatabaseLogger : ILogger
   {
       public void Log(string message)
       {
           // Lógica para gravar uma mensagem em um banco de dados de log
       }
   }

   public class UserManager
   {
       private readonly ILogger _logger;

       public UserManager(ILogger logger)
       {
           _logger = logger;
       }

       public void RegisterUser(string username, string password)
       {
           // Lógica para registrar um novo usuário

           _logger.Log($"Usuário {username} registrado com sucesso.");
       }
   }

Neste exemplo, a classe UserManager depende de uma abstração ILogger, em vez de depender diretamente de classes concretas como FileLogger ou DatabaseLogger. Isso permite que a classe UserManager seja facilmente estendida para suportar diferentes implementações de logging, seguindo o princípio da inversão de dependência.

Lembre-se de que o Princípio SOLID é um conjunto de diretrizes que visam criar um design de software mais flexível e fácil de manter. É importante aplicar esses princípios com base no contexto e nas necessidades específicas do seu projeto.

Espero que este post tenha ajudado você a entender o Princípio SOLID com exemplos em C#. Se você tiver mais dúvidas, sinta-se à vontade para perguntar!

Livros sobre o assunto:

Compartilhe:

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Rolar para cima