Costruttori multipli in PHP

15 luglio 2016

Linguaggi di programmazione con un supporto più avanzato per la programmazione ad oggetti (come ad esempio Java) permettono alle classi di avere costruttori multipli con sequenze diverse di parametri. In questo modo, ad esempio, una classe per rappresentare il tempo può essere istanziata passando come parametro il timestamp oppure una stringa rappresentante una data oppure i parametri singoli per giorno, mese e anno. Questa caratteristica, che si chiama method overloading, è molto potente e permette allo sviluppatore una grande flessibilità nell’utilizzo delle proprie classi.

PHP purtroppo non supporta nativamente i costruttori multipli nelle sue classi: come abbiamo visto ogni classe può avere un unico metodo costruttore chiamato __constructor(). Per fortuna è possibile aggirare questa limitazione in almeno tre diverse maniere.

Soluzioni per il supporto dei costruttori multipli

La prima possibilità consiste nell’impostare dei parametri come facoltativi o gestirli dal costruttore senza dichiararli tramite le funzioni func_num_args(), func_get_args() e func_get_arg() con dei costrutti if/else. Un esempio può essere il seguente:

<?php

class MyTime
{
    private $timestamp;

    public function __construct()
    {
        if (0 === func_num_args()) { // costruttore senza parametri
            $this->timestamp = time();
        } else if (1 === func_num_args()) {
            if (is_numeric(func_get_arg(0))) { // costruttore con il timestamp
                $this->timestamp = func_get_arg(0);
            } else { // costruttore con giorno, mese e anno
                $date = new DateTime(func_get_arg(0));
                $this->timestamp = $date->getTimestamp();
            }
        } else if (3 === func_num_args()) { // costruttore con giorno, mese e anno
            $this->timestamp = mktime(0, 0, 0, func_get_arg(1), func_get_arg(0), func_get_arg(2));
        }

        if (null === $this->timestamp) { // gestione errori per costruttore non supportato
            throw new InvalidArgumentException('Parametri non supportati per il costruttore');
        }
    }

    /**
     *  Metodi che eseguono azioni
     */
}

$data1 = new MyTime();             // timestamp impostato al momento corrente
$data2 = new MyTime(1457524800);   // timestamp impostato al 9 marzo 2016
$data3 = new MyTime('03/09/2016'); // timestamp impostato al 9 marzo 2016
$data4 = new MyTime(9, 3, 2016);   // timestamp impostato al 9 marzo 2016
$data5 = new MyTime(9, 3);         // errore

Come si può vedere il codice risultante è difficile da leggere e da mantenere, inoltre manca di flessibilità infatti non è possibile, ad esempio, cambiare l’ordine dei parametri per giorno, mese e anno. Una soluzione migliore è l’utilizzo del pattern factory che consiste nel creare ulteriori classi (minimo una) che si occupa di istanziare la classe di nostro interesse. In questo caso la nostra classe MyTime conterrebbe solo un costruttore con unico parametro il timestamp, mentre la classe factory tramite vari metodi può calcolare il timestamp con cui istanziarla.

Il vantaggio evidente è nella flessibilità e leggibilità: basta aggiungere un metodo alla classe factory per poter istanziare la classe MyTime con parametri diversi. Questo pattern è molto potente e permette anche di sfruttare oggetti terzi per istanziare la classe di sua competenza (ad esempio per fare query ad un database o per calcoli complessi). Per tale ragione in questo caso specifico si tratterebbe di una soluzione sovradimensionata che comporta lo svantaggio di dover mantenere due classi diverse.

PHP ci offre una terza via che permette di avere i vantaggi di entrambi i metodi: dichiarare il costruttore come privato senza un corpo e usare metodi statici per istanziare la classe. Sempre relativamente al nostro esempio potremmo scrivere un codice simile a questo:

<?php

class MyTime
{
    private $timestamp;

    private function __construct()
    {
    }

    public static function now()
    {
        $time = new static();
        $time->timestamp = time();

        return $time;
    }


    public static function fromTimestamp($timestamp)
    {
        $time = new static();
        $time->timestamp = $timestamp;

        return $time;
    }

    public static function fromString($string)
    {
        $date = new DateTime($string);

        $time = new static();
        $time->timestamp = $date->getTimestamp();

        return $time;
    }

    public static function fromParts($day, $month, $year)
    {
        $time = new static();
        $time->timestamp = mktime(0, 0, 0, $month, $day, $year);

        return $time;
    }

    public static function fromPartsAmericanFormat($month, $day, $year)
    {
        $time = new static();
        $time->timestamp = mktime(0, 0, 0, $month, $day, $year);

        return $time;
    }


    /**
     *  Metodi che eseguono azioni
     */
}

$data1 = MyTime::now();                               // timestamp impostato al momento corrente
$data2 = MyTime::fromTimestamp(1457524800);           // timestamp impostato al 9 marzo 2016
$data3 = MyTime::fromString('03/09/2016');            // timestamp impostato al 9 marzo 2016
$data4 = MyTime::fromParts(9, 3, 2016);               // timestamp impostato al 9 marzo 2016
$data5 = MyTime::fromPartsAmericanFormat(3, 9, 2016); // timestamp impostato al 9 marzo 2016

Da notare che all’interno dei metodi statici viene impostato un attributo privato del costruttore, una cosa che non sarebbe possibile normalmente. PHP permette questa modifica perché un oggetto può modificare qualsiasi attributo, anche quelli privati, di oggetti istanziati a partire dalla propria classe.

Un altro dettaglio da notare è il modo in cui la classe viene istanziata all’interno dei metodi statici, infatti la scelta di usare static non è casuale. L’utilizzo di static (disponibile dalla versione 5.3 di PHP) si riferisce alla classe che viene richiamata dall’esterno, per cui il suo impiego rende disponibili i metodi statici anche in classi che estendono MyTime. Usando self, invece, le chiamate ai metodi statici restituirebbero sempre un’istanza di MyTime, anche se in realtà li abbiamo richiamati da una classe figlia.

Tutte le lezioni

1 ... 4 5 6 ... 50

Se vuoi aggiornamenti su Costruttori multipli in PHP inserisci la tua e-mail nel box qui sotto:
 
X
Se vuoi aggiornamenti su Costruttori multipli in PHP

inserisci la tua e-mail nel box qui sotto:

Ho letto e acconsento l'informativa sulla privacy

Acconsento al trattamento dei dati per attività di marketing