• User Attivo

    Ereditarietà tra classi OOP

    Salve a tutti, non riesco a venirne fuori da questo problema:

    Ho 3 classi (clss_A;clss_B;clss_C;) che vorrei far ereditare a 'catena'.

    clss_B extends clss_A e clss_C extends clss_B fin qui credo sia tutto ok (almeno credo).

    [php]//Classe A
    abstract class_A
    {
    protected $_propertyTable = array();

    protected $_data;

    public function __construct($arData)
    {
    $this->_data=$arData;

    }
    }
    //Classe B
    class_B extends class_A
    {
    public function __construct($u_ID=0)
    {
    if($u_ID!=0)
    {
    $arData=DM::getUtenteData($u_ID);
    parent:: __construct($arData);
    }

           $this->_propertyTable['id']='ute_id';
           $this->_propertyTable['nome']='ute_nome';
           $this->_propertyTable['cognome']='ute_cognome';
           $this->_propertyTable['my_mail']='ute_mail';
    

    }
    }

    //Classe C
    class_C extends class_B
    {
    public function __construct($u_ID=0)
    {
    if($u_ID!=0)
    {
    $arData=DM::getUtenteData($u_ID);
    parent:: __construct($arData);
    }

           $this->_propertyTable['codice_fiscale']='cod_f';
    

    }
    }
    [/php]

    Ora se istanzio la classe_C vorrei poter accedere sia al codice fiscale sia al nome o al cognome .... che invece sembrano non esser visibili !!!!! Vedo solo la proprietà 'codicefiscale' e non il nome o il cognome :mmm:
    Qualche suggerimento ?

    Thanks all :arrabbiato:


  • User Attivo

    Ciao Gandalf,

    Ad una prima occhiata il problema sembrerebbe solo derivare dalle proprietà di visibilità che hai dato agli attributi.
    Infatti, hai dichiarato _propertyTable come protected e risulta quindi invisibile dall'esterno della classe.
    Ne segue che se istanzi la classe C potrai vedere solamente ciò che è stato dichiarato all'interno di essa, ovvero il codice fiscale, ma non quello che hai dichiarato nelle altre classi.

    Le soluzioni sono due:
    O dichiari _propertyTable come public anziché protected, oppure definisci dei metodi pubblici che restituiscano i valori che ti sono necessari.

    Ciao,
    Luca Bartoli


  • User Attivo

    Ciao Luca grazie per la risposta, ma da che sappia io la paorla 'protected' intende la variabile visibile solo ed esclusivamente alle classi che ereditano.

    Ne segue che se istanzi la classe C potrai vedere solamente ciò che è stato dichiarato all'interno di essa
    Ma se C eredita da B e a sua volta B eredita da A ... perchè non dovrei vedere tutto ?

    Cmq provo a rendere Public la variabile _propertyTable lascio la classe C senza proprietà e verifico se da istanzia di C vedo le proprietà di B....

    [provato] non dà segni di vita 😞


  • User Attivo

    Allora il problema non è lì...
    Posso vedere anche il codice che utilizzi per testare la classe?


  • User Attivo

    @lucabartoli said:

    Allora il problema non è lì...
    Posso vedere anche il codice che utilizzi per testare la classe?

    ehm un pò lunghetto (incollo qui)

    ====== CLASSE A (madre) =========

    <?php
    abstract class propertyObject
    {
    **protected $_propertyTable = array();**
    protected $_changedProperties = array();
    protected $_data;
    protected $_errors = array();
     
     
     
     public function __construct($arData)
     {
       $this->_data=$arData;
     } 
       
      
      
      
     function __get($propertyName)
     {
      if(!array_key_exists($propertyName,$this->_propertyTable))
       throw new Exception("Proprietà non valida (get)".$propertyName."!");
       
      if(method_exists($this,'get'.$propertyName))
      {
       return call_user_func(array($this,'get'.$propertyName));
      }else{
       return $this->_data[$this->_propertyTable[$propertyName]];
      } 
     }
     
     
     
     function __set($propertyName, $value)
     {
      // Se la proprietà non esiste ERROR ***
      if(!array_key_exists($propertyName,$this->_propertyTable))
       throw new Exception("Proprietà non valida (set)". $propertyName ."!"); 
       
      if(method_exists($this,'set'.$propertyName))
      {
       return call_user_func(array($this,'set'.$propertyName),$value);
      }else{
       if($this->_propertyTable[$propertyName]!=$value && !in_array($propertyName,$this->_changedProperties))
       {
         $this->_changedProperties[]=$propertyName;
       }
       $this->_data[$this->_propertyTable[$propertyName]]=$value;
      }
     }
      
    }// fine classe
    ?>
    

    ====== CLASSE A (madre - fine) =========

    ====== CLASSE B (figlia) =========

    require_once('clss_PropertyObject.php'); // <--- Classe A
    
    
    class Utente extends propertyObject
    {
     
     public function __construct($u_ID=0)
     {	
    	    if($u_ID!=0)
    	   {
    		  $arData=DataManager::getUtenteData($u_ID);
    		  parent:: __construct($arData);
    	   }  
    	   	
    		   $this->_propertyTable['id']='ute_id';
    		   
    		   $this->_propertyTable['tipo']='ute_tipo';
    		   $this->_propertyTable['stato']='ute_stato';
    		   $this->_propertyTable['num_casuale']='ute_caso';
    		   $this->_propertyTable['nome']='ute_nome';
    		   $this->_propertyTable['cognome']='ute_cognome';
    		   $this->_propertyTable['my_mail']='ute_mail';
    		   
    }
    }
    

    ====== CLASSE B (figlia- fine) =========

    ====== CLASSE C (nipote) =========

    require_once('clss_Utente.php'); // <--- Classe B
    
    class c_utenteAr extends Utente
    {
       public function __construct($a_ID=0)
       {	
    	    if($a_ID!=0)
    	   {
    		  $arData=DataManager::getArmatoreData($a_ID);
    		  parent:: __construct($arData);
    	   }  
     
                       $this->_propertyTable['sito_web']='ute_website';
                       
                       $this->_propertyTable['stato']='ute_stato_name';
                       $this->_propertyTable['comune']='ute_comune_name';
                       $this->_propertyTable['data_nascita']='ute_birthdate';
                       
      }
    }
    

    ====== CLASSE C (nipote - fine) =========

    Ora ... se istanzio

    $x = new UtenteAr(); // tutto ok
    $x->tipo='mio_tipo';  // Errore nella procedura __set('tipo')! Proprietà inesistente
    

    :arrabbiato:


  • User Attivo

    Come non detto, trovato il problema.
    Guarda all'interno della definizione delle classi a questo punto:

    [php] if($u_ID!=0)
    {
    $arData=DM::getUtenteData($u_ID);
    parent:: __construct($arData);
    }[/php]

    Cosa succede?

    Che la classe figlia chiama il costruttore della classe padre solo se $u_ID è diverso da 0.
    Per avere il comportamento che invece stai cercando devi sempre chiamare il costruttore del genitore, in modo da ereditare metodi e attributi.
    E' sufficiente portare fuori dall'if il [php]parent:: __construct($arData);[/php]

    Questo è lo script che ho testato con successo:
    [php]//Classe A
    abstract class class_A
    {
    public $_propertyTable = array();
    protected $_data;

    public function __construct($arData)
    {
    $this->_data=$arData;

    }
    }
    //Classe B
    class class_B extends class_A
    {
    public function __construct($u_ID=0)
    {
    if($u_ID!=0)
    {
    $arData=DM::getUtenteData($u_ID);
    }

           parent:: __construct($arData);           
           $this->_propertyTable['id']='ute_id';
           $this->_propertyTable['nome']='ute_nome';
           $this->_propertyTable['cognome']='ute_cognome';
           $this->_propertyTable['my_mail']='ute_mail';
    

    }
    }

    //Classe C
    class class_C extends class_B
    {
    public function __construct($u_ID=0)
    {
    if($u_ID!=0)
    {
    $arData=DM::getUtenteData($u_ID);
    }

           parent:: __construct($arData);
           $this->_propertyTable['codice_fiscale']='cod_f';
    

    }
    }

    $prova=new class_C();
    print_r($prova->_propertyTable);[/php]

    Ciao,
    Luca


  • User Attivo

    Luca dimmi tu quando posso offrirti un caffè ... (come minimo) 😄

    Solo 1 domanda, quell' if in realtà a me tornava molto comodo per usare il costrutto con un ID utente particolare che mi richiamava senza troppa fatica tutti i dati di quel particolare utente.

    Se lo commento, mi suggerisci una strada secondaria x avere lo stesso risultato ?

    es.

    $x= new Utente(); //(oggetto vuoto in attesa di valori)
    $y= new Utente(46); //(Utente con ID:46)
    

    grazie infinite mi hai rimosso da un vortice da cui non penso ne sarei uscito facilmente :arrabbiato:

    ciao

    P.S.
    Ho visto solo ora la Protected è diventata Public ... continuo col protected o cambio ?


  • User Attivo

    Dipende:

    Se i valori dell'array ti servono solo all'interno della classe puoi lasciare protected.

    Se i valori dell'array ti servono anche dall'esterno della classe, o metti public, oppure lasci protected e poi ti crei delle funzioni per leggere e modificare i valori.

    Per quanto riguarda la comodità dell'if, forse mi sono spiegato male... Non devi commentarlo, nè toglierlo, devi solo portare fuori da esso il [php]parent:: __construct($arData);[/php]
    Le funzionalità rimarranno le stesse perché, se l'id è diverso da 0, prima di lanciare il costruttore del genitore viene eseguito
    [php]$arData=DM::getUtenteData($u_ID);[/php]Comunque, quando istanzi una classe questa è normalmente vuota quindi nessun problema. Fai solo attenzione ad assegnare i valori corrispondenti all'utente specifico dopo aver lanciato il costruttore, oppure a modificare il costruttore in modo da inserire subito i valori corretti.
    Questo perché, per come è ora il tuo script, lanciando il costruttore vengono assegnati i valori "standard" a tutte le variabili... Se quelle variabili hanno già un valore "buono" all'interno, il lancio del costruttore sovrascrive quei valori.

    La via più pulita sarebbe istanziare una nuova classe vuota e poi lanciare un metodo (che devi creare) che assegni i valori corrispondenti ad un certo id. Così non ci sono rischi di sovrapposizioni ed è anche concettualmente un modo più corretto.


  • User Attivo

    La via più pulita sarebbe istanziare una nuova classe vuota e poi lanciare un metodo (che devi creare) che assegni i valori corrispondenti ad un certo id. Così non ci sono rischi di sovrapposizioni ed è anche concettualmente un modo più corretto.

    vediamo se ho ben capito, mi conviene tenere fuori la funzione che ricava i dati dell'entità ?

    DM::getUtenteData($u_ID); diventerebbe una Public Function ? Da richiamare a parte ?

    grazie infinite :ciauz:


  • User Attivo

    Semplicemente immagino una classe fatta circa così:

    Class_C estende class_B, class_B estende class_A.
    Le classi B e C lanciano i costruttori del genitore, così erediti A e B istanziando una variabile di classe C ed erediti A istanziando una variabile di classe B.
    La classe A contiene una funzione pubblica che fa un return del vettore propertyTable, più una funzione set che permetta di modificarne i valori dall'esterno.
    A questo punto, dato che ti sei creato dei metodi che ti permettono di vedere e modificare i valori, puoi chiudere la classe mettendo la propertyTable come protected.

    Il vantaggio è che hai pieno controllo sui valori che vengono assegnati agli attributi della classe, perché tutto passa dalle funzioni che tu hai definito. E se ti serve di implementare un controllo ti basta modificare una funzione, anziché pezzi di script sparsi ovunque... 😉

    Spero di essermi spiegato, a quest'ora non connetto più completamente... 😄