giovedì 28 novembre 2013

La OOP in PHP spiegata in un esempio - Parte 3 - Interfacce predefinite e costrutto $this


Affrontate le classi più semplici, passiamo a quelle un po' meno banali. Nel primo post della seria abbiamo parlato di figure. Una figura deve essere rappresentata in un'area di disegno laddove un'area di disegno è in grado di rappresentare un punto e un segmento. Abbiamo quindi due oggetti in cui uno ha bisogno di un'altro per poter funzionare

Iniziamo con il dichiarare le interfacce di questi due elementi:

<?php
interface iAreaDiDisegno {
    public function tracciaPunto(\iCoordinate $oCoordinate,\iColore $oColore);
    public function tracciaSegmento(\iCoordinate $oCoordinateA,\iCoordinate $oCoordinateB,\iColore $oColore);
}
?>

<?php
interface iFigura{
    public function disegna(\iAreaDiDisegno $oAreaDiDisegno);
}
?>

Come possiamo vedere, una classe che vuole essere un'area di disegno deve saper tracciare al suo interno un punto e un segmento tramite i due appositi metodi. Una classe che vuole essere una figura deve saper disegnare la propria immagine all'interno di un oggetto area di disegno tramite i metodi messi a disposizione dalla interfaccia iAreaDiDisegno.

Tra le figure geometriche abbiamo i poligoni, quindi possiamo definire una classe capace di implementare tali figure: Un poligono non è altro che una serie di vertici ordinati, collegati da segmenti. Dato che i vertici sono ordinati, possono essere trattati come un array. Quindi per comodità la classe cPoligono implementerà l'interfaccia Iterator. L'interfaccia Iterator è un'interfaccia predefinita in PHP, che consente di trattare un oggetto come se fosse un vettore nei cicli foreach. Un poligono ha anche un colore, quindi può implementare l'interfaccia iColore. Vediamo quale possa essere l'interfaccia di tale classe:

<?php

require_once './iFigura.php';
require_once './iColore.php';

class cPoligono implements iFigura, Iterator, iColore {

    private $vertici = [];
    private $oColore;

    public function __construct(\iColore $oColore) {}
    public function setColore(\iColore $oColore) {}
    public function getColore() {}
    public function setVertice(\iCoordinate $oCoordinate) {}
    public function getVertice($numero) {}

    //Interfaccia iFigura
    public function disegna(\iAreaDiDisegno $oAreaDiDisegno) {}

    //Interfaccia Iterator
    public function current() {}
    public function key() {}
    public function next() {}
    public function rewind() {}
    public function valid() {}

    //Interfaccia iColore
    public function setRGB($r, $g, $b) {}
    public function setRGBArray($RGB){}
    public function getRGBArray(){}
    public function setR($r){}
    public function setG($g){}
    public function setB($b){}
    public function getR(){}
    public function getG(){}
    public function getB(){}
}

?>

Completando la classe con il codice dei metodi otteniamo:

<?php

require_once './iFigura.php';
require_once './iColore.php';

class cPoligono implements iFigura, Iterator, iColore {    

    private $oColore, $vertici = [];

    public function __construct(\iColore $oColore) {
        $this->setColore($oColore);
    }


    public function setColore(\iColore $oColore) {
        $this->oColore = $oColore;
    }

    public function getColore() {
        return $this->oColore;
    }    


    public function setVertice(\iCoordinate $oCoordinate) {
        $this->vertici[] = $oCoordinate;
    }

    public function getVertice($numero) {
        if ($numero < count($this->vertici) and $numero >= 0)
            return $this->vertici[$numero];
        else
            throw new Exception("Si cerca di accedere ad un vertice non disponibile");
    }

    //Interfaccia iFigura
    public function disegna(\iAreaDiDisegno $oAreaDiDisegno) {
        $ultimo = $primo = null;
        foreach ($this as $punto)
            if (!is_null($ultimo)) {
                $oAreaDiDisegno->tracciaSegmento($ultimo, $punto, $this->getColore());
                $ultimo = $punto;
            }
            else
                $primo = $ultimo = $punto;
        $oAreaDiDisegno->tracciaSegmento($primo, $ultimo, $this->getColore());
    }

    //Interfaccia Iterator
    public function current() {
        return current($this->vertici);
    }

    public function key() {
        return key($this->vertici);
    }

    public function next() {
        return next($this->vertici);
    }

    public function rewind() {
        return reset($this->vertici);
    }

    public function valid() {
        return current($this->vertici) !== false;
    }


    //Implementazione dell'interfaccia iColore   
    public function setRGB($r, $g, $b) {
        $this->getColore()->setRGB($r, $g, $b);
    }

    public function setRGBArray($RGB) {
        $this->getColore()->setRGBArray($RGB);
    }

    public function getRGBArray() {
        return $this->getColore()->getRGBArray();
    }

    public function setR($r) {
        $this->getColore()->setR($r);
    }

    public function setG($g) {
        $this->getColore()->setG($g);
    }

    public function setB($b) {
        $this->getColore()->setB($b);
    }

    public function getR() {
        return $this->getColore()->getR();
    }

    public function getG() {
        return $this->getColore()->getG();
    }

    public function getB() {
        return $this->getColore()->getB();
    } 


}

?>

I metodi sono tutti molto banali. L'unico degno di nota è disegna(). Tale funzione non fa altro che disegnare i vari segmenti che compongono il poligono. Si osservi come al posto dell'array, nel ciclo foreach, sia stato utilizzato $this, ossia l'oggetto stesso, che è quindi trattato come un array tramite i metodi dell'interfaccia Iterator implementati.