php-design-patterns
A collection of design patterns written in PHP
Os usuários e usuárias estão amando sua aplicação e você quer dar a eles mais alegria. O que poderia trazer mais felicidade do que notificações por email?
Nós queremos que nossos usuários e usuárias se sintam bem depois de se cadastrarem na nossa aplicação, então por que não enviar um email de boas vindas? Você já conhece um serviço de email confiável que tem um SDK fácil de instalar utilizando Composer. Tudo parece estar correndo bem até agora.
Infelizmente, ao integrar o SDK você nota que as classes não se encaixam muito bem. SignUpService
contém apenas o destinatário e o conteúdo, enquanto ThirdPartyEmailClient
espera parâmetros de configuração.
Nosso SDK hipotético possui a seguinte estrutura:
class ThirdPartyEmailClient
{
public function __construct(
private string $apiKey,
private string $region,
) {
}
public function sendEmail(
string $recipient,
string $content,
): void {
echo sprintf("Using apiKey %s and region %s", $this->apiKey, $this->region);
echo sprintf("Sending email to %s and content %s", $recipient, $content);
}
}
Se ThirdPartyEmailClient
está pedindo por uma apiKey
e region
, vamos satisfazer seus pedidos. Nós podemos simplesmente injetar os dois parâmetros na classe SignUpService
, ou mesmo colocando eles diretamente no código (hardcoding). Não seria um problema até você precisar depois enviar um email após o usuário atualizar o seu perfil, implementado em outra classe chamada ProfileService
.
Bom, por enquanto você só está preocupado em implementar a funcionalidade corretamente porque você é um bom profissional e quer entregar o prometido. A solução por agora é copiar e colar os parâmetros na classe SignUpService
. Bom trabalho!
Naquela noite você não conseguiu dormir por causa daquele código duplicado. A apiKey
também está exposta em todos os lugares. Como resolver esse problema?
Eis então que você se lembra do padrão Adapter
. Ele serve para integrar classes que tem interfaces incompatíveis. O primeiro passo é criar a interface esperada pelo cliente:
interface EmailSenderAdapter
{
public function sendEmail(string $recipient, string $content): void;
}
Agora você só precisa colocar ThirdPartyEmailClient
numa classe que implementa a interface anterior:
final class ThirdPartyEmailSenderAdapter implements EmailSenderAdapter
{
public function __construct(private ThirdPartyEmailClient $emailClient)
{
}
public function sendEmail(string $recipient, string $content): void
{
$this->emailClient->sendEmail($recipient, $content);
}
}
SignUpService
agora está feliz porque tem um amigo que entende sua linguagem.
E para melhorar mais ainda, você decide adicionar um teste unitário!
public function testThirdPartyEmailSenderAdapterAdapterUsesCorrectClient(): void
{
$emailClient = $this->createMock(ThirdPartyEmailClient::class);
$emailAdapter = new ThirdPartyEmailSenderAdapter($emailClient);
$emailClient->expects($this->once())
->method('sendEmail')
->with('[email protected]', 'I love design patterns');
$emailAdapter->sendEmail(
'[email protected]',
'I love design patterns');
}
Suas classes estão bem integradas, testadas e se um dia você precisar alterar o provedor de email, seria só necessário criar uma implementação nova de EmailSenderAdapter
, e então a interface antiga pode ser trocada pela nova. Parabéns.