Ereditarietà

di il
14 risposte

Ereditarietà

Ciao,
ho un dubbio riguardante l'ereditarietà: ma se io ho una classe Impiegato (nome, cognome, codice fiscale, conto bancario) e una classe Responsabile che estende la classe impiegato (si aggiunge solo il numero di reparto di cui è responsabile) e avessi bisogno dei dettagli di tutti gli operai, non posso fare tutto in un'unica funzione? In modo che questa funzione mi restituisca i dettagli di tutti, indipendentemente se si tratta di responsabili o impiegati. O devo per forza fare una funzione per ciascun tipo di lavoratore? Perché il tipo di metodo sarebbe ArrayList<Responsabile> e se si dovesse trattare di un Impiegato non dovrebbe contenere il numero di reparto.

14 Risposte

  • Re: Ereditarietà

    z3rgamonamour ha scritto:


    ma se io ho una classe Impiegato (nome, cognome, codice fiscale, conto bancario) e una classe Responsabile che estende la classe impiegato (si aggiunge solo il numero di reparto di cui è responsabile) e avessi bisogno dei dettagli di tutti gli operai, non posso fare tutto in un'unica funzione? In modo che questa funzione mi restituisca i dettagli di tutti, indipendentemente se si tratta di responsabili o impiegati. O devo per forza fare una funzione per ciascun tipo di lavoratore? Perché il tipo di metodo sarebbe ArrayList<Responsabile> e se si dovesse trattare di un Impiegato non dovrebbe contenere il numero di reparto.
    Scusa ma per come l'hai descritto è abbastanza confusionario. Se fai un ArrayList<Responsabile> esso può contenere solo oggetti Responsabile, NON oggetti Impiegato.
    Se invece fai un ArrayList<Impiegato>, allora può contenere sia oggetti Impiegato che oggetti Responsabile. Quando estrai un oggetto però lo "vedi" di base solo come Impiegato e se sai/determini che realmente è un Responsabile solo allora puoi fare un cast per "vedere" il tipo più specifico e quindi tutti i metodi in più aggiunti da Responsabile.
  • Re: Ereditarietà

    andbin ha scritto:


    z3rgamonamour ha scritto:


    ma se io ho una classe Impiegato (nome, cognome, codice fiscale, conto bancario) e una classe Responsabile che estende la classe impiegato (si aggiunge solo il numero di reparto di cui è responsabile) e avessi bisogno dei dettagli di tutti gli operai, non posso fare tutto in un'unica funzione? In modo che questa funzione mi restituisca i dettagli di tutti, indipendentemente se si tratta di responsabili o impiegati. O devo per forza fare una funzione per ciascun tipo di lavoratore? Perché il tipo di metodo sarebbe ArrayList<Responsabile> e se si dovesse trattare di un Impiegato non dovrebbe contenere il numero di reparto.
    Scusa ma per come l'hai descritto è abbastanza confusionario. Se fai un ArrayList<Responsabile> esso può contenere solo oggetti Responsabile, NON oggetti Impiegato.
    Se invece fai un ArrayList<Impiegato>, allora può contenere sia oggetti Impiegato che oggetti Responsabile. Quando estrai un oggetto però lo "vedi" di base solo come Impiegato e se sai/determini che realmente è un Responsabile solo allora puoi fare un cast per "vedere" il tipo più specifico e quindi tutti i metodi in più aggiunti da Responsabile.
    Un cast per "vedere" il tipo più specifico potrebbe essere un if che controlla se Impiegato.getNumeroReparto esiste?
  • Re: Ereditarietà

    z3rgamonamour ha scritto:


    Un cast per "vedere" il tipo più specifico potrebbe essere un if che controlla se Impiegato.getNumeroReparto esiste?
    Se hai un ArrayList<Impiegato> sì, è questo il caso perché hai detto che il numero reparto è una nozione del Responsabile di cui la classe Impiegato non "sa" nulla.

    Quando c'è una collezione di un tipo "base" bisognerebbe cercare il più possibile di sfruttare il polimorfismo, ovvero invocare metodi noti già nel tipo base e potenzialmente/possibilmente RIdefiniti nei sottotipi. Ma ci sono casi in cui è per forza necessario "vedere" il tipo più specifico.
  • Re: Ereditarietà

    Il ""casino"" e' questo

    Impiegato <- Responsabile

    OVVIAMENTE ogni Responsabile e' anche Impiegato, ma SOLO qualche 'impiegato e' anche Responsabile.

    Il cast verso la classe piu' generale (da Responsabile a Impiegato) e' AUTOMATICO, integrato nel linguaggio.
    L'operazione contraria, DOWN CAST (DA Impiegato A Responsabile) ovviamente puo' essere assicurata SOLO in certe circostanze.

    Come identificare queste circostanze?

    Ci sono tanti modi, alcuni dei quali sono

    1) aggiungere un campo di tipo intero/enumerativo che specifica il TIPO di impiegato: e' SI un impiegato, ma di tipo 1,2,3,4,5,...
    Se e'del tipo giusto (ad esempio 2) SAI che e' anche un responsbile
    2) controlli la CLASSE di appartenenza dell'istanze (o.getClass().equals(Responsabile.class))
    3) controlli se l'oggetto e' ISTANZA della classe (o instanceof Responsabile)


    SE la condizione e' soddisfatta, sai che puoi fare un DOWN CAST (un cast verso la classe piu' specializzata)

    Il ""casino"" di ""incasina"" ulteriormente se supponi di avere anche TANTI tipi di Responsabili:

    ResponsabileMarketting ( ), ResponsabileVendite, ResponsabileProduzione, ...

    In questo caso il metodo 3) funziona ottimammente
  • Re: Ereditarietà

    Salve, scusate se mi aggancio al discorso ma avrei bisogno di una piccola delucidazione.
    Creando la classe Impiegato con nome, cognome, codice fiscale, conto bancario...
    
    class Impiegato {
        private String nome;
        private String cognome;
        private String codFis;
        private String iban;
       
        public Impiegato(String nome, String cognome, String codFis, String iban) {
            this.nome = nome;
            this.cognome = cognome;
            this.codFis = codFis;
            this.iban = iban;
        }
    // si fanno i vari metodi di accesso alle variabili private con getNome() getCognome() etc etc
        
        public String getNome(){
            return nome;
        }
        // etc etc etc getCognome(), getCodFis(), getIban().
        
    }
    
    //Dopodichè si crea la classe Responsabile
    
    class Responsabile extends Impiegato {
       
        private String reparto;
    /*   
        ###### nel costrutto di responsabile vanno per forza richiamate anche le variabili di impiegato? o ci sono altri modi?
        
     */   
        public Responsabile(String nome, String cognome, String codFis, String iban, String reparto){
            super(nome, cognome, codFis, iban);
            this.reparto;
        }
        public String getReparto(){
            return reparto;
        }
    }
    
    Se è giusto come ho scritto questo stralcio di codice vi pongo due domande per chiarimento:
    - nel costrutto di responsabile vanno per forza richiamate anche le variabili di impiegato utilizzando poi super()? o ci sono altri modi più snelli?
    - perchè nello specifico nel costrutto si dichiarano le variabili con il this.* e poi nel metodo getXXX() si può anche fare il return della variabile private ( esempio return reparto; ). Diciamo che il mio dubbio è principalmente sull'utilizzo del this()
  • Re: Ereditarietà

    pangolino ha scritto:


    - nel costrutto di responsabile vanno per forza richiamate anche le variabili di impiegato utilizzando poi super()? o ci sono altri modi più snelli?
    Siccome Impiegato ha 1 solo costruttore che riceve quei 4 String, da una sottoclasse Responsabile DEVI per forza invocare quel super-costruttore con la forma super( ..... ).

    Poi Responsabile è ragionevole che riceva nel SUO costruttore quei 4 dati più altro eventuale che si tiene per sè. Quindi quello che hai fatto è la forma giusta, corretta e tradizionale. Non c'è un "altro" modo.

    pangolino ha scritto:


    - perchè nello specifico nel costrutto si dichiarano le variabili con il this.* e poi nel metodo getXXX() si può anche fare il return della variabile private ( esempio return reparto; ). Diciamo che il mio dubbio è principalmente sull'utilizzo del this()
    Il this generalmente lo si può considerare "implicito" nei metodi di istanza. Nei getter è così, basta fare

    return nome;

    perché quel nome è la variabile di istanza che è "in scope" nel metodo.

    Mentre nel costruttore ci sono i parametri che (convenzionalmente) si mettono con lo stesso nome dei campi. Ma il parametro nome effettua quello che si chiama "shadowing", ovvero nasconde il campo.
    All'interno del costruttore, solo mettere nome fa riferimento al parametro, NON al campo. Quindi se vuoi referenziare il campo, bisogna qualificarlo con this.nome cioè indicare esplicitamente che vuoi il nome variabile di istanza.
  • Re: Ereditarietà

    Ok ora mi è chiaro!
    grazie mille !
  • Re: Ereditarietà

    Propongo un cambio di approccio al problema,

    Se lo scopo è avere un Oggetto che gestisca dei dati comuni (nome, cognome, codice fiscale, iban) e un dato che classifica l'oggetto (impiegato,responsabile,ecc) forse l'approccio migliore non è quello di estendere la classe ma avere due classi distinte indipendenti.


    Creiamo una classe tipo utente che mi gestisce la tipologia utente (e mettiamo anche un metodino che a fronte di un codice mi dia la descrizione)
    A questo punto se creiamo una classe utente che abbia nel suo metodo come variabile in ingresso un oggetto tipo utente possiamo gestire il requisito in maniera più flessibile senza estendere le classi ma con 2 semplici classi possiamo creare utenti associati a tipologie differenti

    esempio classe TipoUtente
    public class TipoUtente {
    
    
    	private String codice=null;
    	private String descrizione=null;
    	
    	public String descrizioneTipoUtente(String codice) {
    		if(codice==this.codice) {
    			return this.descrizione;
    		}
    		return null;
    	}
    	----getters and setters
    	
    	
    Classe Utente
    public class Utente {
    	 private String nome=null;
    	 private String cognome=null;
    	 private String codFis=null;
    	 private String iban=null;
    	 private String tipologia=null;
    
    	   
    	 public Utente(String nome, String cognome, String codFis, String iban,TipoUtente tipoUtente ) {
    	     this.nome = nome;
    	     this.cognome = cognome;
    	     this.codFis = codFis;
    	     this.iban = iban;
    	     this.tipologia = tipoUtente.descrizioneTipoUtente(tipoUtente.getCodice());
    	    }
    	    	----getters and setters
    	    
    main di esempio
    
    public static void main(String[] args) {
    		
    		TipoUtente tipoUtente1 = new TipoUtente();
    		TipoUtente tipoUtente2 = new TipoUtente();
    		TipoUtente tipoUtente3 = new TipoUtente();
    		
    		tipoUtente1.setCodice("R01");
    		tipoUtente1.setDescrizione("Responsabile Acquisti");
    		tipoUtente2.setCodice("D01");
    		tipoUtente2.setDescrizione("Direzione Acquisti");
    		tipoUtente3.setCodice("I01");
    		tipoUtente3.setDescrizione("Impiegato");
    		
    		Utente impiegato= new Utente("Mario", "Rossi", "xxx", "yyy", tipoUtente3);
    		Utente responsabileAcquisti= new Utente("Paolo", "Bianchi", "a", "b", tipoUtente1);
    		Utente direzioneAcquisti= new Utente("Alberto", "Moro", "zz", "ff", tipoUtente2);
    		
    		System.out.println(impiegato.getTipologia());
    		System.out.println(responsabileAcquisti.getTipologia());
    		System.out.println(direzioneAcquisti.getTipologia());
    
    	}
    
    Un approccio simile si usa se hai una applicazione che si appoggia su un db in quel caso si gestiscono le classi in maniera differente ma simile a quanto descritto. Avremmo quindi due tabelle una tipoUtente e una Utente, quest'ultima con un outerJoin sulla tabella Utente.
  • Re: Ereditarietà

    E' un buon esercizio mentale vedere varie esposizioni! Grazie anche a te luca!
  • Re: Ereditarietà

    Lucadf89 ha scritto:


    Propongo un cambio di approccio al problema,

    Se lo scopo è avere un Oggetto che gestisca dei dati comuni (nome, cognome, codice fiscale, iban) e un dato che classifica l'oggetto (impiegato,responsabile,ecc) forse l'approccio migliore non è quello di estendere la classe ma avere due classi distinte indipendenti.
    Chiariamo innanzitutto una cosa: l'utente iniziale aveva un dubbio (più che legittimo) sulla ereditarietà e nei post precedenti non si è mai parlato di "database".

    Lucadf89 ha scritto:


    (e mettiamo anche un metodino che a fronte di un codice mi dia la descrizione)
    	public String descrizioneTipoUtente(String codice) {
    		if(codice==this.codice) {
    			return this.descrizione;
    		}
    		return null;
    	}
    Questo metodino, di fatto NON ha alcun senso. Già quel codice==this.codice non è una buona cosa è funziona "guarda caso" solo perché in Utente fai:

    tipoUtente.descrizioneTipoUtente(tipoUtente.getCodice());

    per cui è ovvio che quel codice è lo stesso oggetto e == dà true.

    Quindi quello che hai scritto è INAPPROPRIATO. Non serve.

    Lucadf89 ha scritto:


    Classe Utente
    public class Utente {
    	 private String nome=null;
    	 private String cognome=null;
    	 private String codFis=null;
    	 private String iban=null;
    	 private String tipologia=null;
    
    Precisiamo innanzitutto che avere due entità Utente e TipoUtente di per sè è GIUSTO, non c'è nulla di sbagliato. Se vuoi fare la classe Utente in modo "sensato" le possibilità sono due:

    a) In Utente tieni il CODICE del tipo utente (NON la descrizione!!):
    public class Utente {
        private String nome;
        private String cognome;
        private String codFis;
        private String iban;
        private String codiceTipoUtente;    // <------
    oppure

    b) In Utente tieni il riferimento all'oggetto TipoUtente:
    public class Utente {
        private String nome;
        private String cognome;
        private String codFis;
        private String iban;
        private TipoUtente tipoUtente;    // <------
    Tenere solo la descrizione è inutile e non ha senso, perché non puoi più "legare" l'utente al suo tipo utente.

    Lucadf89 ha scritto:


    Un approccio simile si usa se hai una applicazione che si appoggia su un db in quel caso si gestiscono le classi in maniera differente ma simile a quanto descritto.
    Ribadisco ancora che non è il tuo approccio che è sbagliato. È assolutamente più che ragionevole avere una tabella "utente" e un'altra tabella "tipo utente". Ma si tratta di un design ben differente.
    La ereditarietà invece serve per un altro motivo: per "specializzare" il comportamento. Se Responsabile estende Impiegato, magari ci deve essere un metodo che fa un qualche calcolo o logica particolare che in Responsabile è ridefinito per farlo in maniera diversa o più specifica. Qui la ereditarietà è utile.

    E comunque il fatto di avere una gerarchia di classi, NON preclude certo l'uso (lettura/scrittura) con un database. Anche se ovviamente servono degli accorgimenti più specifici.

    Lucadf89 ha scritto:


    Avremmo quindi due tabelle una tipoUtente e una Utente, quest'ultima con un outerJoin sulla tabella Utente.
    Non voglio sembrare pignolo (beh, sì ... lo so) ma ... non sono le tabelle ad evere gli inner/outer join. La scelta del join la fai a livello di query.
    Nelle tabelle al massimo ci sono delle foreign key ovvero colonne designate in modo specifico per fare riferimento ad un'altra colonna in un'altra tabella.

    Quindi nello scenario descritto, la tabella Utente potrebbe avere una colonna cod_tipo_utente (foreign key) che fa riferimento alla colonna codice della tabella Tipo_Utente. Se la colonna cod_tipo_utente in Utente è not-nullable (cosa più che ragionevole), vuol dire che un Utente ha sempre una tipologia (anche qui, ragionevole/sensato). Pertanto a livello di query basta un normale join ("inner") verso Tipo_Utente.


    Conclusioni: studia un po' di più ..
  • Re: Ereditarietà

    Mi è sorta un'altra domanda....
    
    class Impiegato {
        private String nome;
        private String cognome;
        private String codFis;
        private String iban;
       
        public Impiegato(String nome, String cognome, String codFis, String iban) { //costrutto
            this.nome = nome;
            this.cognome = cognome;
            this.codFis = codFis;
            this.iban = iban;
        }
    // si fanno i vari metodi di accesso alle variabili private con getNome() getCognome() etc etc
        
        public String getNome(){
            return nome;
        }
       
    
    Avendo referenziato il campo dei parametri nel costrutto ha sempre senso creare un setter per ogni variabile privata?
    
    public void setNome(){
        this.nome = nome;
    }
    
    Sempre attraverso il costrutto ho modo di modificare tutti i parametri "insieme" ed invece ogni singolo setter per ogni variabile li modifico singolarmente?
  • Re: Ereditarietà

    pangolino ha scritto:


    Avendo referenziato il campo dei parametri nel costrutto ha sempre senso creare un setter per ogni variabile privata?
    Sì, se gli oggetti devono essere "mutabili", sì i "setter" ovviamente servono (poi se tutti o no, si potrebbe argomentare).

    Se invece vuoi definire oggetti "immutabili", metti i campi final (e il costruttore li deve assegnare) e definisci solo i "getter".
  • Re: Ereditarietà

    Ottimo grazie!
  • Re: Ereditarietà

    Grazie andbin ottime le tue puntualizzazioni, abbi pazienza non sono uno sviluppatore ho ripreso a scrivere un paio di righe di codice dopo anni che non lo facevo e sto cercando di imparare un po di java da autodidatta. Lavoro come analista quindi lo faccio per passatempo e per capire meglio come funzionano le applicazioni su cui disegno i processi.

    La mia riflessione è ovviamente off topic e scritta male chiedo venia
Devi accedere o registrarti per scrivere nel forum
14 risposte