An Interest In:
Web News this Week
- April 20, 2024
- April 19, 2024
- April 18, 2024
- April 17, 2024
- April 16, 2024
- April 15, 2024
- April 14, 2024
Design Pattern: Factory Method
Como o prprio nome j diz, ele a nossa fbrica. De que? De objetos uai.
Este um padro criacional que fornece um interface de criao de objetos numa superclasse, mas que seja possvel alternar o tipo do objeto que retorna dessa criao.
Explicando o problema
Vamos imaginar que voc est desenvolvendo um e-commerce. No primeiro momento sua aplicao apenas aceitar carto de crdito, portanto sempre que vier um pedido no seu sistema toda a regra de pagamentos e validaes ficar dentro da classe CreditCard
.
<?phpclass Cart { public function checkout() { $payment = new CreditCard; return $payment?->pay(); }}class CreditCard { public function pay() { return 'Transao concluda com sucesso' . PHP_EOL; }}$cart = new Cart();echo $cart->checkout(); //Transao concluda com sucesso
Agora sua loja est indo muito bem e crescendo, mas seus clientes comeam a reclamar e pedem por mais meios de pagamento. Para evitar que o nmero de vendas caia, logo voc comea a desenvolver melhorias adicionando um novo meio de pagamento Invoice
(Boletos).
uma boa noticia certo? Mas se voc no tomar cuidado no inicio do desenvolvimento, possvel que tudo esteja acoplado a uma nica classe, e adicionar qualquer coisa pode alterar toda a base de cdigo. Alm disso sempre que for criado um meio de pagamento novo, provavelmente passar por isso tudo novamente.
Vejamos um exemplo adicionando a classe Invoices
num universo simplificado.
<?phpclass Cart { private $cart; public function __construct ($cart) { $this->cart = $cart; } public function checkout() { $payment = null; if ($this->cart->payment === 'boleto') { $payment = new Invoice; } if ($this->cart->payment === 'credit') { $payment = new CreditCard; } return $payment?->pay() ?? 'Erro'; }}class Invoice { public function pay() { return 'Boleto para ser pago'. PHP_EOL; }}class CreditCard { public function pay() { return 'Transao concluda com sucesso' . PHP_EOL; }}//Case 1$myCart = ['payment' => 'boleto'];$cart = new Cart((object) $myCart);echo $cart->checkout(); //Boleto para ser pago//Case 2$myCart = ['payment' => 'credit'];$cart = new Cart((object) $myCart);echo $cart->checkout(); //Transao concluda com sucesso//Case 3 - Mtodo de pagamento no implementado$myCart = ['payment' => 'Outro meio de pagamento'];$cart = new Cart((object) $myCart);echo $cart->checkout(); //Erro
Nesse pequeno exemplo parece at simples (s adicionar uns ifs e j era), mas nem sempre assim, adicionar uma nova classe num cdigo que depende de classes concretas pode dar uma certa dor de cabea.
Pense comigo, como ficaria esse cdigo com 5 meios de pagamentos? Tero 5 ifs? Um Switch? Parece meio ruim n, alm de deixar o sdigo bastante sujo, ficar cheio de condicionais que mudam o comportamento da aplicao, podendo at criar um erro inesperado.
Soluo
O padro Factory sugere que voc no chame diretamente o operador new
. Voc pode estar se perguntando como ir criar os objetos, mas calma, ainda ser com o operador new
, mas ele ser chamado dentro desse mtodo chamado de fbrica
, esses objetos gerados e retornados por esse mtodo sero chamados de produtos
.
Usando nosso exemplo anterior, vamos aplicar o conceito para entender a ideia dele:
<?phpclass Cart { private $factoryPayment; private $cart; public function __construct ($cart) { $this->factoryPayment = new FactoryPayment; $this->cart = $cart; } public function checkout() { $payment = $this->factoryPayment->createPayment($this->cart->payment); return $payment?->pay() ?? 'Erro'; }}class FactoryPayment { public function createPayment($payment) : Payment|null { return match ($payment) { 'boleto' => new Invoice, 'credit' => new CreditCard, default => null }; }}interface Payment { public function pay();}class Invoice implements Payment{ public function pay() { return 'Boleto para ser pago'. PHP_EOL; }}class CreditCard implements Payment{ public function pay() { return 'Transao concluda com sucesso' . PHP_EOL; }}//Case 1$myCart = ['payment' => 'boleto'];$cart = new Cart((object) $myCart);echo $cart->checkout(); //Boleto para ser pago//Case 2$myCart = ['payment' => 'credit'];$cart = new Cart((object) $myCart);echo $cart->checkout(); //Transao concluda com sucesso//Case 3 - Mtodo de pagamento no implementado$myCart = ['payment' => 'Outro meio de pagamento'];$cart = new Cart((object) $myCart);echo $cart->checkout(); //Erro
Olhando de incio pode parecer sem sentido, pois apenas mudamos a chamada de criao para outra classe. Mas isso ajuda bastante na hora de incluir novos meios de pagamento por exemplo, basta implementar a interface Payment
com o mtodo pay
e se tiver tudo correto com a nova classe vai funcionar tudo corretamente sem precisar de alterar nada no cdigo principal, pois est tudo abstrado.
Outro caso muito maneiro a possibilidade de criar outro factory antes do FactoryPayment
, para criar gateways de pagamento. Suponha que vc decida pagar boletos (Invoice) com o gateway X e o carto de crdito com o Y (CreditCard) com esse padro isso possvel, irei explicar no cdigo cada ponto, veja:
<?php//Classe principal que ser o cdigo cliente que receber os produtosclass Cart { private $factoryGateway; private $cart; public function __construct ($cart) { $this->factoryGateway = new FactoryGateway; $this->cart = $cart; } public function checkout() { $payment = $this->factoryGateway->createGateway($this->cart->payment); return $payment?->pay() ?? 'Erro'; }}// Classe fbrica de gateways que ser chamada pelo cdigo cliente// usando o mtodo de pagamento para deciso.class FactoryGateway { public function createGateway($payment) : Payment|null { $gateway = match ($payment) { 'boleto' => new GatewayX, 'credit' => new GatewayY, default => null }; return $gateway?->createPayment(); }}// Classe abstrata que ser usada como fbrica de pagamentosabstract class AbstractPayment { abstract public function createPayment(): Payment; protected function authenticate() { echo 'Autenticado no ' . get_class($this) . PHP_EOL; }}// Agora temos que implementar os gateways e os mtodos que retornaro os produtos// que no nosso caso so meios de pagamentoclass GatewayX extends AbstractPayment{ public function createPayment(): Payment { $this->authenticate(); return new Invoice; }}class GatewayY extends AbstractPayment{ public function createPayment() : Payment { $this->authenticate(); return new CreditCard; }}// Abaixo temos a interface e os produtos que a implementam. interface Payment { public function pay();}class Invoice implements Payment{ public function pay() { //cria boleto return 'Boleto para ser pago'. PHP_EOL; }}class CreditCard implements Payment{ public function pay() { //valida dados do carto, limite e etc return 'Transao concluda com sucesso' . PHP_EOL; }}//Case 1$myCart = ['payment' => 'boleto'];$cart = new Cart((object) $myCart);echo $cart->checkout(); //Boleto para ser pago com sucesso no GatewayX//Case 2$myCart = ['payment' => 'credit'];$cart = new Cart((object) $myCart);echo $cart->checkout(); //Transao concluda com sucesso no GatewayY//Case 3 - Mtodo de pagamento no implementado$myCart = ['payment' => 'Outro meio de pagamento'];$cart = new Cart((object) $myCart);echo $cart->checkout(); //Erro
OBS: Ele fica bem similar ao prximo padro que vou mostrar em um artigo futuro, que o Abstract Factory.
Pontos de ateno
Todos os
produtos
devem implementar a mesma classe ou interface.Ex:
Payment
implementado em todos os produtos.Produtos concretos
so implementaes diferentes da mesma interface.Ex:
Invoice
,CreditCard
.Na classe criador, deve ser declarado o mtodo fbrica que retornar os novos objetos produto. Esse retorno deve corresponder interface do produto.
Ex: Criador
AbstractPayment
possui o mtodo fbricacreatePayment
e retornado um produto do tipoPayment
.Apesar de ter criador no nome, essa no a principal responsabilidade dela, possvel ter alguma regra de negcio relacionada aos seus produtos.
Ex: Mtodo
authenticate
da classe criadoraAbstractPayment
.Criadores concretos sobrescrevem o mtodo fbrica base para criar um tipo diferente de produto.
Ex:
GatewayX
eGatewayY
.
SOLID
Aqui temos meno a 2 itens do SOLID:
Princpio de responsabilidade nica (S): Voc pode mover o cdigo criador para um nico ponto, facilitando a manuteno.
Principio aberto/fechado (O): Voc pode adicionar novos produtos na aplicao sem quebrar o cdigo cliente.
Concluso
Use esse padro sempre que voc no souber os tipos e dependncias dos objetos que seu cdigo ir utilizar, ele separa o cdigo construtor de produtos do cdigo principal. Isso deixa o cdigo de construo do produto mais simples de ser extendido e independente do resto.
Por exemplo, caso queira adicionar mais mtodos de pagamento ou gateways, basta implementar as classes correspondentes, adicionar nos mtodos fbrica e pronto, seu cdigo est com a nova funcionalidade.
isso pessoal, chegamos ao fim de mais um artigo, at o prximo.
Original Link: https://dev.to/luanloose/design-pattern-factory-method-3dhc
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To