SOLID – Guia Completo

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.

Leave a Reply