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, onde as classes filhas sobrescrevem os métodos das classes pai. As interfaces fornecem 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 certos métodos, independentemente de 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. Esta é a principal vantagem sobre o polimorfismo baseado em herança: você pode agrupar classes não relacionadas por suas capacidades, em vez de sua ancestralidade.

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

challenge icon

Desafio

Fácil

Vamos construir um sistema de armazenamento que demonstra o polimorfismo baseado em interfaces. 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). Este contrato garante que qualquer sistema de armazenamento possa armazenar e recuperar dados, independentemente de como seja implementado internamente.
  • FileStorage.php — Crie uma classe FileStorage que implementa Storable. Inclua o arquivo da interface. A classe deve ter uma propriedade privada $directory definida através do construtor. Implemente save($key, $data) para retornar "Saving '$data' to file $this->directory/$key.txt". Implemente retrieve($key) para retornar "Reading from file $this->directory/$key.txt".
  • DatabaseStorage.php — Crie uma classe DatabaseStorage que também implementa Storable. Inclua o arquivo da interface. A classe deve ter uma propriedade privada $tableName definida através do construtor. Implemente save($key, $data) para retornar "Inserting '$data' into table $this->tableName with key $key". Implemente retrieve($key) para retornar "Selecting from table $this->tableName where key = $key".
  • main.php — Inclua ambos os arquivos de armazenamento. Crie uma função chamada storeData que aceita 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 aceita 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 o armazenamento de arquivo primeiro, depois o armazenamento de banco de dados. Imprima cada resultado em sua própria linha. Em seguida, use fetchData() para recuperar do armazenamento de arquivo e imprima esse resultado.

Observe como storeData() e fetchData() funcionam de forma idêntica com ambos os tipos de armazenamento — eles apenas se importam que o objeto implemente Storable. O sistema de arquivos e o banco de dados têm implementações internas completamente diferentes, mas são intercambiáveis através 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: Criar uma função storeData que aceite um Storable, chave e dados
// Ela deve retornar o resultado da chamada de save() com a chave e os dados

// TODO: Criar uma função fetchData que aceite um Storable e uma chave
// Ela deve retornar o resultado da chamada de retrieve() com a chave

// TODO: Criar uma instância de FileStorage com o diretório

// TODO: Criar uma instância de DatabaseStorage com o nome da tabela

// TODO: Usar storeData() para salvar dados com o armazenamento em arquivo e imprimir o resultado

// TODO: Usar storeData() para salvar dados com o armazenamento em banco de dados e imprimir o resultado

// TODO: Usar fetchData() para recuperar do armazenamento em arquivo e imprimir o resultado
?>
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