Constructores Semánticos / Named Constructors en PHP

Constructores Semánticos / Named Constructors en PHP

Constructores semánticos ó named constructors es un pequeño patrón de refactoring que nos permite eliminar complejidad de los constructores cuando estos implican conocer detalles a fondo de la implementación del mismo.

Tenemos el siguiente ejemplo:

class Price
{
    private $amount;
    private $currency;

    public function __construct(float $amount, string $currency)
    {
        $this->checkAmount($amount);
        $this->checkCurrency($currency);
        $this->amount = $amount;
        $this->currency = $currency;
    }

    private function checkAmount(float $amount)
    {
        if (0 >= $amount) {
            throw new RuntimeException();
        }
    }

    private function checkCurrency(string $currency)
    {
        $allowedCurrencies = ['EUR', 'USD', 'GBP'];

        if (!in_array($currency, $allowedCurrencies)) {
            throw new RuntimeException();
        }
    }

    public function amount() : float
    {
        return $this->amount;
    }

    public function currency() : string
    {
        return $this->currency;
    }
}

En este ejemplo podemos ver rápidamente un pequeño “*Value Object*” que representaría un precio. El value object se compone de dos atributos, la cantidad y la divisa a usar.

Más adelante en otro post estaré ampliando un poco más sobre lo que son los value objects ó objetos de valor.

Tal cual está diseñado, este value object nos obliga a conocer detalles de su implementación, como por ejemplo qué divisas soparte y cuáles no.

Implementación de Constructores Semánticos ó Named Constructors

Para aplicar el refactoring de los constructores semánticos ó named constructors hay que seguir unos pocos pasos:

  • Convertir el constructor en privado

  • Crear uno o varios métodos estáticos que usando el constructor de la forma adecuada, devuelvan un nuevo objeto.

  • Darle a los nuevos métodos estáticos un nombre que describa su intencionalidad (Aquí entra la parte semántica).

  • Sustituir las antiguas llamas al constructor por llamadas a los nuevos métodos estáticos

Veamos un ejemplo de este refactor:

<?php

class Price
{
    private $amount;
    private $currency;

    public function __construct(float $amount, string $currency)
    {
        $this->checkAmount($amount);
        $this->checkCurrency($currency);
        $this->amount = $amount;
        $this->currency = $currency;
    }

    private function checkAmount(float $amount)
    {
        if (0 >= $amount) {
            throw new RuntimeException();
        }
    }

    private function checkCurrency(string $currency)
    {
        $allowedCurrencies = ['EUR', 'USD', 'GBP'];

        if (!in_array($currency, $allowedCurrencies)) {
            throw new RuntimeException();
        }
    }

    public static function createPricePounds($amount)
    {
        return new self($amount, 'GBP');
    }

    public static function createPriceInDollars($amount)
    {
        return new self($amount, 'USD');
    }

    public static function createPriceInEuros($amount)
    {
        return new self($amount, 'EUR');
    }

    public function amount() : float
    {
        return $this->amount;
    }

    public function currency() : string
    {
        return $this->currency;
    }
}

Como se puede observar, hemos definido el constructor como privado y añadido 3 nuevos métodos:

  • createPriceInDollars

  • createPriceInPounds

  • createPriceInEuros

Y usarlos es tan sencillo como:

$poundsPrice = Price::createPriceInPounds(22.5);
$dollarsPrice = Price::createPriceInDollars(22.5);
$eurosPrice = Price::createPriceInEuros(22.5);

Ahora, la interfaz pública del value object expone las distintas formas de crear un precio que soporta, con lo que el programador no debe de conocer detalles internos de su implementación.

Consideraciones Finales

Esto no deja de ser una variante de implementación del patrón Factory Method, en la que el método constructor se implementa en la misma clase y por tanto, ha de ser estático.

Hay otro caso de uso en el que los constructores semánticos ó named constructors son de utilidad, y es cuando deseamos simular la sobrecarga del constructor como se hace en JAVA por ejemplo u otros lenguajes que lo permiten, pero que en PHP no esta disponible incluso en la versión 7.3 de PHP.

La solución pasa por crear nuevos constructores semánticos que nos permitan implementar diferentes constructores con diferentes parámetros.

Aprovecho también para mencionar que en la próxima versión de PHP, la versión 7.4 que esté próxima por ser liberada, han incluido los incluidos las contravariación de argumentos como algo nativo.

Esto permitiría sobre escribir métodos de una clase, cambiando los tipos de argumentos que recibe, puedes conocer más acerca de esto en **este post**.

Esto último pudiese ser una variante también como algo adicional a los constructores semánticos ó named constructors.

Recuerda que si tienes alguna sugerencia o pregunta, no dudes en dejar tus comentarios al final del post.

Si te gustó este post, ayúdame a que pueda servirle a muchas más personas, compartiendo mis contenidos en tus redes sociales.

Espero que este post haya sido de gran ayuda para ti, y como siempre, cualquier inquietud o duda que tengas, puedes contactarme por cualquiera de las vías disponibles, o dejando tus comentarios al final de este post. También puedes sugerir que temas o post te gustaría leer a futuro.

Did you find this article valuable?

Support Francisco Ugalde by becoming a sponsor. Any amount is appreciated!