Traits vs Herança
Parte da seção Programação Orientada a Objetos do Journey de PHP da Coddy — lição 55 de 91.
Agora que você entende como os traits funcionam, vamos esclarecer quando usar traits em vez de herança. Ambos permitem a reutilização de código, mas servem a propósitos diferentes e possuem características distintas.
Herança estabelece uma relação "é-um". Uma classe Dog que estende Animal significa que um cachorro é um animal. A classe filha herda a identidade da classe pai e pode ser usada em qualquer lugar onde o tipo pai é esperado.
Traits fornecem uma capacidade "tem-um" (has-a). Quando uma classe usa uma trait Loggable, ela tem funcionalidade de log - mas ela não se torna um logger. Traits adicionam comportamento sem mudar o que a classe fundamentalmente é.
<?php
// Herança: Dog É um Animal
class Animal {
public function breathe(): void {
echo "Breathing";
}
}
class Dog extends Animal {
public function bark(): void {
echo "Woof!";
}
}
// Trait: Dog TEM capacidade de log
trait Loggable {
public function log(string $msg): void {
echo "Log: $msg";
}
}
class Cat extends Animal {
use Loggable;
}
Principais diferenças para lembrar:
| Aspecto | Herança | Traits |
|---|---|---|
| Relacionamento | é-um (is-a) | tem-uma capacidade (has-a) |
| Limite | Apenas pai único | Múltiplas traits permitidas |
| Verificação de tipo | Funciona com instanceof | Sem identidade de tipo |
| Melhor para | Relacionamentos hierárquicos | Comportamentos compartilhados entre classes não relacionadas |
Use herança quando as classes compartilham uma identidade comum. Use traits quando classes não relacionadas precisam da mesma funcionalidade sem fazer parte da mesma árvore genealógica.
Desafio
FácilVamos construir um sistema de veículos que demonstra quando usar herança versus traits. Você criará uma hierarquia de veículos que compartilham uma identidade comum por meio da herança, enquanto adiciona capacidades transversais por meio de traits.
Você organizará seu código em quatro arquivos:
Trackable.php— Crie uma trait chamadaTrackableque fornece capacidade de rastreamento por GPS. A trait deve ter:- Uma propriedade privada
$location(string) - Um método
setLocation(string $location)que armazena a localização atual - Um método
getLocation()que retorna"Location: " . $this->location
- Uma propriedade privada
Vehicle.php— Crie uma classe baseVehicleque estabelece a relação "é um" (is-a). Inclua o arquivo da trait. Use a promoção de construtor para definir uma propriedade protegida$brand. Adicione um métododescribe()que retorna"Vehicle: " . $this->brand. Esta classe deve usar a traitTrackable, já que todos os veículos em nossa frota terão GPS.Car.php— Crie uma classeCarque estendeVehicle. Inclua o arquivo Vehicle. Use a promoção de construtor para aceitar a marca e uma propriedade privada$doors(inteiro). Chame o construtor pai com a marca. Sobrescreva o métododescribe()para retornar"Car: " . $this->brand . " with " . $this->doors . " doors".main.php— Inclua o arquivo Car. Você receberá três entradas: um nome de marca, um número de portas e uma string de localização. Crie uma instância deCar, defina sua localização e, em seguida, imprima duas linhas:- O resultado da chamada de
describe() - O resultado da chamada de
getLocation()
- O resultado da chamada de
Observe como a herança e as traits trabalham juntas aqui: Car é um Vehicle (a herança estabelece a identidade), enquanto Vehicle tem capacidade de rastreamento (a trait adiciona comportamento). Você poderia usar instanceof Vehicle para verificar se algo é um veículo, mas não há um tipo "Trackable" para verificar — essa é a principal diferença entre essas duas abordagens.
Folha de consulta
Use herança para relacionamentos "é um" e traits para capacidades "tem um".
A Herança estabelece identidade - uma classe filha é um tipo de classe pai e pode ser usada em qualquer lugar onde o tipo pai é esperado.
As Traits adicionam comportamento sem alterar a identidade da classe - uma classe tem funcionalidade, mas não se torna aquela coisa.
<?php
// Herança: Dog É UM Animal
class Animal {
public function breathe(): void {
echo "Breathing";
}
}
class Dog extends Animal {
public function bark(): void {
echo "Woof!";
}
}
// Trait: Cat TEM capacidade de log
trait Loggable {
public function log(string $msg): void {
echo "Log: $msg";
}
}
class Cat extends Animal {
use Loggable;
}
| Aspecto | Herança | Traits |
|---|---|---|
| Relacionamento | é-um | tem-uma capacidade |
| Limite | Apenas um pai | Múltiplas traits permitidas |
| Verificação de tipo | Funciona com instanceof | Sem identidade de tipo |
| Melhor para | Relacionamentos hierárquicos | Comportamentos compartilhados entre classes não relacionadas |
Use herança para relacionamentos hierárquicos onde as classes compartilham uma identidade comum. Use traits para comportamentos compartilhados entre classes não relacionadas.
Experimente você mesmo
<?php
// Arquivo principal - criar e testar a instância de Car
// TODO: Incluir o arquivo Car.php
// Ler entradas
$brand = trim(fgets(STDIN));
$doors = intval(trim(fgets(STDIN)));
$location = trim(fgets(STDIN));
// TODO: Criar uma instância de Car com marca (brand) e portas (doors)
// TODO: Definir a localização do carro
// TODO: Imprimir o resultado de describe()
// TODO: Imprimir o resultado de getLocation()
?>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