Desafio PHP
Registro de classes com fábrica e contador
Enunciado
Crie uma classe abstrata Animal com uma propriedade estática $total e um método estático criar que recebe um array com tipo e outros dados. O método deve usar static:: para instanciar a classe correta (Cachorro ou Gato) com base no tipo. Cada classe filha deve ter seu próprio construtor. O contador $total deve ser incrementado a cada criação. Exiba o total e os dados dos animais criados.
Requisitos
Animaldeve ser abstrata e terpublic static int $total = 0.- O método
criardeve usarstatic::e uma estrutura de decisão para escolher a subclasse. - As classes
CachorroeGatodevem estenderAnimale ter propriedades específicas. - O contador deve ser incrementado no construtor de
Animalou de cada subclasse.
Código inicial
<?php
abstract class Animal
{
public static int $total = 0;
public function __construct(public readonly string $nome) {
// TODO: incremente o contador
}
// TODO: implemente o método estático criar
}
class Cachorro extends Animal
{
// TODO: propriedade $raca e construtor
}
class Gato extends Animal
{
// TODO: propriedade $cor e construtor
}
// TODO: use Animal::criar para criar animais e exiba os dados
Saída esperada
Cachorro: Rex (Labrador)
Gato: Mimi (Branco)
Total de animais: 2
Ver dica
No método criar, verifique $dados['tipo'] e retorne new Cachorro(...) ou new Gato(...). Use static:: para permitir que subclasses estendam a fábrica. O contador pode ser incrementado no construtor de Animal.
Mostrar solução
<?php
abstract class Animal
{
public static int $total = 0;
public function __construct(public readonly string $nome)
{
self::$total++; // incrementa o contador global
}
// Fábrica estática que escolhe a subclasse com base no tipo
public static function criar(array $dados): static
{
return match ($dados['tipo']) {
'cachorro' => new Cachorro($dados['nome'], $dados['raca']),
'gato' => new Gato($dados['nome'], $dados['cor']),
default => throw new InvalidArgumentException('Tipo desconhecido'),
};
}
abstract public function exibir(): string;
}
class Cachorro extends Animal
{
public function __construct(string $nome, public readonly string $raca)
{
parent::__construct($nome);
}
public function exibir(): string
{
return "Cachorro: {$this->nome} ({$this->raca})";
}
}
class Gato extends Animal
{
public function __construct(string $nome, public readonly string $cor)
{
parent::__construct($nome);
}
public function exibir(): string
{
return "Gato: {$this->nome} ({$this->cor})";
}
}
// Criando animais via fábrica
$cachorro = Animal::criar(['tipo' => 'cachorro', 'nome' => 'Rex', 'raca' => 'Labrador']);
$gato = Animal::criar(['tipo' => 'gato', 'nome' => 'Mimi', 'cor' => 'Branco']);
echo $cachorro->exibir() . PHP_EOL;
echo $gato->exibir() . PHP_EOL;
echo 'Total de animais: ' . Animal::$total . PHP_EOL;
Passo a passo
Animalé abstrata e definepublic static int $total = 0para contar todas as instâncias.- O construtor de
Animalrecebe$nomee incrementaself::$total. Como as subclasses chamamparent::__construct, o contador é atualizado. - O método estático
criarusamatchpara decidir qual subclasse instanciar com base em$dados['tipo']. Retornastaticpara permitir que subclasses reutilizem a fábrica. CachorroeGatoestendemAnimal, adicionam propriedades específicas e implementamexibir.- Chamamos
Animal::criarduas vezes e exibimos os dados e o total.
Por que funciona
O late static binding em static:: permite que o método criar seja herdado e ainda assim retorne o tipo correto. O contador estático em Animal é compartilhado por todas as subclasses, pois é definido na classe base. Assim, cada criação incrementa o mesmo contador.
Erros comuns
- Usar
selfno retorno decriar:return new self(...)não funcionaria porqueAnimalé abstrata. Além disso,staticé necessário para que subclasses possam sobrescrever a fábrica. - Esquecer de chamar
parent::__construct: o contador não seria incrementado e$nomenão seria definido. - Não tratar tipo desconhecido: o
matchsemdefaultlançariaUnhandledMatchError. Usamosthrowpara dar uma mensagem clara.
Outra forma de resolver
Você pode usar um método estático em cada subclasse em vez de uma fábrica central. Por exemplo, Cachorro::criar($dados) e Gato::criar($dados). A fábrica central é útil quando o tipo é dinâmico e você quer um ponto único de criação.
Saída esperada
Cachorro: Rex (Labrador)
Gato: Mimi (Branco)
Total de animais: 2