Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
October 18, 2021 04:15 pm GMT

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

  1. Todos os produtos devem implementar a mesma classe ou interface.

    Ex: Payment implementado em todos os produtos.

  2. Produtos concretos so implementaes diferentes da mesma interface.

    Ex: Invoice , CreditCard.

  3. 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 fbrica createPayment e retornado um produto do tipo Payment.

  4. 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 criadora AbstractPayment.

  5. Criadores concretos sobrescrevem o mtodo fbrica base para criar um tipo diferente de produto.

    Ex: GatewayX e GatewayY.

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

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To