Interface vs Classe Abstrata
Parte da seção Programação Orientada a Objetos do Journey de PHP da Coddy — lição 30 de 91.
Agora que você aprendeu tanto interfaces quanto classes abstratas, vamos esclarecer quando usar cada uma. Embora elas possam parecer semelhantes, elas servem a propósitos diferentes e possuem capacidades distintas.
| Recurso | Interface | Classe Abstrata |
|---|---|---|
| Métodos | Apenas assinaturas de métodos | Tanto métodos abstratos quanto concretos |
| Propriedades | Apenas constantes | Pode ter propriedades |
| Herança múltipla | Uma classe pode implementar várias | Uma classe pode estender apenas uma |
| Construtor | Não permitido | Pode ter construtor |
Use uma interface quando você deseja definir um contrato que classes não relacionadas podem cumprir. Por exemplo, tanto um Printer quanto um Logger podem implementar uma interface Writable, mesmo que não compartilhem um ancestral comum.
Use uma classe abstrata quando você tem classes relacionadas que compartilham código comum. Se suas classes Dog e Cat ambas precisarem da mesma implementação do método eat(), uma classe abstrata Animal permite que você escreva esse código uma única vez.
Você também pode combinar ambas as abordagens. Uma classe pode estender uma classe abstrata enquanto implementa múltiplas interfaces:
<?php
abstract class Animal {
protected $name;
public function eat() {
return $this->name . " is eating";
}
}
interface Swimmable {
public function swim();
}
class Duck extends Animal implements Swimmable {
public function __construct($name) {
$this->name = $name;
}
public function swim() {
return $this->name . " is swimming";
}
}
Ponto Chave: Escolha interfaces para definir capacidades em classes não relacionadas. Escolha classes abstratas quando precisar compartilhar código entre classes relacionadas.
Desafio
FácilVamos construir um sistema de veículos que demonstra quando usar uma classe abstrata versus uma interface — e como combinar ambas as abordagens de forma eficaz.
Você criará um sistema onde os veículos compartilham propriedades comuns por meio de herança, mas também podem ganhar capacidades adicionais por meio de interfaces. Isso reflete decisões de design do mundo real: um carro "é um" veículo (herança), mas também "tem a capacidade" de ser reabastecível (interface).
Organize seu código em quatro arquivos:
Refuelable.php— Defina uma interfaceRefuelablecom uma única assinatura de método:refuel($amount). Esta capacidade pode ser aplicada a qualquer classe que precise de reabastecimento, independentemente de sua hierarquia de herança.Vehicle.php— Defina uma classe abstrataVehicleque fornece funcionalidade compartilhada para todos os veículos. Ela deve ter uma propriedade protegida$brande uma propriedade protegida$fuelLevel(começando em 0). O construtor aceita o nome da marca. Inclua um método concretogetBrand()que retorna a marca, um método concretogetFuelLevel()que retorna o nível de combustível atual e um método abstratodrive()que as classes filhas devem implementar.Car.php— Crie uma classeCarque estendeVehiclee implementaRefuelable. Inclua ambos os arquivos necessários no topo. O construtor aceita a marca e a passa para o construtor pai. Implementedrive()para retornar"[brand] car is driving". Implementerefuel($amount)para adicionar a quantidade ao nível de combustível e retornar"[brand] refueled to [fuelLevel] liters".main.php— Inclua o arquivo Car. Você receberá duas entradas: um nome de marca e uma quantidade de combustível (converta para inteiro). Crie umCarcom a marca. Imprima o resultado dedrive()na primeira linha e, em seguida, imprima o resultado derefuel()com a quantidade de combustível na segunda linha.
Este desafio ilustra a distinção fundamental: a classe abstrata Vehicle compartilha código e estabelece uma relação de "é um", enquanto a interface Refuelable adiciona uma capacidade que poderia ser aplicada a outras classes não relacionadas (como um gerador ou um barco) sem exigir que sejam veículos.
Folha de consulta
Interfaces e classes abstratas servem a propósitos diferentes:
| Recurso | Interface | Classe Abstrata |
|---|---|---|
| Métodos | Apenas assinaturas de métodos | Tanto métodos abstratos quanto concretos |
| Propriedades | Apenas constantes | Pode ter propriedades |
| Herança múltipla | Uma classe pode implementar várias | Uma classe pode estender apenas uma |
| Construtor | Não permitido | Pode ter construtor |
Use uma interface para definir um contrato para classes não relacionadas. Use uma classe abstrata para compartilhar código comum entre classes relacionadas.
Uma classe pode estender uma classe abstrata enquanto implementa múltiplas interfaces:
<?php
abstract class Animal {
protected $name;
public function eat() {
return $this->name . " is eating";
}
}
interface Swimmable {
public function swim();
}
class Duck extends Animal implements Swimmable {
public function __construct($name) {
$this->name = $name;
}
public function swim() {
return $this->name . " is swimming";
}
}
Experimente você mesmo
<?php
require_once 'Car.php';
// Ler a entrada
$brand = trim(fgets(STDIN));
$fuelAmount = intval(trim(fgets(STDIN)));
// TODO: Criar um Carro com a marca
// TODO: Imprimir o resultado de drive() na primeira linha
// TODO: Imprimir o resultado de refuel() com a quantidade de combustível na segunda linha
?>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
1Fundamentos de POO
Arquivos ExternosIntrodução à POOClasses vs ObjetosA Palavra-chave $thisMétodosPropriedadesConstrutor (__construct)Destrutor (__destruct)Recapitulação - Calculadora Simples4Herança
Herança BásicaA Palavra-chave parent::Sobrescrita de MétodoA Palavra-chave finalClasses AbstratasRecapitulação - Hierarquia de Funcionários7Encapsulamento
Público, Protegido, PrivadoModificadores de Acesso em DetalhesGetters e SettersOcultação de InformaçãoPromoção de Construtor (8.0)Recapitulação - Sistema de Registro de Alunos10Conceitos Avançados de POO
Composição vs HerançaInjeção de DependênciaClasses AnônimasEnums (PHP 8.1)Fibers (PHP 8.1)Mergulho Profundo em Clonagem de ObjetosGeradores e Iteradores13Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão Repository2Namespaces e Autoloading
Introdução a NamespacesA Palavra-chave usePadrão de Autoloading PSR-4Autoloader do ComposerRecapitulação - Projeto Organizado5Interfaces e Contratos
Introdução a InterfacesImplementando InterfacesImplementação de Múltiplas InterfacesInterface vs Classe AbstrataType Hinting com InterfacesRecapitulação - Calculadora de Formas8Métodos Mágicos
Introdução aos Métodos Mágicos__toString e __debugInfo__get, __set, __isset, __unset__call e __callStatic__clone e Clonagem de Objetos__serialize e __unserializeRecapitulação - Coleção Personalizada11Sistema de Tipos e Tratamento de Erros
Declarações de TiposTipos AnuláveisTipos de União e InterseçãoClasses de ExceçãoHierarquia de Exceções PersonalizadasTry, Catch, FinallyRecapitulação - Validador de Formulário14Projeto: Gerenciamento de Biblioteca
Visão Geral do ProjetoClasses Livro e Usuário3Propriedades de Classe
Propriedades de Instância vs EstáticasConstantes em ClassesMétodos e Propriedades EstáticosPropriedades Privadas e ProtegidasPropriedades Readonly (PHP 8.1)Recapitulação - Gerenciador de Conta Bancária6Polimorfismo
Sobrescrita de Métodos RevisitadaPolimorfismo via InterfacesType Hinting e Union TypesLate Static BindingRecapitulação - Processador de Pagamentos9Traits
Introdução a TraitsUsando Múltiplos TraitsResolução de Conflitos de TraitsMétodos Abstratos em TraitsTraits vs Herança12Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão ObserverPadrão Strategy