Newsgroup: it.comp.www.php
Titolo: php 5.3, classi astratte e intefacce

Ciao a tutti, ho un dubbio sull'utilizzo di classi astratte e
interfacce.
Sto seguendo un libro preso da poco che si chiama php 6 guida per lo
sviluppatore e non vorrei ci fosse qualche errore di traduzione o
magari gli esempi sono realizzati con una versione precedente alla
5.3.
 
La guida mi propone un esempio che ora andrò a semplificare:
 
------------------------------------------------------
interface.Validator.php
 
interface Validator {
  public function validate();
}
 
E fin qui nulla di strano
------------------------------------------------------
class.PropertyObject.php
 
require_once('interface.Validator.php');
  abstract class PropertyObject implements Validator {
    ......
    public function validate() {}
    /*
    In questo modo rispetto l'interfaccia e implemento il metodo
validate ma il metodo è vuoto!
    */
......
  }
------------------------------------------------------
class.Address.php
  require_once('class.PropertyObject.php');
 
  class Address extends PropertyObject {
.....
function validate() {
      echo 'ho validato';
    }
......
}
------------------------------------------------------
 
Ora vi cito quello che dice il libro è che non mi torna:
La classe PropertyObject è dichiarata abstract per due motivi. Primo,
la classe PropertyObject da sola non è molto utile. Le classi che
estendono PropertyObject hanno ancora del lavoro da svolgere prima di
poter utilizzare i suoi metodi.Secondo, non è stata fornita
un'implementazione del metodo richiesto validate(). Poiché  quel
metodo è ancora indicato come abstract in PropertyObject si deve
etichettare come abstract anche la classe costringendo tutte le classi
ereditanti a implementare quella funzione.
 
Alcune considerazioni e punti che non mi tornano:
Il libro dice: poichè quel metodo è ancora indicato come abstract....
ma dove? validate() in PropertyObject non è abstract, e se lo
specifico io facendo  abstract public function validate();
l'interprete mi dice: Fatal error: Can't inherit abstract function
Validator::validate() (previously declared abstract in PropertyObject)
Errore che penso derivi dal mancato rispetto dell'interfaccia,
sbaglio??
 
Altra cosa, se volessi costringere ogni classe che eredita da
PropertyObject a implementare il metodo validate() non sarebbe più
corretto non usare l'interfaccia e fare appunto abstract public
function validate(); ?? in questo modo l'implementazione di validate
nelle classi che ereditano è obbligatoria...
 
Invece per come è messo ora il codice, dentro la classe Address non
sono obbligato a implementare validate(), o almeno il mio interprete
php5.3 non da nessun problema.
 
Mi sfugge forse qualcosa?
 
Ciao
Matteo

P.S. qui trovate i sorgenti del libro, capitolo 3
http://www.hoepli.it/editore/hoepli_file/download_pub/978-88-203-4359-0_PHP6_Code_for_Download.zip
 

 php5.3 non da nessun problema.
 
Bisogna vedere la configurazione php come è stata impostata. 
Dovresti mettere l'output completo di ogni errore.
In ogni caso anche se hai un livello completo di output di errore l'interprete potrebbe limitarsi a non eseguire le operazioni e bloccarsi (tipo lascia la pagina bianca).
Le cose cambiano anche in base al web server e piattaforma.
 
In Java avendo il compilatore un errore di questo tipo te lo avrebbe segnalato prima dell'esecuzione.
 
Spero di essere stato chiaro :)

 Spero di essere stato chiaro :)
 
Purtroppo ho ancora diversi dubbi vedendo come si comporta il codice a
livello pratico :(

 
 
I metodi che hanno 'abstract' davanti devono essere OBBLIGATORIAMENTE ridefiniti nelle sotto-classi, quelli senza si chiamano metodi astratti ma possono essere utilizzati nelle sottoclassi.
 
 
Prendiamo l'esempio nel sito 
php.net
 
 
<?php
abstract class AbstractClass
{
    // Force Extending class to define this method
    abstract protected function getValue();
    abstract protected function prefixValue($prefix);
------------------------------------------------------
I due metodi sopra DEVONO essere riscritti nelle sotto-classi altrimenti dà errore. Prova a copiare tutto il codice e commentare nella sottoclasse ConcreteClass1  il metodo getValue(). Avrai un errore di questo tipo:
 
Fatal error: Class ConcreteClass1 contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (AbstractClass::getValue) in /home/content/93/8539493/html/prova.php on line 23
-------------------------------------------------------
 
    // Common method
    public function printOut() {
        print $this->getValue() . "\n";
    }
 
------------------------------------------------------------
Questo è un metodo diciamo 'astratto' perchè fa parte di una classe abstract ma ha una implementazione classica come tutte le altre.
Nel senso che posso fare così:
 
$class1 = new ConcreteClass1;
$class1->printOut();
 
Ma non così:
$class1 = new AbstractClass;   ->  ERRORE!!! non si può instanziare una classe astratta
$class1->printOut();
 
------------------------------------------------------------
 
 
}
 
class ConcreteClass1 extends AbstractClass
{
    protected function getValue() {
        return "ConcreteClass1";
    }
 
    public function prefixValue($prefix) {
        return "{$prefix}ConcreteClass1";
    }
}
 
class ConcreteClass2 extends AbstractClass
{
    public function getValue() {
        return "ConcreteClass2";
    }
 
    public function prefixValue($prefix) {
        return "{$prefix}ConcreteClass2";
    }
}
 
$class1 = new ConcreteClass1;
$class1->printOut();
echo $class1->prefixValue('FOO_') ."\n";
 
$class2 = new ConcreteClass2;
$class2->printOut();
echo $class2->prefixValue('FOO_') ."\n";
?>
 
 
Quello che forse non ti è chiaro nell'esempio del libro è L'OVERRIDING DEI METODI, cioè la ridefinizione dei metodi.
 
Nel tuo esempio:   function validate() è di proprietà dell'interfaccia Validator
 
Viene ripresa nella classe astratta PropertyObject e ridefinita (cioè viene fatto un overriding della funzione). Non la posso definire abstract perchè è di Validator e non 'propria' di PropertyObject. Diciamo che è una funzione "ereditata".
 
Address essendo sottoclasse di PropertyObject eredita la funzione validate() e la ridefinisce (altro overriding).
 
Nella prima risposta che ti avevo messo non avevo letto tutto per bene e ammetto che il problema non era risolto.
 
 
Se vuoi che una classe sia obbligata a ridefinire un metodo, nella classe abstract metti davanti alla funzione 'abstract'. Ma la funzione deve essere solo della classe astratta e non derivata da altre super-classi o interfacce.
Per riprendere il tuo esempio, la classe PropertyObject poteva essere cosìì:
 
 
  abstract class PropertyObject implements Validator {
 
    public function validate() {}  ->  non puoi metterla abstract perchè è di Validator
 
 
 
    abstract function getID();   ->  questa funzione deve essere PER FORZA ridefinita nelle sottoclassi, altrimenti dà errore
 
 
 
    function getNome() {}   ->  non sei obbligato a ridefinirla nelle sottoclassi, sei libero di richiamarla da sotto-classi  (es.   $sottoclasse->getNome()  )
 
  } 
 
 
Se invece vuoi far si che le sottoclassi NON possano ridefinire le funzioni delle super-classi basta aggiungere nella funzione della superclasse l'attributo final prima della funzione.
Es.
 
 
final function getStato() {}   ->  questa funzione può essere richiamata dalle sottoclassi ma non ridefinita.
 
 
 
 
 
Secondo me comunque fra il codice del libro e quello che c'è scritto ci deve essere un errore:
 
"La classe PropertyObject è dichiarata abstract per due motivi. Primo,
la classe PropertyObject da sola non è molto utile. Le classi che
estendono PropertyObject hanno ancora del lavoro da svolgere prima di
poter utilizzare i suoi metodi.Secondo, non è stata fornita
un'implementazione del metodo richiesto validate(). Poiché  quel
metodo è ancora indicato come abstract in PropertyObject si deve
etichettare come abstract anche la classe costringendo tutte le classi
ereditanti a implementare quella funzione. "
 
 
 
La funzione validate() di PropertyObject è ripresa da Validator e non scritta come "nuova". -> questa è una operazione di overriding!
 
 
Spero che anche altre persone possano darti una risposta al tuo quesito così si confrontano eventuali problemi irrisolti.

 "comuni" e quindi non tutti sono astratti...
 
Infatti a me risultà che le cose stanno esattamente in questo modo, in 
una classe definita abstract puoi implementare dei metodi ed in questo 
caso non sei obbligato ad implementarli nella classe che la estende; 
diverso il caso se il metodo è definito come astratto.
E' vero che la classe astratta non può essere instanziata, non che non 
può implementare metodi.
 
Sto studiando anche io oop in php, mentre faccio il refactoring di 
codice vecchio stampo, quindi potrei aver imparato poco e male...
 
 
-- 
Design for the future, because it will be here sooner than you think.
(C) Eric S. Raymond, from "The Art of Unix Programming"

 codice vecchio stampo, quindi potrei aver imparato poco e male...
 
No no ciò che dici è giusto, ma l'esempio che stavo facendo io era un
po' più "complesso" dato che c'è anche un'interfaccia e non sono
riuscito subito a capire come interfacce e classi astratte lavorano
assieme.
 
Ora è tutto chiaro è la logica è che:
Le classi abstract possono avere metodi non astratti (e che quindi
possono essere utilizzati normalmente dalle sotto classi) e metodi
astratti che devono essere per forza definiti nelle sotto classi. Nel
mio esempio però, il metodo validate è un metodo dell'interfaccia
validator e non è quindi un metodo astratto che deve essere per forza
ridefinito nelle sotto classi!
 
Sono comunque d'accordo con Giuseppe quando dice che nel libro che sto
seguendo ci potrebbe essere qualcosa che non va! quindi se avrò altri
dubbi, riscriverò :) Anzi, faccio subito una domanda stupida; appurato
che OVERRIDING è corretto come termine e riferisce alla
ridefiniziaione di un metodo in una sotto classe per
"specializzarlo",il termine OVERWRITING esiste? in caso a cosa si
riferisce (ovviamo parlo sempre di oop)


L'ORVERWRITING nella programmazione ad oggetti sinceramente non l'ho mai sentita.
Esiste l'OVERLOADING e come detto prima l'OVERRIDING.
 
L'OVERLOADING è la possibilità di definire la stessa funzione più volte all'interno della classe (deve cambiare il numero di parametri).
 
Es. 
 
 
function  saluta() {
 
}
 
function saluta($nome) {
 
}
 
function saluta($nome, $cognome) {
 
}
 
Usata quando si hanno più parametri nella funzione e non sempre sono usati tutti. Si evita il controllo se la variabile è piena o vuota all'interno della funzione stessa.
 
In Php però è consentito solo con determinate funzioni e basta per il momento.
 
__get()
__set()
__call()
 
http://www.php.net/manual/en/intro.overload.php
 


wrote:

Grazie come al solito Giuseppe!
Se posso permettermi ti/vi vorrei portare a conoscenza del gruppo php
italia su FB. Se avete voglia/piacere di iscrivervi,magari cresce e ne
scappa qualcosa di buono!
 
Ciao!