Utilizzare una proprietà pubblica è una cattiva pratica?

Perchè mai l’utilizzo di una proprietà pubblica dovrebbe essere una cattiva pratica? Semplice, per via dello scarso controllo sui dati inseriti! Questa è la miglior spiegazione che mi viene in mente per considerare questa pratica,
“cattiva”.

In realtà, personalmente, non vedo nulla di particolarmente problematico. Il problema è, che uso si fa di questa possibilità?

Come dicevo, uno dei maggiori inconvenienti è la possibilità di inserire qualsiasi tipo di dato all’interno della proprietà, magari incompatibile con le operazioni svolte dentro la classe, di tipi diverso rispetto a ciò che è necessario oppure di tipo corretto (numero, stringa, array, ecc…) ma con valori al di fuori di ciò che la classe gestisce (es.: valori numerici troppo alti o troppo bassi, stringhe troppo corte o troppo lunge, ecc…).

Facciamo un esempio :

class cl_a {
  public $dt1;
  public $dt2;
  public function somma() {
    echo("La somma di ".$this->dt1." e di ".$this->dt2." è pari a ".
         ($this->dt1+$this->dt2));
  }
}
$obj = new cl_a();
$obj->dt1 = 13; 
$obj->dt2 = 5; 
$obj->somma();
// l'output :
// La somma di 13 e di 5 è pari a 18
$obj->dt1 = 13; 
$obj->dt2 = array("test"); 
$obj->somma(); //errore!
// l'output produrrebbe un "simpatico" errore fatale :
// Fatal error: Unsupported operand types in c:...test.php on line X

Ovviamente ciò è vero se il controllo non avviene mai(come nell’esempio). Però se i dati vengono controllati prima di passarli alla classe, questo problema, di fatto, è inesistente.

Agli inizio (sia con PHP che con altri linguaggi), tendevo ad affrontare la situazione cercando di inserire, nelle varie classi, metodi per il controllo dinamico dei dati. In questo modo si riusciva a prevenire e a controllare gli eventuali dati non corretti. Tanto per fare un’esempio, riprendiamo quanto sopra, e modifichiamolo aggiungendo un controllo :

class cl_a {
  private $dt1;
  private $dt2;
  public function set_dt1($valore)
  {
    if (is_numeric($valore))
      $this->dt1 = $valore;
    else {
      echo("Attenzione : Il metodo set_dt1 della classe cl_a richiede un valore numerico!");
      $this->dt1 = 0;
    }
  }
  public function set_dt2($valore)
  {
    if (is_numeric($valore))
      $this->dt2 = $valore;
    else {
      echo("Attenzione : Il metodo set_dt2 della classe cl_a richiede un valore numerico!");
      $this->dt2 = 0;
    }
  }
  public function somma() {
    echo("La somma di ".$this->dt1." e di ".$this->dt2." è pari a ".
         ($this->dt1+$this->dt2));
  }
}
$obj = new cl_a();

$obj->set_dt1(13);
$obj->set_dt2(5);
$obj->somma();
// l'output :
// La somma di 13 e di 5 è pari a 18
$obj->set_dt1(13);
$obj->set_dt2(array("test")); 
$obj->somma(); //errore!
// l'output produrrebbe un "simpatico" messaggio di avvertimento, ma nessun 
// errore fatale :
// Attenzione : Il metodo set_dt2 della classe cl_a richiede un valore numerico!

Questo tipo di approccio, molto semplice, sostituisce l’uso delle proprietà con dei metodi che impostano i dati, controllandone prima la validità. Ovviamente esistono anche altri sistemi tra i quali, quello che preferisco è l’utilizzo dell’intrigante metodo ‘__set’, di cui possiamo trovare i dettagli nel manuale ufficiale :

http://it.php.net/ma … oop5.overloading.php

Ad un certo punto mi sono chiesto, è davvero necessario inserire questo tipo di controlli ? A prima vista sembrerebbe di si, visto che l’utilizzo di dati non validi può generare enormi problemi.

Sicuramente, l’uso intensivo di questo tipo di controlli, posti all’interno di molte classi (mageri centinaia), ha un’impatto negativo sulle prestazioni (soprattutto in linguaggi interpretati come PHP), inoltre complica la scrittura del software, allungandone i tempi di sviluppo e rendendo più difficile un’eventuale rilettura e modifica dei
sorgenti.

Riflettendoci meglio mi sono domandato, da dove arrivano questi dati sbagliati?

Osserviamolo :

Flusso dei dati

Chiaramente il problema sono i dati introdotti, attraverso varie metodologie, “all’interno” del programma. Sono due le fonti di dati, quelli inseriti dall’utente attraverso maschere e moduli e quelle “importate” attraverso procedure di lettura da fonti esterne (es.: file di testo, fonti RSS, database di versioni precedenti del software ecc…).

Bene, visto che la fonte del problema è ben identificabile, perchè dobbiamo riempire le nostre classi di controlli? Non esiste nessuna ragione particolare, sarà sufficiente spostare i controlli dove realmente servono, cioè durante la fase di acquisizione dei dati, in questo modo :

Flusso dei dati Alternativo

Ovviamente, questo spostamento dei controlli comporta anche alcuni svantaggi, il primo dei quali è rappresentato dalla “fragilità” delle classi. Ad esempio, portando una classe in un nuovo progetto, dovremmo assicurarci che i dati che le giungeranno siano sempre corretti.

In ogni caso, questo approccio, mi ha permesso di risparmiare molte linee di codice, rendendo il software più facile da leggere e modificare in quanto ha reso possibile la centralizzazione dei controlli, semplificando così l’implementazione delle singole classi.

Questo discorso, naturalmente, non rappresenta una soluzione adatta a qualsiasi progetto. E’ necessario che il programmatore sappia valutare quale metodologia adottare in base alle esigenze dello sviluppo e alle sue preferenze personali. In senso generale, penso che nessuna pratica sia “cattiva” se dimostra di essere funzionante, efficace e minimamente logica.