Menu
Coddy logo textTech

Polimorfismo via Interfaces

Parte da seção Programação Orientada a Objetos do Journey de PHP da Coddy. Lição 34 de 91.

Na lição anterior, você viu o polimorfismo por meio da herança, em que as classes filhas substituem os métodos das classes pai. As interfaces oferecem outra maneira poderosa de alcançar o polimorfismo, especialmente ao trabalhar com classes não relacionadas que compartilham um comportamento comum.

Com interfaces, o polimorfismo vem do próprio contrato. Qualquer classe que implemente uma interface garante que possui determinados métodos, independentemente da sua hierarquia de herança:

<?php
interface Notifiable {
    public function send(string $message);
}

class EmailNotifier implements Notifiable {
    public function send(string $message) {
        return "Email: " . $message;
    }
}

class SMSNotifier implements Notifiable {
    public function send(string $message) {
        return "SMS: " . $message;
    }
}

class PushNotifier implements Notifiable {
    public function send(string $message) {
        return "Push: " . $message;
    }
}

function notify(Notifiable $notifier, string $message) {
    return $notifier->send($message);
}

echo notify(new EmailNotifier(), "Hello") . "\n";
echo notify(new SMSNotifier(), "Hello") . "\n";
echo notify(new PushNotifier(), "Hello");

Saída:

Email: Hello
SMS: Hello
Push: Hello

A função notify() funciona com qualquer objeto Notifiable. Essas classes não compartilham uma classe-pai — elas são completamente não relacionadas, exceto por implementarem a mesma interface. Essa é a principal vantagem sobre o polimorfismo baseado em herança: você pode agrupar classes não relacionadas por suas capacidades, em vez de por sua ancestralidade.

Ponto principal: O polimorfismo baseado em interfaces permite que classes não relacionadas sejam usadas de forma intercambiável com base em um comportamento compartilhado, tornando seu código mais flexível e desacoplado de implementações específicas.

challenge icon

Desafio

Fácil

Vamos criar um sistema de armazenamento que demonstra o polimorfismo baseado em interface. Você criará diferentes backends de armazenamento, um sistema de arquivos e um banco de dados, que não têm nada em comum, exceto pelo fato de ambos implementarem a mesma interface de armazenamento. Uma única função funcionará com qualquer tipo de armazenamento, mostrando como as interfaces permitem o polimorfismo entre classes completamente não relacionadas.

Você organizará seu código em quatro arquivos:

  • Storable.php: defina uma interface Storable com duas assinaturas de método: save($key, $data) e retrieve($key). Esse contrato garante que qualquer sistema de armazenamento possa armazenar e recuperar dados, independentemente de como é implementado internamente.
  • FileStorage.php: crie uma classe FileStorage que implemente Storable. Inclua o arquivo da interface. A classe deve ter uma propriedade privada $directory definida pelo construtor. Implemente save($key, $data) para retornar "Saving '[data]' to file [directory]/[key].txt". Implemente retrieve($key) para retornar "Reading from file [directory]/[key].txt".
  • DatabaseStorage.php: crie uma classe DatabaseStorage que também implemente Storable. Inclua o arquivo da interface. A classe deve ter uma propriedade privada $tableName definida pelo construtor. Implemente save($key, $data) para retornar "Inserting '[data]' into table [tableName] with key [key]". Implemente retrieve($key) para retornar "Selecting from table [tableName] where key = [key]".
  • main.php: inclua os dois arquivos de armazenamento. Crie uma função chamada storeData que aceite um parâmetro Storable, uma chave e dados. A função deve retornar o resultado da chamada de save() com a chave e os dados. Crie outra função chamada fetchData que aceite um parâmetro Storable e uma chave, retornando o resultado da chamada de retrieve().

Você receberá quatro entradas: um caminho de diretório, um nome de tabela, uma chave e alguns dados para armazenar. Crie um FileStorage com o diretório e um DatabaseStorage com o nome da tabela. Use sua função storeData() para salvar os dados usando primeiro o armazenamento de arquivos e, depois, o armazenamento de banco de dados. Imprima cada resultado em sua própria linha. Em seguida, use fetchData() para recuperar os dados do armazenamento de arquivos e imprima esse resultado.

Observe como storeData() e fetchData() funcionam de forma idêntica com os dois tipos de armazenamento. Elas só se importam que o objeto implemente Storable. O sistema de arquivos e o banco de dados têm implementações internas completamente diferentes, mas ainda assim são intercambiáveis por meio do contrato da interface.

Experimente você mesmo

<?php
require_once 'FileStorage.php';
require_once 'DatabaseStorage.php';

// Ler entradas
$directory = trim(fgets(STDIN));
$tableName = trim(fgets(STDIN));
$key = trim(fgets(STDIN));
$data = trim(fgets(STDIN));

// TODO: Crie uma função storeData que aceita um Storable, key e data
// Deve retornar o resultado de chamar save() com a key e data

// TODO: Crie uma função fetchData que aceita um Storable e key
// Deve retornar o resultado de chamar retrieve() com a key

// TODO: Crie uma instância de FileStorage com o directory

// TODO: Crie uma instância de DatabaseStorage com o table name

// TODO: Use storeData() para salvar data com file storage, depois imprima o resultado

// TODO: Use storeData() para salvar data com database storage, depois imprima o resultado

// TODO: Use fetchData() to retrieve from file storage, then print the result
?>
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos

Pratique por conta própria: Compilador de PHP online