Hoje vou explicar sobre o Padrão de Projeto Prototype. O seu nome já dá uma noção explícita do seu uso. Quando queremos criar um protótipo que nos permitirá copiar objetos sem ter que gerar uma nova classe para isso. Ou seja, nós conseguimos clonar objetos através de cópias de um protótipo de classe.
A ideia central do Padrão Prototype é firmar um contrato através de uma interface, onde as classes que se especializarem nesta habilidade, de serem protótipos, possam implementar a capacidade de gerar um clone de si.
Quando devo utilizar o Padrão de Projeto Prototype?
Suponha que você queira criar um objeto específico para ter um comportamento similar a de outro objeto apenas em runtime, ou seja, apenas em tempo de execução. Seria um gasto desnecessário de esforço ter que fazer, por exemplo, uma herança em uma classe concreta para ter um comportamento que, tecnicamente, só vai acontecer em um momento específico. Bastaria para este caso clonar o objeto em questão utilizando o Prototype.
No artigo sobre Abstract Factory, houve um uso razoável de herança. Dependendo da complexidade, o padrão de projeto Prototype pode ser uma alternativa em alguns momentos e complementar em outros. Ou seja, uma Abstract Factory pode conter um conjunto de protótipos, onde poderia ser utilizado, por exemplo, na clonagem dos produtos.
Exemplo de uso do Padrão Prototype
Nada melhor que um exemplo para conseguirmos fixar um conhecimento. Como exemplo, vamos criar um protótipo de criação de Smartphones. Vamos “supor” 😇 que a maioria dos Smartphones das grandes marcas, “aparentemente” 🤫 só mudam as carcaças e colocam apenas pequenas peças adicionais para dizer que são “smartphones com modelos novos”. Acredito que seja um bom exemplo para fixar o conhecimento de como utilizar o Padrão de Projeto Prototype, não é verdade?
Primeiro vamos criar uma classe abstrata para representar a estrutura dos smartphones para evitarmos redundância de códigos. Ou seja, utiliza o reuso de códigos.
Esta classe Smartphone, vai implementar uma interface Cloneable, o que garante que as classes que herdam estas características, serão obrigadas a implementarem esta capacidade de gerar clone de si.
<?php declare(strict_types=1); namespace Growthdev\DesignPatterns\Creational\Prototype; interface Cloneable { public function clone(): self; }
<?php declare(strict_types=1); namespace Growthdev\DesignPatterns\Creational\Prototype; abstract class Smartphone implements Cloneable { private string $name; private float $price; private string $addicionalFeatures = 'none'; public function setName(string $name): self { $this->name = $name; return $this; } public function setPrice(float $price): self { $this->price = $price; return $this; } public function setAddicionalFeatures(string $addicionalFeatures): self { $this->addicionalFeatures = $addicionalFeatures; return $this; } public function getName(): string { return $this->name; } public function getPrice(): float { return $this->price; } public function getAddicionalFeatures(): string { return $this->addicionalFeatures; } public function getModel(): string { return sprintf( 'Model name: %s, Price: %.2f, Addicional Features: %s', $this->name, $this->price, $this->addicionalFeatures ); } }
Agora vamos criar as classes concretas apenas implementando o método clone.
<?php declare(strict_types=1); namespace Growthdev\DesignPatterns\Creational\Prototype; final class Samsung extends Smartphone { public function clone(): Cloneable { return clone $this; } }
<?php declare(strict_types=1); namespace Growthdev\DesignPatterns\Creational\Prototype; final class Iphone extends Smartphone { public function clone(): Cloneable { return clone $this; } }
Com isso já conseguimos criar os testes para você ver na prática como funciona o clone em runtime.
<?php declare(strict_types=1); namespace Growthdev\DesignPatterns\Tests\Creational\Prototype; use Growthdev\DesignPatterns\Creational\Prototype\Samsung; use PHPUnit\Framework\TestCase; final class SamsungPrototypeTest extends TestCase { public function testCanCreateSamsungPhoneClone(): void { $samsungXPT1 = (new Samsung()) ->setName('Samsung XPT1') ->setPrice(1000); $samsungXPT2 = clone $samsungXPT1; $samsungXPT2->setName('Samsung XPT2') ->setPrice(2000) ->setAddicionalFeatures('99 GB'); $this->assertEquals( 'Model name: Samsung XPT1, Price: 1000.00, Addicional Features: none', $samsungXPT1->getModel() ); $this->assertEquals( 'Model name: Samsung XPT2, Price: 2000.00, Addicional Features: 99 GB', $samsungXPT2->getModel() ); } }
<?php declare(strict_types=1); namespace Growthdev\DesignPatterns\Tests\Creational\Prototype; use Growthdev\DesignPatterns\Creational\Prototype\Iphone; use PHPUnit\Framework\TestCase; final class IphonePrototypeTest extends TestCase { public function testCanCreateIphoneClone(): void { $iphoneXQT1 = (new Iphone()) ->setName('Iphone XQT1') ->setPrice(11000); $iphoneXQT2 = clone $iphoneXQT1; $iphoneXQT2->setName('Iphone XQT2') ->setPrice(250000) ->setAddicionalFeatures('99 GB'); $this->assertEquals( 'Model name: Iphone XQT1, Price: 11000.00, Addicional Features: none', $iphoneXQT1->getModel() ); $this->assertEquals( 'Model name: Iphone XQT2, Price: 250000.00, Addicional Features: 99 GB', $iphoneXQT2->getModel() ); } }
Este é o último Padrão de Projeto Criacional, ou Padrão de Criação. Já expliquei sobre Abstract Factory, Builder, Factory Method e Singleton. Completando este artigo sobre O Padrão Prototype, entraremos nos demais padrões Estruturais e Comportamentais.
Os códigos de exemplos de todos os padrões de projetos estão no meu Github:
https://github.com/growthdev-repo/design-patterns
Me conta um pouco sobre você… Como você está aplicando estes conceitos no seu dia a dia? Ainda está encontrando dificuldades, se sim, deixe aqui seu comentário para que outros que passaram por situações semelhantes possam interagir e com isso criar uma corrente muito grande de colaboração. Até o próximo artigo e compartilhe este artigo com outras pessoas!
Confiança Sempre!!!
Seja o primeiro a comentar