SOLID e o entrelaçamento de princípios
Tags: oop, solid
Published: 2021-08-27 16:05:05

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**](https://dev.to/erandirjunior/solid-e-o-principio-do-nao-faca-mais-do-que-nao-deve-5g2b)
* [**SOLID e o princípio do faça algo, mas do seu jeito**](https://dev.to/erandirjunior/solid-e-o-principio-do-faca-algo-mas-do-seu-jeito-1gkb)
* [**SOLID e o princípio do 6 por meia dúzia**](https://dev.to/erandirjunior/solid-e-o-principio-do-6-por-meia-duzia-2h8h)
* [**SOLID e o princípio da implementação desnecessária**](https://dev.to/erandirjunior/solid-e-o-principio-da-implementacao-desnecessaria-9p3)
* [**SOLID e o princípio do desacoplamento**](https://dev.to/erandirjunior/solid-e-o-principio-do-desacoplamento-332e)

## 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
<?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
<?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.