SOLID é um acrônimo para descrever alguns dos princípios apresentados por Robert C Martin (também conhecido como Uncle Bob) para sistemas desenvolvidos através do paradigma orientado a objetos (POO).
Tais princípios formam a base para um código bem estruturado, o qual permitiria uma manutenção mais fácil e facilitaria o desenvolvimento de novas funcionalidades e sua refatoração.
Estes princípios buscam apresentar como um desenvolvedor pode tirar um bom proveito dos recursos do POO, evitando o aumento da complexidade do sistema e promovendo a reusabilidade do código.
Single Responsability Principal
O Princípio da Responsabilidade Única consiste em atribuir apenas uma responsabilidade para uma classe, promovendo maior segmentação do sistema e facilidade para identificar a localização de determinado trecho de código. Além do mais, concentrar muito código em uma classe resulta em um código extremamente acoplado e dependente, dificultando a manutenção e aumentando a possibilidade de introduzir bugs.
Tal cenário também dificulta o planejamento e desenvolvimento de testes para o sistema.
Um software que não está de acordo com este princípio, apresenta em seu código fonte uma ou mais “God Classes“, a qual se trata de um anti-pattern que descreve uma classe com inúmeras responsabilidades.
Open-Closed Principle
O Princípio Aberto-Fechado consiste em descrever um código que esteja Aberto para extensões e fechado para alterações. Em outras palavras, para o desenvolvimento de novas funcionalidades, devemos sempre incrementar através de novas classes, e não alterar as superclasses.
Isso implica em um software com um nível de abstração bem estruturado e capaz de utilizar de forma eficiente os recursos de OOP tais como : herança, interfaces, polimorfismo e encapsulamento.
Exemplo :
// Classe original alterada. Não segue o princípio
// Antes
class Empregado {
private $salario;
public function calcularSalario()
{
return $this->salario;
}
}
// Depois
class Empregado {
private $salario;
private $comissao;
public function calcularSalario($vendas)
{
return $this->salario + ($this->comissao * $vendas);
}
}
// Classe original preservada e extendida, está seguindo o princípio
class Empregado {
protected $salario;
public function calcularSalario()
{
return $this->salario;
}
}
class EmpregadoComissionado extends Empregado {
private $comissao;
public function calcularSalarioComComissao($vendas)
{
return parent::calcularSalario() + ($this->comissao * $vendas);
}
// Neste caso, além de facilitar a manutenção, o método original está sendo preservado e não sofre alterações diretas
}
Liskov Substitution Principle
O princípio da substituição de Liskov, implica que uma classe A a qual possui como subclasse a classe B, pode ser “substituída” pela classe B. Em outras palavras : uma subclasse deve ser compatível com os casos de uso da superclasse.
Exemplo :
class Empregado {
protected $salario;
protected $vendas;
public function calcularSalario()
{
return $this->salario;
}
}
class EmpregadoComissionado extends Empregado {
private $comissao = 0.1;
public function calcularSalario()
{
return parent::calcularSalario() + ($this->comissao * $this->vendas);
}
}
function getEmpregadoSalario(Empregado $empregado){
return $empregado->calcularSalario();
}
$empregado = new Empregado();
$empregadoComissionado = new EmpregadoComissionado();
echo getEmpregadoSalario($empregado);
echo getEmpregadoSalario($empregadoComissionado);
Nesse caso acima, a classe EmpregadoComissionado herda de Empregado, e o uso de Empregado pode ser substituído por EmpregadoComissionado.
O princípio implica em inúmeros fatores, tais como visibilidade e assinatura de métodos, lançamento e tratativa de exceções, retornar valores de tipos diferentes de dados. Todos estes devem ser tratados para que o desenvolvedor garanta a conformidade com o princípio.
Este princípio garante a integridade do sistema e diminuí a possibilidade de surgir novos bugs inesperados.
Interface Segregation Principle
O Princípio da Segregação de Interface implica que uma classe não deve implementar uma interface a qual apresenta métodos que não serão utilizados.
Diferente do que por vezes ocorre, implementar uma interface e apenas apresentar a assinatura seguido pelo corpo vazio de alguns de seus métodos, não é o ideal e deve ser evitado ao máximo.
Para garantir que este erro não ocorra, é necessário que o desenvolvedor implemente interfaces mais específicas segmentando melhor cada comportamento para cada caso de uso e regra de negócio.
Dependency Inversion Principle
O princípio da inversão de dependência consiste em passar objetos externos como parâmetros para uma classe a qual dependa deles, através de uma abstração, evitando que a classe dependente mantenha alto acoplamento com relação à este objeto.
Suponha que eu tenha a classe Pedido e essa classe instancia a classe CartaoDeCredito no interior de um dos seus métodos, o ideal seria que a classe Pedido recebesse uma instância de uma abstração MeioDePagamento permitindo que Pedido não tenha uma dependência restrita a CartaoDeCredito.
// Errado, não está seguindo o princípio de inversão de dependência
class CartaoDeCredito{
public function pagar()
{
return pagar('endpoint_do_gateway_de_pagamento');
}
}
class Pedido {
public function fecharPedido()
{
$cartaoDeCredito = new CartaoDeCredito();
$cartaoDeCredito->pagar();
}
}
// Correto, agora está em conformidade com o princípio
abstract class MeioDePagamento{
abstract function pagar();
}
class CartaoDeCredito extends MeioDePagamento
{
public function pagar()
{
return pagar('endpoint_do_gateway_de_pagamento');
}
}
class Pedido {
private $meioDePagamento;
public function __construct(MeioDePagamento $meioDePagamento)
{
$this->meioDePagamento = $meioDePagamento;
}
public function fecharPedido()
{
$this->meioDePagamento->pagar();
}
Isso promove a independência da classe Pedido, uma vez que é possível utilizar qualquer instância da abstração MeioDePagamento, também facilitando o desenvolvimento de novas funcionalidades. A classe Pedido mantem contato apenas com uma abstração, e não possui responsabilidades ou mesmo conhecimento sobre detalhes de implementação.