Herança
Esta aula aborda o conceito de herança em PHP, explicando como criar hierarquias de classes usando a palavra-chave extends, como acessar métodos da classe pai com parent, como sobrescrever métodos e quando aplicar esse padrão de design de forma adequada.
A herança é um dos pilares da programação orientada a objetos (POO) e permite que uma classe filha herde propriedades e métodos de uma classe pai. Em PHP, a herança é implementada com a palavra-chave extends. Isso promove reutilização de código e estabelece uma relação hierárquica entre classes.
Nesta aula, exploraremos como usar extends, a referência parent para acessar membros da classe pai, a sobrescrita de métodos (override) e boas práticas para decidir quando utilizar herança.
extends
A sintaxe para declarar herança em PHP é simples: após o nome da classe filha, usa-se a palavra-chave extends seguida do nome da classe pai. A classe filha herda todos os métodos e propriedades públicas e protegidas da classe pai (mas não os privados).
Exemplo:
<?php
class Animal {
public $nome;
public function __construct($nome) {
$this->nome = $nome;
}
public function fazerSom() {
return "Som genérico";
}
}
class Cachorro extends Animal {
public function latir() {
return "Au au!";
}
}
$dog = new Cachorro("Rex");
echo $dog->nome; // Rex
echo $dog->fazerSom(); // Som genérico
echo $dog->latir(); // Au au!
?>Neste exemplo, Cachorro herda a propriedade $nome e o método fazerSom() de Animal. Observe que não precisamos redefinir o construtor; ele também é herdado.
parent
A palavra-chave parent é usada para referenciar a classe pai de dentro da classe filha. É especialmente útil quando a classe filha sobrescreve um método, mas ainda deseja chamar a versão da classe pai.
Exemplo:
<?php
class Animal {
public function fazerSom() {
return "Som de animal";
}
}
class Cachorro extends Animal {
public function fazerSom() {
return parent::fazerSom() . " e late";
}
}
$dog = new Cachorro();
echo $dog->fazerSom(); // Som de animal e late
?>Também podemos usar parent no construtor para reaproveitar a lógica de inicialização da classe pai:
<?php
class Animal {
public $nome;
public function __construct($nome) {
$this->nome = $nome;
}
}
class Cachorro extends Animal {
public $raca;
public function __construct($nome, $raca) {
parent::__construct($nome);
$this->raca = $raca;
}
}
$dog = new Cachorro("Rex", "Labrador");
echo $dog->nome; // Rex
echo $dog->raca; // Labrador
?>Sobrescrita
A sobrescrita (override) ocorre quando a classe filha redefine um método ou propriedade herdada da classe pai. O método na classe filha deve ter a mesma assinatura (nome e parâmetros) que o método na classe pai. O PHP permite alterar a visibilidade para torná-la mais permissiva (por exemplo, de protected para public), mas não mais restritiva.
Exemplo:
<?php
class Animal {
protected function fazerSom() {
return "Som genérico";
}
}
class Cachorro extends Animal {
public function fazerSom() {
return "Au au!";
}
}
$dog = new Cachorro();
echo $dog->fazerSom(); // Au au!
?>Note que a visibilidade do método em Cachorro foi alterada de protected para public, o que é permitido. Se tentássemos o contrário (de public para protected), ocorreria um erro fatal.
Para evitar sobrescrita acidental, podemos usar a palavra-chave final no método da classe pai. Métodos final não podem ser sobrescritos.
<?php
class Animal {
final public function fazerSom() {
return "Som fixo";
}
}
class Cachorro extends Animal {
// Erro: Cannot override final method
public function fazerSom() {
return "Au au!";
}
}
?>Quando usar herança
Herança deve ser usada quando existe uma relação clara do tipo "é um" (is-a). Por exemplo, um cachorro é um animal, um carro é um veículo. Evite usar herança apenas para reutilizar código, pois isso pode levar a hierarquias profundas e frágeis. Prefira composição quando a relação for "tem um" (has-a).
Boas práticas:
- Mantenha a hierarquia rasa (poucos níveis).
- Use classes abstratas para definir contratos comuns.
- Evite herdar de classes concretas que não foram projetadas para herança.
- Considere usar interfaces ou traits quando a herança não for a melhor opção.
Exemplo de quando usar herança: uma classe Funcionario e Gerente que estende Funcionario. Um gerente é um funcionário com atributos adicionais.
<?php
class Funcionario {
protected $nome;
protected $salario;
public function __construct($nome, $salario) {
$this->nome = $nome;
$this->salario = $salario;
}
public function getSalario() {
return $this->salario;
}
}
class Gerente extends Funcionario {
private $bonus;
public function __construct($nome, $salario, $bonus) {
parent::__construct($nome, $salario);
$this->bonus = $bonus;
}
public function getSalario() {
return parent::getSalario() + $this->bonus;
}
}
$gerente = new Gerente("João", 5000, 1000);
echo $gerente->getSalario(); // 6000
?>Boas práticas e observações finais
Sempre que sobrescrever um método, considere se precisa chamar o método pai usando parent::. Isso evita duplicação de lógica e mantém a consistência. Lembre-se de que o PHP suporta herança simples (uma classe só pode estender uma classe pai), mas você pode implementar múltiplas interfaces. Use final em classes ou métodos que não devem ser estendidos ou sobrescritos.
Referências
- PHP Manual: Herança
- PHP Manual: parent
- PHP Manual: Final
- PHP Manual: Classes abstratas
- PHP Manual: Traits
Exercícios
- Crie uma classe base
Veiculocom propriedadesmarcaemodelo, e um métodoexibirDetalhes()que retorna uma string com essas informações. Em seguida, crie uma classeCarroque estendeVeiculoe adiciona a propriedadeportas. Sobrescreva o métodoexibirDetalhes()para incluir o número de portas. - Explique por que o código abaixo gera um erro e como corrigi-lo:
- Crie uma classe abstrata
Formacom um método abstratocalcularArea(). Depois crie duas classes concretasQuadradoeCirculoque estendemFormae implementam o método. - Qual a diferença entre usar
parent::e$this->ao chamar um método sobrescrito? Dê um exemplo. - Refatore o código abaixo para usar herança corretamente, evitando duplicação. Crie uma classe base
Animale duas classes filhasGatoePeixe.
<?php
class Veiculo {
protected $marca;
protected $modelo;
public function __construct($marca, $modelo) {
$this->marca = $marca;
$this->modelo = $modelo;
}
public function exibirDetalhes() {
return "Marca: $this->marca, Modelo: $this->modelo";
}
}
class Carro extends Veiculo {
private $portas;
public function __construct($marca, $modelo, $portas) {
parent::__construct($marca, $modelo);
$this->portas = $portas;
}
public function exibirDetalhes() {
return parent::exibirDetalhes() . ", Portas: $this->portas";
}
}
$carro = new Carro("Toyota", "Corolla", 4);
echo $carro->exibirDetalhes();
?><?php
class A {
private function metodo() { }
}
class B extends A {
public function metodo() { }
}
?>protected ou public na classe A se desejar que ele seja herdado e sobrescrito.<?php
abstract class Forma {
abstract public function calcularArea();
}
class Quadrado extends Forma {
private $lado;
public function __construct($lado) {
$this->lado = $lado;
}
public function calcularArea() {
return $this->lado * $this->lado;
}
}
class Circulo extends Forma {
private $raio;
public function __construct($raio) {
$this->raio = $raio;
}
public function calcularArea() {
return pi() * $this->raio * $this->raio;
}
}
$quadrado = new Quadrado(4);
echo $quadrado->calcularArea(); // 16
$circulo = new Circulo(3);
echo $circulo->calcularArea(); // 28.274333882308
?>parent:: chama o método da classe pai diretamente, ignorando a sobrescrita na classe filha. $this-> chama o método da instância atual, que pode ser o sobrescrito. Exemplo: parent::fazerSom() chama o método da classe pai, enquanto $this->fazerSom() chama o método da classe filha (se sobrescrito).<?php
class Gato {
public $nome;
public function __construct($nome) { $this->nome = $nome; }
public function mover() { return "Anda"; }
}
class Peixe {
public $nome;
public function __construct($nome) { $this->nome = $nome; }
public function mover() { return "Nada"; }
}
?><?php
class Animal {
protected $nome;
public function __construct($nome) {
$this->nome = $nome;
}
public function mover() {
return "Se move";
}
}
class Gato extends Animal {
public function mover() {
return "Anda";
}
}
class Peixe extends Animal {
public function mover() {
return "Nada";
}
}
$gato = new Gato("Felix");
echo $gato->mover(); // Anda
$peixe = new Peixe("Nemo");
echo $peixe->mover(); // Nada
?>