Padrão de projeto Adapter em 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?

Vamos dar boas vindas!

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);
    }
}

Faça funcionar

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!

Algo parece errado

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?

Faça funcionar melhor

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!

Testando

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');
}

Você conseguiu!

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.

GitHub logo fabiothiroki / php-design-patterns

A collection of design patterns written in PHP

php-design-patterns

A collection of design patterns written in PHP

PHP Composer Coverage Status

Creational

Structural

17