Skillbook Logo
foto profilo

Skill Factory

Lista post > Introduzione alla logica degli oggetti - Lezione 4

Introduzione alla logica degli oggetti - Lezione 4

Gino Visciano | Skill Factory - 05/10/2018 22:44:13 | in Tutorials

In questa lezione parliamo di Ereditarietà, che insieme all'incapsulamento ed il polimorfismo, è uno dei più importanti paradigmi dell'Object Oriented.

CHE COS'E' L'EREDITARIETA'
L'ereditarietà è una tecnica di riuso del codice che permette di specializzare una classe già esistente, per crearne una nuova.

La nuova classe si chiama classe derivata, mentre la classe ereditata si chiama classe superiore, come mostra il diagramma UML seguente:

L'ereditarietà si dice singola se la classe derivata può ereditare una sola classe, mentre si dice multipla se la classe derivata può ereditare più classi contemporaneamente. Ad esempio  il linguaggio C++ permette anche l'ereditarietà è multipla, mentre  Java e C# permettono solo l'ereditarietà singola, il diagramma UML seguente mostra un esempio di ereditarietà è multipla:

 

 

Un classe Persona che contiene gli attributi seguenti: id, nome, cognome, dataDiNascita, luogoDiNascita e sesso, può essere specializzata in una classe Dipendente che, oltre agli altributi della classe Persona contiene anche gli attributi seguenti: stipendio e ruolo, come mostra il diagramma UML seguente:

Con la classe Dipendete adesso si possono istanziare oggetti che oltre a contenere il ruolo e lo stipendio, contengono anche id, nome, cognome, dataDiNascita, luogoDiNascita e sesso, come mostra il diagramma UML seguente:

Di seguito implementiamo l'esempio appena visto con il linguaggio c++.

Prima di tutto creiamo una classe Data per gestire oggetti di tipo data, servirà in seguito per assegnare la data di nascita alla persona:

Adesso creiamo la classe Persona, che specializzaremo successivamente in Dipendente:

Infine creaiamo la classe Dipendente, che eredita la classe superiore Persona:

GESTIONE DEI COSTRUTTORI PARAMETRIZZATI
Se la classe superiore ha un costruttore parametrizzato, usato per inizializzare gli attributi, la classe derivata lo deve eseguire altrimenti non può funzionare correttamente.

Il diagramma UML seguente, mostra in che modo il costruttore della classe derivata esegue il costruttore parametrizzato della classe superiore:

Vediamo lo stesso esempio scritto in linguaggio C++:

class Persona {
private:
string sesso;
public:
int id;
string nome;
string cognome;
Data *dataDiNascita;
string luogoDiNascita;
void setMaschio(){
        this->sesso="maschio";
}
void setFemmina(){
         this->sesso="femmina";
}
string getSesso(){
          if(sesso=="") sesso="maschio";
          return this->sesso;
}
Persona(){
     this->dataDiNascita=new Data();
}
Persona(int id,string nome,string cognome, Data *dataDiNascita,string luogoDiNascita, string sesso){
     if(sesso=="maschio")setMaschio();
     else setFemmina();
     this->dataDiNascita=new Data();
     this->id=id;
     this->nome=nome;
     this->cognome=cognome;
     this->dataDiNascita=dataDiNascita;
     this->luogoDiNascita=luogoDiNascita;
}

};

class Dipendente : public Persona {
public:
double stipendio;
string ruolo;
Dipendente(){}
Dipendente(int id, string nome,string cognome, Data *dataDiNascita,string luogoDiNascita, string sesso, double stipendio, string ruolo):Persona(nome, cognome, dataDiNascita, luogoDiNascita, sesso){
    this->stipendio=stipendio;
    this->ruolo=ruolo;}
}
};

OVERRIDE (SOVRASCRITTURA) DI METODI EREDITATI DALLA CLASSE SUPERIORE
L'override è la tecnica che permette di sovrascrivere un metodo ereditato da una classe superiore per modificarne il comportamento all'interno della classe derivata. L'override, insieme all'overload può essere definito plimorfismo dei metodi.

Immaginiate di avere nella classe Persona il metodo getInformazioni() che restituisce i valori di tutti gli attributi della classe separti da una virgola. Se la classe Persona viene ereditata dalla classe Dipendente, questo metodo dovrà essere necessariamete sovrascritto altrimenti nell'elenco di attributi restituito mancherà lo stipendio ed il ruolo del Dipendente.

In C++ un metodo della della classe superiore per essere sovrascritto nella classe derivata, deve essere virtuale, come mostra l'esempio seguente:

class Persona {
private:
string sesso;
public:
int id;
string nome;
string cognome;
Data dataDiNascita;
string luogoDiNascita;
void setMaschio(){
        this->sesso="maschio";
}
void setFemmina(){
        this->sesso="femmina";
}
string getSesso(){
           if(sesso=="") sesso="maschio";
           return this->sesso;
}

// Metodo virtuale che verrà sovrascritto nella classe Dipendente
virtual string getInformazioni(){
             return nome + ", "+cognome+", "+dataDiNascita.getData()+", "+luogoDiNascita+", "+sesso;
}

 

Persona(){
     this->dataDiNascita=new Data();
}
Persona(int id,string nome,string cognome, Data *dataDiNascita,string luogoDiNascita, string sesso){
     if(sesso=="maschio")setMaschio();
     else setFemmina();
     this->dataDiNascita=new Data();
     this->id=id;
     this->nome=nome;
     this->cognome=cognome;
     this->dataDiNascita=dataDiNascita;
     this->luogoDiNascita=luogoDiNascita;
}
};

class Dipendente : public Persona {
public:
double stipendio;
string ruolo;
// Sovrascrittura del Metodo getInformazioni ereditato dalla classe Persona
string getInformazioni(){
           // Conversione double in stringa
           stringstream strStipendio;
           strStipendio << this->stipendio;
           return nome + ", "+cognome+", "+dataDiNascita.getData()+", "+luogoDiNascita+", "+
getSesso()+", "+strStipendio.str()+", "+ruolo;
}

Dipendente(){}
Dipendente(int id,string nome,string cognome, Data *dataDiNascita,string luogoDiNascita, string sesso, double stipendio, string ruolo):
    Persona(id, nome, cognome, dataDiNascita, luogoDiNascita, sesso){
    this->stipendio=stipendio;
    this->ruolo=ruolo;}
};

INCAPSULAMENTO DEGLI ATTRIBUTI E DEI METODI DI UNA CLASSE IN CASO DI EREDITARIETA'
Nell'esempio precedente, quando viene sovrascritto il metodo getInformazioni() della classe Dipendente, l'attributo sesso è stato sostituito dal metodo getSesso().
Questa operazione è necessaria perché l'attributo sesso della classe Persona è privato. Gli elementi privati di una classe sono visibili solo ai metodi della stessa classe, mentre non sono visibili in alcun caso ai metodi di altre classi.
In questo caso, sovrascrivendo il metodo getInformazioni() nella classe Dipendente, diventa un metodo esterno alla classe superiore e non può più vedere l'attributo sesso.
Il metodo getInformazioni() della classe Dipendente, per leggere il valore dell'attributo sesso, lo può fare solo attraverso il metodo getSesso() della classe Persona perchè è pubblico, come mostra l'immagine seguente:

Per poter usare direttamente l'attributo sesso nel metodo getInformazioni() della classe Dipendente, bisogna impostarlo protected, come mostra l'immagine seguente:

Per chiarire meglio il ruolo dei modificatori di accesso approfondiamo insieme il diagramma UML seguente:

Osservando il diagramma si capisce che una classe derivata che eredita una classe superiore, può usare attraverso un metodo, senza istanziare, tutti i suoi elementi pubblici e protetti.
Infatti il metodo metodotSeconda() della classe Seconda vede:
   attributoPublic;
   attributoProtected;
   metodoPrimoA();
   metodoPrimoC()
.

Mentre non vede l'attributoPrivate perché è privato. Se fosse necessario accedere a questo attributo lo potrebbe fare solo attraverso l'uso dei metodi:
    metodoPrimoA();
    metodoPrimoC().

Invece una classe utilizzatrice, per usare attraverso un suo metodo, gli elementi di un'altra classe, le deve prima istanziare e attraverso l'oggetto creato può vedere solo gli elementi pubblici della classe istanziata.
Infatti il metodo metodotTerza() della classe Terza, attraverso l'oggetto s vede:
   s->attributoPublic;
   s->metodoPrimoA();
   s->metodoSeconda().

Mentre non vede:
   attributoPrivate
   attributoProtected;
   metodoPrimoC()
.

Se fosse necessario accedere agli attributi oppure ai metodi non visibili lo potrebbe fare solo attraverso l'uso dei metodi:
   s->metodoPrimoA();
   s->metodoSeconda()
.

Per rendere efficace questo argomento, vi suggerisco di implementare con il linguaggio C++  le classi Prima, Seconda e Terza ed analizzarne attentamente il comportamento.

EREDITARIETA' MULTIPLA
Il linguaggio C++ permette di ereditare più classi contemporaneamente in questo caso si parla di ereditarietà multipla, vediamo un esempio:

Immaginate di avere una classe Persona a cui volete aggiungere le informazioni dell'indirizzo ed i riferimenti principali. In questo caso la classe Persona potrebbe ereditare una classe Indirizzo ed una classe Riferimenti, come mostra il diagramma UML seguente: 

class Indirizzo{
public:
    string via;
    string cap;
    string citta;
    string provincia;
    Indirizzo(){}
};

class Riferimenti{
public:
    string telefonoCasa;
    string telefonoUfficio;
    string cellulare;
    string email;
    string social_1;
    string social_2;
    string social_3;
    Riferimenti(){}
};

class Persona : public Indirizzo, public Riferimenti{
private:
   string sesso;
public:
   int id;
   string nome;
   string cognome;
   Data *dataDiNascita;
   string luogoDiNascita;
   void setMaschio(){
            this->sesso="maschio";
   }
   void setFemmina(){
           this->sesso="femmina";
   }
   string getSesso(){
           if(sesso=="") sesso="maschio";
           return this->sesso;
   }
   Persona(){
           this->dataDiNascita=new Data();
   }
   Persona(int id,string nome,string cognome, Data *dataDiNascita,string luogoDiNascita, string sesso,
                   string via, string cap, string citta, string provincia, string telefonoCasa, string telefonoUfficio,
                   string email,string facebook, string linkedin, string skillbook){
                   if(sesso=="maschio")setMaschio();
                   else setFemmina();
                   this->dataDiNascita=new Data();
                   this->id=id;
                   this->nome=nome;
                   this->cognome=cognome;
                   this->dataDiNascita=dataDiNascita;
                   this->luogoDiNascita=luogoDiNascita;
                   this->via=via;
                   this->cap=cap;
                   this->citta=citta;
                   this->provincia=provincia;
                   this->telefonoCasa=telefonoCasa;
                   this->telefonoUfficio=telefonoUfficio;
                   this->email=email;
                   this->social_1=facebook;
                   this->social_2=linkedin;
                   this->social_3=skillbook;
                   }
};

Nella prossima lezione parleremo di Polimorfismo degli oggetti.


<< Lezione precedente           Lezione successiva >>


  Clicca qui per scaricare i laboratori di questa lezione  (Per eseguire i laboratori installate Code Block sul vostro computer)


Laboratori di Logica di Programmazione in C

 

ereditarietà singola ereditarietà multipla object oriented override sovrascrittura ereditarietà java javascript typescript incapsulamento modificatori di accesso c++ c# costruttori parametrizzati extends code block programmazione classi ed oggetti

Share Button
TOP