martedì 17 dicembre 2013

La OOP in PHP spiegata in un esempio - Parte 12 - Classi statiche pure


Dal post precedente possiamo osservare una cosa. La classe cCoordinateTrasformabili è una classe strana. Perché? Riguardando il suo codice possiamo notare come non abbia variabili oggetto, se non quelle definite dalla sua genitrice ossia è priva di uno stato proprio. La sua definizione si compone di soli metodi. Tali metodi fanno uso delle variabili oggetto, ma ne sono comunque disgiunti tramite i metodi d'accesso set/get.

In tali casi si potrebbe optare per la creazione di una classe statica pura. La trasformazione è molto semplice e produce:


<?php
require_once './iCoordinate.php';

class cTrasformazioniCoordinate {  
    public static function trasla(\iCoordinate $oCoordinate, $dx, $dy) {
        $dx = (int) $dx;
        $dy = (int) $dy;
        $oCoordinate->setX($oCoordinate->getX()+$dx);
        $oCoordinate->setY($oCoordinate->getY()+$dy);
    }
 
    public static function scala(\iCoordinate $oCoordinate, \iCoordinate $oPuntoDiFuga, $scalaX, $scalaY) {      
        $x = ($oCoordinate->getX()-$oPuntoDiFuga->getX())*$scalaX;
        $y = ($oCoordinate->getY()-$oPuntoDiFuga->getY())*$scalaY;
        $oCoordinate->setX($x+$oPuntoDiFuga->getX());
        $oCoordinate->setY($y+$oPuntoDiFuga->getY());      
    }
 
    public static function ruota(\iCoordinate $oCoordinate, \iCoordinate $oCentroRotazione, $angolo) {
        $oCoordinate->trasla(-$oCentroRotazione->getX(), -$oCentroRotazione->getY());
        $x = $oCoordinate->getX() * cos(deg2rad($angolo)) - $oCoordinate->getY() * sin(deg2rad($angolo));
        $y = $oCoordinate->getY() * cos(deg2rad($angolo)) + $oCoordinate->getX() * sin(deg2rad($angolo));
        $oCoordinate->setX($x);
        $oCoordinate->setY($y);
        $oCoordinate->trasla($oCentroRotazione->getX(), $oCentroRotazione->getY());
    }      
}
?>

In questo modo cPoligono potrebbe, anziché convertire i propri oggetti iCoordinate in oggetti cCoordinateTrasformabili nel metodo setVertice(), utilizzare questi metodi statici richiamabili come cTrasformazioneCoordinate::ruota($oVertice, $oCentrorotazione, $angolo) all'interno del metodo di calcolo.

I due metodi (tramite classe e tramite classe statica) sono assolutamente equivalenti, dato che alla fine l'oggetto su cui agire va comunque comunicato, o perché posto prima del metodo o perché posto come argomento del metodo:

$oggetto->metodo(paramteri);
classeStatica::metodo($oggetto, parametri);

Il secondo metodo si presta al supporto della programmazione modulare, nel senso che possiamo definire un tipo di dato (la classe cCoordinate che definisca i soli metodi d'accesso alle prorpietà/stato) e un insieme di metodi statici (nel caso specifico si tratta metodi di semplice calcolo), tra cui quello che genera l'oggetto cCoordinate, utili a modificare lo stato dell'oggetto stesso. A mio modo di vedere però tale pratica è un passo indietro rispetto alla programmazione a oggetti, in cui le classi definiscono i tipi di dati e gli oggetti lavorano in sinergia per mezzo dell'invocazione dei metodi (detti non a caso anche messaggi) rigorosamente definiti nell'interfaccia stessa della classe tramite il sistema dell'ereditarietà.

D'altra parte, volendo isolare un sistema di classi è possibile creare dei metodi statici o un classe che funga da interfaccia tra il client e il sistema di classi, andando a nascondere la complessità del sistema e isolandolo, di modo che eventuali modifiche al sistema non influenzino il client a patto che la classe interfaccia o quella statica non subisca modifiche ai metodi già esistenti (nome ed elenco dei parametri).
                                       
Le classi statiche pure potrebbero essere utilizzate per implementare delle forme procedurali (un passo indietro rispetto alla OOP) come nel caso della doppia interfaccia di MySQLi. Infatti il driver MySQLi, per la quale è definita l'interfaccia OOP e quella così detta "procedurale", è dotato oltre che della classica classe anche di un insieme di semplici funzioni. Ad ognuna di tali funzioni occorre passare un oggetto MySQLi come argomento. In particola l'interfaccia procedurale non fa altro che utilizzare l'oggetto per invocare l'omonimo metodo dell'oggetto stesso.

$res mysqli_query($mysqli"SELECT 'A world full of ' AS _msg FROM DUAL");
$res $mysqli->query("SELECT 'choices to please everybody.' AS _msg FROM DUAL");
(Esepio tratto dal manuale di PHP.net)

Se avessimo avuto un classe statica, quel mysqli_query() sarebbe stato scritto come sMySQLi::query() o qualcosa di simile. Rifacendosi però alla concezione procedurale, abbiamo la più semplice e familiare funzione mysqli_query().