Fala mestre,
Antes de assistir essa última aula sobre fail fast eu havia refatorado a minha fábrica de especialidades e de médicos por que notei que podia abstrair algumas coisas com template method, não sei se faz muito sentido após ter visto essa ultima aula, mas está ai para quem tiver curiosidade e ache que faça sentido, muita coisa pode ser melhorada também.
Eu fiz basicamente três coisas:
1 - Criei uma abstract class FabricaDeEntidades para juntar coisas em comum em todas as fábricas. Essa fábrica de entidades recebe dois parâmetros para ser construído:
Primeiro parâmetro: Um objeto que implementa a interface DadosNormalizaveis(nome péssimo), que vai me garantir que os dados passados serão transformado em um array. Assim eu posso receber tanto um json, quanto qualquer outro tipo de dado vindo da web pq sei que será formatado para um array.
Segundo parâmetro: Um array que contém os nomes das propriedades que serão obrigatórios para construção da entidade.
2 - Criei uma interface chamada DadosNormalizaveis que contém o método normalize($dados): array. Esse método deve me garantir que os dados de alguma forma serão transformados em um array e assim posso padronizar a construção de diferentes entidades.
3 - Criei uma trait chamada Entidade Helper que pode possuir alguns métodos auxiliares para a minha fábrica de entidades.
abstract class FabricaDeEntidades
{
use EntidadeHelper;
protected $tipoDeDado;
protected $propriedades;
public function __construct(DadosNormalizaveis $tipoDeDado, array $propriedades)
{
$this->tipoDeDado = $tipoDeDado;
$this->propriedades = $propriedades;
}
public function tipoDeDado(DadosNormalizaveis $tipoDeDado): void
{
$this->tipoDeDado = $tipoDeDado;
}
public function propriedades(array $propriedades): void
{
$this->propriedades = $propriedades;
}
public function criarCom($dados)
{
$dados = $this->tipoDeDado->normalize($dados);
if($erro = $this->naoExistemPropriedades($dados, $this->propriedades)){
$msg = sprintf("Faltam as propriedades: %s", implode(', ', $erro));
throw new EntityFactoryException($msg);
}
return $this->comoCriarEntidade($dados);
}
abstract function comoCriarEntidade(array $dados);
}
trait EntidadeHelper
{
public function naoExistemPropriedades(array $dados, array $propriedades)
{
return array_filter($propriedades, fn($prop) => !isset($dados[$prop]));
}
}
class JsonParaArray implements DadosNormalizaveis
{
public function normalize($data): array
{
return (array) json_decode($data);
}
}
Agora para criar qualquer classe do tipo FabricaDeEntidades preciso somente dizer como o objeto é setado/construido e quais propriedades eu desejo receber nos dados:
class FabricaDeEspecialidades extends FabricaDeEntidades
{
public function __construct(DadosNormalizaveis $tipoDeDado)
{
$propriedades = ['descricao'];
parent::__construct($tipoDeDado, $propriedades);
}
public function comoCriarEntidade(array $dados): Especialidade
{
$especialidade = new Especialidade();
$especialidade->setDescricao($dados['descricao']);
return $especialidade;
}
}