Uma das minhas soluções favoritas, o Strategy é um padrão de projeto comportamental que muita gente já usou sem saber!
Este padrão de projeto é usado para encapsular seus códigos de forma a facilitar a expanção de recursos de forma organizada e eficiente, a grande diferença entre ele e outros padrões de projeto comportamentais é a forma simples como ele se apresenta.
Imagine que você tem uma loja virtual com várias formas de pagamento e um objeto para calcular o valor final a ser cobrado, ele se parece com isso:
Gostou deste artigo?
Receba atualizações semanais com novos artigos do WebDevBr e outras dicas!
class Payment
{
public function finalPrice(Products $products, string $methodOfPayment): float
{
$total = $products->getTotal();
if ($methodOfPayment == 'boleto') {
$price = $total + 1.50;
} else if ($methodOfPayment == 'debito') {
$price = $total * 1.05;
} else if ($methodOfPayment == 'credito') {
$price = $total;
} else {
throw new \Exception('Method is invalid');
}
return $price;
}
}
Eu não sei vocês, mas toda vez que eu vejo a palavra else
eu fico apavorado, isso pra mim quer dizer que minha classe (ou estrutura de classes) pode ser melhorada, depois que eu comecei a estudar clean code meu código mudou muito.
Agora imagine que você vai aceitar pagamento via PayPal, muito simples, mais um else if
e ta resolvido? Mas se você se preocupa com seu código tanto quanto eu já deve saber que estamos quebrando o princípio "aberto para expanção, fechado para alteração" (ou Open/Closed principle) o O do SOLID, isso quer dizer que não podemos alterar um objeto para incluir um recurso novo nele, não é produtivo. Adivinhem qual é a solução?
Uma das possíveis soluções seria usar o Template Method que eu já comentei aqui no WebDevBr, mas desta vez eu vou preferir o Strategy, para este caso ele é mais simples e eficaz.
O padrão Strategy precisa de 3 coisas para existir, uma classe cliente, uma interface e classes concretas, vejamos um exemplo:
interface MethodOfPayment
{
public function calc(float $total): float;
}
class Boleto implements MethodOfPayment
{
public function calc(float $total): float
{
return $total + 1.50;
}
}
class Debito implements MethodOfPayment
{
public function calc(float $total): float
{
return $total * 1.05;
}
}
class Credito implements MethodOfPayment
{
public function calc(float $total): float
{
return $price = $total;
}
}
Aqui temos então uma interface e 3 classes concretas que a implementa, vamos voltar a classe cliente, no nosso exemplo a Payment
.
class Payment
{
public function finalPrice(Products $products, MethodOfPayment $methodOfPayment): float
{
$total = $products->getTotal();
return $methodOfPayment->calc($total);
}
}
Ficou até mais interessante, o $methodOfPayment
que antes recebia uma string passou a receber a nossa interface, ou seja, qualquer classe que use e siga as regras impostas pela MethodOfPayment
, assim eu tenho certeza que tal objeto terá o método calc()
, qual o tipo de variável ele aceita como entrada e qual a saída (recurso do PHP 7), afinal de contas a interface força isso.
Pra muita gente isso não faz sentido algum, afinal você acabou escrevendo muito mais do que antes, mas com certeza você não terá só uma classe/método cliente para calcular o preço final, ainda tem parcelas, descontos, cambio, fechamento do pedido, relatórios, administração, tem certeza que você não precisa se organizar? A maioria das pessoas só se da conta destes erros depois que já é tarde de mais.
Na prática, chamamos isso de programar para uma interface, o revéz (o primeiro exemplo com if e else) seria programar para uma implementação, o que não faz muito sentido na orientação a objetos.
Espero que este artigo tenha sido útil, e até a próxima pessoal!