SOLID e o entrelaçamento de princípios
Fala meu povo, seguinte, eu já fiz 5 artigos abordando os princípios SOLID, sigla por sigla, com exemplos teóricos e práticos. Ainda assim, sinto que faltava alguma coisa para deixar mais claro para todos, então, resolvi escrever esse artigo para que possamos revisar o conteúdo aprendido de uma forma bem prática.
E antes que eu me esqueça, aqui estão os 5 artigos que mencionei anteriormente, recomendo a leitura antes continuar neste artigo:
- 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
- SOLID e o princípio do desacoplamento
Problema
Temos um pequeno problema para resolver: quando a fatura de um cliente fechar, o valor e o número do código de barras para pagamento, deverá ser enviado para o cliente em uma das formas escolhidas por ele anteriormente. As formas possíveis são SMS, E-MAIL e WHATSAPP.
Mão na massa
Com a premissa falada anteriormente, vamos agora implementar essa lógica. Começaremos pela parte mais importante do nosso problema, a Regra de Negócio, vamos lá?
<?php
interface InvoiceItemFinder
{
public function findByClient(int $clientId): InvoiceItemCollection;
}
interface Notification
{
public function sender(Client $client, Invoice $invoice);
}
interface IssuePostingInvoice
{
public function issue(string $value): string;
}
class IssueInvoice
{
public function __construct(
private InvoiceItemFinder $invoiceItemFinder,
private Notification $notification,
private IssuePostingInvoice $issuePostingInvoice
)
{}
public function issue(Client $clientId)
{
// list all items on the invoice
$items = $this->invoiceItemFinder;
// sums all item values and adds the variable called $total
// generates the posting invoice for payment
$postingInvoice = $this->issuePostingInvoice($total);
// send notification to client
$this->notification($client, $postingInvoice);
}
}
Explicando o que foi feito anteriormente, criamos uma classe chamada IssueInvoice, que tem a responsabilidade de montar a fatura. Aind em nossa classe IssueInvoice, podemos perceber que aplicamos o Single Reponsability Principle, já que ela é responsável por uma ação específica, e tudo o que ela precisa para executar essa ação, é implementada em outras classes.
Ainda continuando nessa classe, podemos perceber o uso do Dependency Inversion Principle, pois em nosso construtor, estamos recebendo as dependências que devem ser de tipos específicos. Com isso em mente, caso a nossa regra precise mudar, as dependências precisarão mudar, e não o inverso.
Já construímos nossa lógica principal, agora vamos implementar as dependências do nosso domínio, veja abaixo:
<?php
interface InvoiceItemFinder
{
public function findByClient(int $clientId): InvoiceItemCollection;
}
interface Notification
{
public function sender(Client $client, Invoice $invoice);
}
interface IssuePostingInvoice
{
public function issue(string $value): string;
}
class IssueInvoice
{
public function __construct(
private InvoiceItemFinder $invoiceItemFinder,
private Notification $notification,
private IssuePostingInvoice $issuePostingInvoice
)
{}
public function issue(Client $clientId)
{
// list all items on the invoice
$items = $this->invoiceItemFinder;
// sums all item values and adds the variable called $total
// generates the posting invoice for payment
$postingInvoice = $this->issuePostingInvoice($total);
// send notification to client
$this->notification($client, $postingInvoice);
}
}
class SMS implements Notification
{
public function sender(Client $client, Invoice $invoice)
{
// code...
}
}
class Email implements Notification
{
public function sender(Client $client, Invoice $invoice)
{
// code...
}
}
class WhatsApp implements Notification
{
public function sender(Client $client, Invoice $invoice)
{
// code...
}
}
class InvoiceItemFinderRepository implements InvoiceItemFinder
{
public function findByClient(int $clientId): InvoiceItemCollection
{
// code...
return $invoiceItemCollection;
}
}
class IssuePostingInvoiceService implements IssuePostingInvoice
{
public function issue(string $value): string
{
// code...
}
}
Pronto, acabamos de criar as classes que implementam as nossas dependências. Um detalhe importante que eu posso não ter falado, é que não vou me aprofundar muito na lógica interna das classes, até porque a finalidade do artigo não é essa. Continuando a explicação, eu criei várias classes que implementam as nossas interfaces, essa classes serão passadas para nossa classe IssueInvoice no momento da sua criação.
Algo que não sei se perceberam, mas nas classes que implementam Notification acabamos respeitando o Open/Closed Principle, onde não sabemos a implementação interna das classes, mas sabemos a entrada, saída, e o que ela deve fazer.
Outro ponto que posso falar, é sobre o Liskov Segregation Principle, aqui, podemos substituir qualquer subtipo de notificação por outro subtipo, que o nosso código continuará funcionando. E não somente isso, se perceberem, nossas classes implementam somente as interfaces necessárias, e recebem somente os dados necessário, respeitando o Interface Segregation Principle.
Resumo
Então pessoal, a lógica implementada ficou bem fácil para o nível apresentado, obviamente que seria algo mais complexo que isso, porém como disse anteriormente, a ideia do artigo é apresentar os princípios SOLID na prática. Com isso, podemos ver que conseguimos aplicar todos os princípios em um problema real, ou muito parecido com algo que já presenciei ou que vocês já presenciaram.
O ponto em questão, foi os benefícios que tivemos ao aplicar esse conjunto de boas práticas, ficou muito mais fácil a manutenibilidade, testabilidade, etc. Podemos substituir a implementação interna das nossas dependências que isso não irá impactar em nossa classe de domínio, criamos uma implementação DESACOPLADA.
Acredito que depois de estudar cada princípio desse separadamente, e agora estudar eles em conjunto, você que está lendo, está preparado para desenvolver melhor. Você já tem a capacidade de identificar possíveis infrações a esses princípios. Agora é com você, pratique bastante, leia e releia, procure outros exemplos, busque sempre melhorar.
Para quem leu todos os artigos eu só tenho a agradecer, sinceramente espero que eu tenha conseguido ajudá-los a se tornar uma pessoa que desenvolve melhor. Por hoje é isso, chega ao fim essa aventura para aprender SOLID, nos vemos em outros artigos, até mais.