Contructores más ordenados en PHP (Factory Method)

Seguro que ya has vivido esto:

  • Creas una clase con un constructor que acepta unos parámetros.
  • Te das cuenta que los datos pueden venir de un array y ves que es más cómodo pasar todo el array de golpe.
  • Te das cuenta de que los datos también pueden venir de un objeto.

El problema es que en PHP no tenemos sobrecarga de funciones.

Entonces vas y creas una monstruosidad como ésta… y además sientes orgullo de tu creación:

class Coche
{
    private $marca = '';
    private $modelo = '';

    public function __construct($marca, $modelo, $datos = null)
    {
        if (is_array($datos)) {
            $this->marca = $datos['marca'];
            $this->modelo = $datos['modelo'];
        }
        else if (is_object($datos)) {
            $this->marca = $datos->marca;
            $this->modelo = $datos->modelo;
        }
        else {
            $this->marca = $marca;
            $this->modelo = $modelo;
        }
    }

    public function __toString()
    {
        return $this->marca . " " . $this->modelo;
    }
}

$datos = [
    'marca' => 'Citroen',
    'modelo' => 'C4'
];
$coche = new Coche("", "", $datos);
echo $coche . PHP_EOL; // Muestra "Citroen C4"

Y todavía puede haber variaciones incluso peores como:

public function __construct($marcaODatos, $modelo = '')

Y todavía se podría empeorar. Todos hemos hecho/visto aberraciones así.

¿Y qué tiene esto de malo?

Pues que es una chapuza. Es un constructor basura que hará unas cosas u otras dependiendo de los datos y podemos liarla pero bien.

Entonces ¿hay alguna otra opción?

Sí, podemos usar lo que llaman un Factory Method. Esto viene a ser un método estático que crea un objeto.

Por ejemplo podríamos mejorar el ejemplo anterior con algo así:

class Coche
{
    private $marca = '';
    private $modelo = '';

    public function __construct(string $marca, string $modelo)
    {
        $this->marca = $marca;
        $this->modelo = $modelo;
    }

    public static function desdeArray(array $datos) {
        return new self($datos['marca'], $datos['modelo']);
    }

    public static function desdeObjeto(stdClass $datos) {
        return new self($datos->marca, $datos->modelo);
    }

    public function __toString()
    {
        return $this->marca . " " . $this->modelo;
    }
}

$datos = [
    'marca' => 'Citroen',
    'modelo' => 'C4'
];
$coche = Coche::desdeArray($datos);

echo $coche . PHP_EOL;

Aquí tenemos un único constructor y luego tenemos unos constructores dedicados que nos permiten hacer todo tipo de malabarismos para crear objetos.

Un pasito más

Y ya que estamos podemos usar lo que se llaman Value Objects para los parámetros del constructor. Otro día escribiré sobre los Value Objects, mientras puedes ir leyendo un artículo sobre cómo asegurarnos de que una función recibe los parámetros correctos.

Me siento solo, dime algo...