Os princípios SOLID são um conjunto de cinco diretrizes de design de software orientadas a objetos, criadas por Robert C. Martin (também conhecido como Uncle Bob), que visam tornar o código mais flexível, manutenível e extensível.
O que é SOLID? 🧱 Link para o cabeçalho
O acrônimo SOLID representa os cinco princípios:
-
S - Single Responsibility Principle (Princípio da Responsabilidade Única): Uma classe deve ter apenas uma razão para mudar, ou seja, uma única responsabilidade.
-
O - Open/Closed Principle (Princípio Aberto/Fechado): Entidades de software (classes, módulos, funções, etc.) devem ser abertas para extensão, mas fechadas para modificação.
-
L - Liskov Substitution Principle (Princípio da Substituição de Liskov): Objetos de uma superclasse devem poder ser substituídos por objetos de suas subclasses sem quebrar a aplicação.
-
I - Interface Segregation Principle (Princípio da Segregação de Interfaces): Clientes não devem ser forçados a depender de interfaces que não utilizam. É melhor ter muitas interfaces pequenas e específicas do que uma grande e genérica.
-
D - Dependency Inversion Principle (Princípio da Inversão de Dependência): Módulos de alto nível não devem depender de módulos de baixo nível. Ambos devem depender de abstrações (interfaces). Além disso, abstrações não devem depender de detalhes, mas os detalhes devem depender de abstrações.
Exemplo em PHP: Violando o SOLID (SRP) ❌ Link para o cabeçalho
Para ilustrar, vamos focar no Single Responsibility Principle (SRP), o primeiro e um dos mais importantes princípios. O SRP diz que uma classe deve ter apenas uma responsabilidade.
Considere uma classe ProcessadorDePedido que, além de processar o pedido, também lida com o cálculo do total e o envio de e-mail de notificação.
<?php
class ProcessadorDePedido
{
public function processar(array $itens)
{
$total = $this->calcularTotal($itens);
// Lógica de processamento do pedido...
echo "Pedido processado. Total: R$ " . number_format($total, 2, ',', '.') . "\n";
$this->enviarEmailConfirmacao("cliente@email.com", $total);
}
private function calcularTotal(array $itens): float
{
$total = 0.0;
foreach ($itens as $item) {
$total += $item['preco'] * $item['quantidade'];
}
return $total;
}
private function enviarEmailConfirmacao(string $email, float $total): void
{
// Lógica complexa de envio de e-mail (conexão com servidor, formatação, etc.)
echo "E-mail de confirmação de pedido no valor de R$ " . number_format($total, 2, ',', '.') . " enviado para $email.\n";
}
}
// Uso
$pedido = new ProcessadorDePedido();
$itens = [
['preco' => 100.00, 'quantidade' => 1],
['preco' => 50.00, 'quantidade' => 2],
];
$pedido->processar($itens);
?>
O Problema 😥 Link para o cabeçalho
A classe ProcessadorDePedido viola o SRP porque tem três responsabilidades:
-
Processamento do pedido em si.
-
Cálculo do total do pedido.
-
Envio de e-mail de confirmação.
Se precisarmos mudar a lógica de cálculo (ex: adicionar imposto) ou mudar a forma como os e-mails são enviados (ex: de SMTP para um serviço de terceiros), seremos forçados a modificar a classe ProcessadorDePedido. Isso aumenta o risco de introduzir bugs em outras responsabilidades (como o processamento principal) e torna a classe difícil de testar e manter.
Aplicando o SOLID (SRP) no Exemplo em PHP ✅ Link para o cabeçalho
Para aderir ao SRP, devemos separar as responsabilidades em classes distintas.
1. Separação de Responsabilidades (Refatoração)
Criamos classes para as responsabilidades de cálculo e envio de e-mail:
CalculadoraDePedido
<?php
class CalculadoraDePedido
{
public function calcularTotal(array $itens): float
{
$total = 0.0;
foreach ($itens as $item) {
$total += $item['preco'] * $item['quantidade'];
}
return $total;
}
}
?>
ServicoDeEmail
<?php
class ServicoDeEmail
{
public function enviarConfirmacao(string $email, float $total): void
{
// Lógica REAL de envio de e-mail
echo "E-mail de confirmação de pedido no valor de R$ " . number_format($total, 2, ',', '.') . " enviado para $email. (Usando Serviço de E-mail)\n";
}
}
?>
ProcessadorDePedido(Responsabilidade Única)
A classe principal agora depende de outras classes para realizar suas tarefas auxiliares, focando apenas no processamento do pedido. Usamos o Construtor para injetar as dependências.
<?php
class ProcessadorDePedido
{
private CalculadoraDePedido $calculadora;
private ServicoDeEmail $servicoEmail;
// Injeção de Dependências
public function __construct(CalculadoraDePedido $calculadora, ServicoDeEmail $servicoEmail)
{
$this->calculadora = $calculadora;
$this->servicoEmail = $servicoEmail;
}
public function processar(array $itens, string $emailCliente)
{
// 1. Processamento principal
$total = $this->calculadora->calcularTotal($itens);
// Lógica de processamento do pedido...
echo "Pedido processado. Total: R$ " . number_format($total, 2, ',', '.') . "\n";
// 2. Notificação
$this->servicoEmail->enviarConfirmacao($emailCliente, $total);
}
}
// Uso com boas práticas (SOLID - SRP)
$calculadora = new CalculadoraDePedido();
$servicoEmail = new ServicoDeEmail();
$processador = new ProcessadorDePedido($calculadora, $servicoEmail);
$itens = [
['preco' => 100.00, 'quantidade' => 1],
['preco' => 50.00, 'quantidade' => 2],
];
$processador->processar($itens, "cliente@email.com");
?>
O Benefício 🚀 Link para o cabeçalho
Com a aplicação do SRP, as classes ficam:
-
Mais fáceis de entender: Cada classe faz uma coisa só.
-
Mais fáceis de manter: Uma mudança na lógica de cálculo (ex: impostos) só afeta a classe
CalculadoraDePedido, sem tocar noProcessadorDePedidoouServicoDeEmail. -
Mais fáceis de testar: Podemos testar a lógica de cálculo, a lógica de envio de e-mail e o processamento do pedido de forma isolada, injetando mocks ou stubs nos testes unitários.