Herança em PHP: Fundamentos e Prática
Herança é um dos pilares da Programação Orientada a Objetos. Permite que uma classe filha herde atributos e métodos de uma classe pai, eliminando duplicação de código e criando hierarquias lógicas. Em PHP, usamos a palavra-chave extends para estabelecer essa relação.
class Veiculo {
protected $marca;
protected $velocidade = 0;
public function __construct($marca) {
$this->marca = $marca;
}
public function acelerar() {
$this->velocidade += 10;
return "Acelerou para {$this->velocidade} km/h";
}
public function getMarca() {
return $this->marca;
}
}
class Carro extends Veiculo {
private $portas;
public function __construct($marca, $portas) {
parent::__construct($marca);
$this->portas = $portas;
}
public function abrirBagagem() {
return "Bagagem aberta no {$this->marca}";
}
}
$carro = new Carro("Toyota", 4);
echo $carro->acelerar(); // Acelerou para 10 km/h
echo $carro->abrirBagagem(); // Bagagem aberta no Toyota
Note que usamos protected para atributos que a classe filha precisa acessar, e parent:: para chamar métodos da classe pai. Isso garante encapsulamento sem bloquear o acesso necessário.
Visibilidade e Modificadores
A escolha correta entre public, protected e private é fundamental. Use protected para membros que classes filhas devem acessar, private para dados sensíveis que nunca mudarão, e public apenas quando necessário expor a interface.
Polimorfismo: Múltiplas Formas, Uma Interface
Polimorfismo permite que objetos de diferentes classes respondam ao mesmo método de formas distintas. É a capacidade de uma classe filha sobrescrever (override) métodos da classe pai, mantendo a mesma assinatura.
abstract class Animal {
protected $nome;
public function __construct($nome) {
$this->nome = $nome;
}
abstract public function fazer_som();
public function apresentar() {
return "Sou o {$this->nome}";
}
}
class Cachorro extends Animal {
public function fazer_som() {
return "{$this->nome} faz: Au au!";
}
}
class Gato extends Animal {
public function fazer_som() {
return "{$this->nome} faz: Miau!";
}
}
$animais = [
new Cachorro("Rex"),
new Gato("Félix")
];
foreach ($animais as $animal) {
echo $animal->fazer_som() . "\n";
// Rex faz: Au au!
// Félix faz: Miau!
}
Aqui usamos uma classe abstract com um método abstract, forçando que subclasses implementem fazer_som(). Cada animal responde diferentemente ao mesmo método. Isso é polimorfismo em ação.
Tipagem e Type Hints
PHP 7+ permite definir tipos de retorno e parâmetros. Combine isso com polimorfismo para código mais robusto:
interface Voo {
public function decolar(): string;
public function pousar(): string;
}
class Aviao implements Voo {
public function decolar(): string {
return "Avião decolando...";
}
public function pousar(): string {
return "Avião pousando...";
}
}
function executarVoo(Voo $veiculo): void {
echo $veiculo->decolar() . "\n";
echo $veiculo->pousar() . "\n";
}
executarVoo(new Aviao());
Usando interface e type hints, garantimos que qualquer classe que implemente Voo funcionará com executarVoo(). Isso é polimorfismo paramétrico.
Casos Práticos: Quando Usar
Herança vs. Composição
Nem sempre herança é a melhor solução. Prefira composição quando a relação não é claramente "é um(a)":
// ❌ Evite
class Carro extends Motor { }
// ✅ Prefira
class Carro {
private $motor;
public function __construct(Motor $motor) {
$this->motor = $motor;
}
public function ligar() {
return $this->motor->iniciar();
}
}
Use herança para relações "é um(a)" (Carro é Veiculo) e composição para relações "tem um(a)" (Carro tem Motor).
Exemplo Real: Sistema de Pagamento
abstract class Pagamento {
protected $valor;
protected $status = 'pendente';
public function __construct($valor) {
$this->valor = $valor;
}
abstract public function processar(): bool;
public function getStatus(): string {
return $this->status;
}
}
class PagamentoCartao extends Pagamento {
private $numero;
public function __construct($valor, $numero) {
parent::__construct($valor);
$this->numero = $numero;
}
public function processar(): bool {
// Simula validação
if (strlen($this->numero) === 16) {
$this->status = 'aprovado';
return true;
}
$this->status = 'rejeitado';
return false;
}
}
class PagamentoPixel extends Pagamento {
public function processar(): bool {
$this->status = 'aprovado';
return true;
}
}
function realizarVenda(Pagamento $metodo): void {
if ($metodo->processar()) {
echo "Venda finalizada - Status: " . $metodo->getStatus();
} else {
echo "Falha na venda - Status: " . $metodo->getStatus();
}
}
realizarVenda(new PagamentoCartao(150, '1234567890123456'));
realizarVenda(new PagamentoPixel(200));
Este exemplo mostra como polimorfismo permite adicionar novos métodos de pagamento sem modificar realizarVenda(). Respeita o princípio Open/Closed.
Conclusão
Herança e polimorfismo são ferramentas poderosas quando usadas corretamente. Primeiro aprendizado: use herança para relações hierárquicas reais, não para reutilização de código superficial. Segundo: polimorfismo permite escrever código flexível e extensível que funciona com múltiplas classes através da mesma interface. Terceiro: combine com interfaces e classes abstratas para criar contratos claros e manuteníveis. Dominar esses conceitos elevará significativamente a qualidade da sua arquitetura de software.