SOLID e o princípio do desacoplamento
E aí galera, tudo beleza? Espero que sim, chegamos ao último tópico sobre SOLID. E se você ainda não leu os artigos anteriores sobre o tema, já deixo aqui os links para que você possa aproveitar:
- SOLID e o princípio do não faça mais do que não deve
- SOLID e o princípio do faça algo, mas do seu jeito
- SOLID e o princípio do 6 por meia dúzia
- SOLID e o princípio da implementação desnecessária
Passado todos os artigos anteriores, vamos estudar sobre o último princípio SOLID, estou falando do Dependency Inversion Principle (DIP), em português: Princípio da Inversão de Dependência.
Problema
Seguindo o nosso bom e velho roteiro, vamos ver uma historinha para entender como esse princípio nos ajuda.
Digamos que somos donos de uma montadora de veículos e temos um contrato com uma empresa de fabricação de farol. Nesse contrato constam as informações como o tamanho e a qualidade do material que esperamos receber. Então a gente faz o pedido da quantidade que queremos, a empresa de farol fabrica, e nos envia para que possamos instalar no veículo, até aqui tudo bem, certo?
Agora iremos nos atentar às informações presentes no contrato. Se notarmos atentamente, em nenhum momento informamos o formato do farol. Imaginem que nós estivéssemos esperando faróis circulares, e nos fossem mandados faróis quadrados, ou em outro formato, teríamos que redesenhar o veículo ou descartar o material recebido. Ficaríamos no prejuízo. um grande prejuízo. absurdo. Imaginem o prejuízo que teríamos.
A história acima pode ser um pouco absurda, mas que pode acontecer bastante em nosso código. Agora eu gostaria que observassem atentamente o código abaixo:
<?php
class Payment
{
public function pay(Employee $employee)
{
$client = new GuzzleHttp\Client();
$res = $client->request('GET', 'https://api.exchange.com/dollar');
$body = $res->getBody();
// payment process
}
}
Como podem ver, o código acima é bem simples de entender: estamos fazendo o pagamento de um funcionário que recebe em dólar. Eu quero que se atente ao uso do Guzzle - que é uma lib para requisições - para a consulta da cotação da moeda, que nesse caso é o dólar.
Primeiramente gostaria de falar que não há problema algum em usar o Guzzle, só que a maneira como estamos utilizando, não é a melhor possível. Caso tenha alguma atualização dessa biblioteca, pode ser que a chamada das ações sofram modificações significativas, imaginem o impacto disso em nosso sistema. Vamos supor que atualizamos essa biblioteca e a chamada de ações ficou assim:
<?php
class Payment
{
public function pay(User $user)
{
$client = new GuzzleHttp\Guzzle();
$body = $client->get('https://api.exchange.com/dollar')->body();
// payment process
}
}
Nós teríamos que modificar todo o nosso sistema. E não só isso, estamos em nossa regra de negócio, o componente mais importante do nosso sistema, que está dependendo diretamente de componentes externos dos quais não temos controle. Isso põe em risco todo o nosso software.
Princípio da Inversão de Dependência
Vamos ver sobre a definição desse princípio:
O código que implementa uma política de alto nível não deve depender do código que implementa detalhes de nível mais baixo. São os detalhes que devem depender das políticas.
Para deixar mais claro as palavras acima, podemos entender que a parte principal do nosso sistema, isso é, nossas regras de negócio, não devem depender de coisas que não temos controle, de algo que mude independente das nossas regras, ou seja, nossas regras não dependem dos detalhes, os detalhes dependem das nossas regras.
Pode parecer um pouco confuso o que eu disse anteriormente, mas é mais simples do que parece, olhem o código abaixo para que vocês entendam de uma vez o que foi dito.
<?php
interface CurrentExchange
{
public function getCurrentExchange(string $currency): float;
}
class Payment
{
function __construct(private CurrentExchange $currentExchange)
{}
public function pay(Employee $employee)
{
$body = $this->currentExchange->getCurrentExchange('dollar');
// payment process
}
}
class CurrentExchangeApiExchange implements CurrentExchange
{
public function getCurrentExchange(string $currency): float
{
$client = new GuzzleHttp\Client();
$res = $client->request('GET', "https://api.exchange.com/{$currency}");
return $res->getBody();
}
}
$employee = new Employee('Fulano', 5000);
$payment = new Payment(new CurrentExchangeApiExchange());
$payment->pay($employee);
Dado o código acima, vamos entender por partes. Primeiro que agora a nossa classe Payment, depende da interface CurrentExchange. Sendo assim, qualquer classe que seja passada para Payment, deve obrigatoriamente implementar a interface CurrentExchange. No código acima, quem faz isso é a classe CurrentExchangeApiExchange.
Segunda coisa que podemos perceber, é que se o dado, ao invés de ser buscado em um sistema terceiro, fosse buscado de um banco de dados por exemplo, ou mesmo se o Guzzle mudar, para nossa classe de negócio não mudaríamos uma linha de código.
Indo um pouco mais a fundo, se a nossa classe de negócio precisasse da cotação de um dia específico, passaríamos a moeda e o dia para a pesquisa de cotação. Logo, a interface e as classes que implementam essa interface, teriam que se adaptar à nossa classe de negócio.
<?php
interface CurrentExchange
{
public function getCurrentExchange(string $currency, Datetime $date): float;
}
class Payment
{
function __construct(private CurrentExchange $currentExchange)
{}
public function pay(Employee $employee)
{
$body = $this->currentExchange->getCurrentExchange('dollar', new DateTime('NOW'););
// payment process
}
}
class CurrentExchangeApiExchange implements CurrentExchange
{
public function getCurrentExchange(string $currency, , Datetime $date): float
{
// implementation
}
}
Os detalhes da aplicação tiveram que mudar para se adaptar às nossas regras de negócio, e não o contrário, vocês conseguem perceber os ganhos que tivemos?
Resumo
Bem devs, eu particularmente acho esse tópico muito interessante, pois nós desacoplamos a nossa aplicação, as trocas de componentes se tornam muito mais fáceis, além de manter a coerência em nossos sistemas, onde a parte mais importante é a que comanda e não o inverso.
Acho que o exemplo que dei deixa toda a explicação bem clara. O uso desse princípio é muito importante no desenvolvimento de qualquer sistema que você vá construir.
Dica
Gostaria de dar uma última dica sobre esse princípio: prefira depender de interfaces ou classes abstratas, evite depender de classes concretas. Lembre-se do OCP, LSP e também do ISP.
Por hoje é só e até a próxima.