Visualizzazione dei risultati da 1 a 7 su 7
  1. #1

    [JAVA] Dispatch dei metodi

    Sono sempre io... non avendo fondamenti di OOP trovo una certa difficoltà nel calarmi nella mentalità di questo tipo di programmazione: pur intuendone la potenza per il momento la sto imparando passivamente e ciò non mi piace, oltre a non essermi di nessun aiuto!
    Per esempio qualcuno sa spiegarmi con un esempio concreto quando verrà il momento che apprezzerò il dispatch dinamico dei metodi? A cosa serve una cosa del genere, tipo quella riportata qui sotto?

    class GreenBox {
    void callme() {
    System.out.println("Inside Green Box");
    }
    }

    class RedBox extends GreenBox {
    void callme() {
    System.out.println("Inside Red Box");
    }
    }

    class Dispatch {
    public static void main(String args[]) {
    GreenBox x = new RedBox();
    x.callme();
    }
    }

    C:\java_source>java Dispatch
    Inside Red Box

    Istanzio una variabile di un tipo e poi la riferisco ad un altro, perchè? Avrei ottenuto lo stesso risultato con:

    class Dispatch {
    public static void main(String args[]) {
    RedBox x = new RedBox();
    x.callme();
    }
    }

    ed allora mi sorge spontaneo non capire l'utilità di tutto ciò pur sapendo che a qualcosa serve!

    Abbiate pazienza.....
    Il mondo è diviso in 10 gruppi di persone... chi capisce il binario e chi no!

    http://www.pratesi.net

  2. #2
    Moderatore di Programmazione L'avatar di LeleFT
    Registrato dal
    Jun 2003
    Messaggi
    17,304
    Questo caso in cui viene evidenziata la potenza dell'ereditarietà e del polimorfismo risulta evidente in tutti quei casi in cui non puoi far supposizioni sul tipo preciso di oggetto con cui hai a che fare. Ti dico subito che una cosa del genere
    codice:
    GreenBox x = new RedBox();
    non si usa, in pratica, mai. Questo è il classico esempio didattico. Il vero utilizzo dell'ereditarietà avviene quando non sai a priori quale tipo di oggetto stai usando. Prendiamo l'esempio di un metodo che deve visualizzare l'area di una figura geometrica. Questo metodo prende come parametro un oggetto di tipo Figura (classe madre di altre sottoclassi più specifiche).
    Questo metodo non sa con quale tipo di oggetto lavorerà: gli verrà passato un Triangolo? Un Quadrato? Un Trapezio? Un Cercio? Boh... lui non lo sa, però sa che di sicuro gli sarà passata una Figura, perchè tutte le classi menzionate prima estendono tale classe.

    La parte più bella e più potente, poi, stà nel polimorfismo: quel metodo deve visualizzare l'area dell'oggetto passato. Ora, un triangolo ha una sua formula per il calcolo dell'area che è nettamente differente dalla formula per il calcolo dell'area di un cerchio, diversa da quella di un Trapezio, e così via... come si procede, quindi? Semplice: tutte le classi prevederanno un metodo calcola(). Ognuna lo implementerà coerentemente, ma sarà il sistema a tempo d'esecuzione preoccuparsi di richiamare quello corretto. Se la Figura passata sarà un Triangolo, richiamerà il metodo calcola() della classe Triangolo, se sarà un Cerchio, richiamerà quela del Cerchio, e così via.

    Forse un esempio aiuta a capire:
    codice:
    // Definisco la classe madre: tutte le altre estendono questa
    public abstract class Figura {
       public int calcola();
    }
    
    // Questa sarà la classe Triangolo (a grandi linee)
    public class Triangolo extends Figura {
       ...
       public int calcola() {
          return (base * altezza) / 2;
       }
       ...
    }
    
    // Classe Cerchio...
    public class Cerchio extends Figura {
       ...
       public int calcola() {
          return (r * r) * Math.PI;
       }
       ...
    }
    
    ...
    Bene... ora vediamo come si comporta in esecuzione:
    codice:
    public class Aree {
       public static void main(String [] a) {
          Triangolo t = new Triangolo();
          ... // Definisco il mio triangolo
          Cerchio c = new Cerchio();
          ... // Definisco il cerchio
          ...
          visualizzaArea(t);
          visualizzaArea(c);
       }
    
       public static void visualizzaArea(Figura f) {
          System.out.println( f.calcola() );
       }
    }
    Come puoi vedere, nel metodo visualizzaArea io non mi preoccupo affatto del tipo di figura che mi viene passato, tanto non mi importa: so che mi verrà passata una Figura, che possiede un metodo calcola(). Non devo fare altro che richiamarlo.

    Spero che come esempio sia stato abbastanza semplice e chiaro. Per altre delucidazioni sono sempre qui.


    Ciao.
    "Perchè spendere anche solo 5 dollari per un S.O., quando posso averne uno gratis e spendere quei 5 dollari per 5 bottiglie di birra?" [Jon "maddog" Hall]
    Fatti non foste a viver come bruti, ma per seguir virtute e canoscenza

  3. #3

    Avanti passo, passo...

    L'esempio da te fatto è sicuramente pertinente con la mia situazione: per correttezza ti posso svelare un retroscena. Oltre al Dispatch dei metodi sto incontrando ovvie difficoltà anche con classi dichiarate abstract e con le interfaccie; per le ultime due però ho trovato sul forum delle discussioni già aperte, che almeno nel caso di abstract mi hanno chiarito qualcosa (le interfaccie ancora sono mistero al 100%), tanto è che mi sono creato questo esempio basandomi su una delucidizione letta in un post, esempio che bada caso è proprio quello dell'area!

    codice:
    abstract class Poligono {
    	int base;
    	int altezza;
    	abstract double area();
    }
    
    class Triangolo extends Poligono {
    	Triangolo(int b, int h) {
    		this.base = b;
    		this.altezza = h;
    	}
    	double area() {
    		return (altezza*base/2);
    	}
    }
    
    class Quadrato extends Poligono {
    	Quadrato(int lato) {
    		this.base = lato;
    		this.altezza = lato;
    	}
    	double area() {
    		return (base*altezza);
    	}
    }
    
    class CalcoloArea {
    	public static void main(String args[]) {
    		Poligono T1 = new Triangolo(10, 5);
    		Poligono Q1 = new Quadrato(3);
    		System.out.println("Area Triangolo: " + T1.area());
    		System.out.println("Area Quadrato: " + Q1.area());
    	}
    }
    Con tale esempio ho iniziato ad intuire quale possa essere l'uso di abstract... vedendo quello fatto da te vedo anche ciò che io nel mio non sono riuscito a fare, cioè chiamare un metodo come visualizzaArea che si comporta in conseguenza dell'oggetto che gli viene passato!
    Forse per qualcuno ho scoperto l'acqua calda ma vi assicuro che dopo anni e anni di procedure gli oggetti sono un po ostici!
    Il mondo è diviso in 10 gruppi di persone... chi capisce il binario e chi no!

    http://www.pratesi.net

  4. #4
    Moderatore di Programmazione L'avatar di LeleFT
    Registrato dal
    Jun 2003
    Messaggi
    17,304
    Beh, per cominciare potremmo passare alle classi abstract.
    Una classe è dichiarata abstract quando al suo interno vi sono dei metodi che non possono, a priori, essere implementati, per scarsità di informazioni. L'esempio della Figura, che torna sempre fuori, è abbastanza intuitivo: non posso implementare il metodo calcola() per calcolare l'area di una figura, se non so di che figura si tratta. Però ho certamente bisogno di raggruppare Triangoli, Cerchi, Trapezi e così via sotto una grande famiglia che li accomuna. Nasce così la classe Figura che di per se non fornisce molte informazioni, però è la base per tutte le altre figure. Di solito ci si crea una gerarchia di ereditarietà, ossia c'è una classe che fa da capostipite dalla quale discendono le classi figlie.
    Di solito (ma questo non è vero in generale) le classi abstract risiedono alle origini della gerarchia, e non sono mai (o quasi, perchè non avrebbe senso) il punto di arrivo della gerarchia.
    Le classi abstract non possono, quindi, essere istanziate. Questo significa che non si può richiamare un costruttore per tali classi attraverso l'operatore new ed il motivo è abbastanza semplice da capire: sono classi che forniscono meno informazioni di quante ne siano richieste per creare un oggetto... immaginiamo di voler istanziare una figura, la prima domanda che sorge è "Che figura?": servono, quindi, ulteriori e più specifiche informazioni, che le classi abstract delegano alle figlie.

    Spero di essere stato sufficientemente chiaro nella spiegazione.

    Ciao.
    "Perchè spendere anche solo 5 dollari per un S.O., quando posso averne uno gratis e spendere quei 5 dollari per 5 bottiglie di birra?" [Jon "maddog" Hall]
    Fatti non foste a viver come bruti, ma per seguir virtute e canoscenza

  5. #5

    La strada è lunga...

    Ok, diciamo che relativamente alle classi abstract un'idea sul loro utilizzo inizio ad avercela, grazie anche all'esempio sulle figure. Adesso come accennavo nel mio post precedente ci sarebbe da fare una digressione sulle interfaccie, delle quali ho capito ben poco. Esiste un esempio semplice mediante il quale è possibile evidenziare il loro utilizzo?
    Forse il libro dove mi sto documentando non è fatto per un principiante di OOP, ma anche alcune risorse che ho trovato in rete trattano l'argomento "interfaccie" esaltandone la potenza senza però fare degli esempi che possano dare un senso a tutto ciò...
    Grazie ancora a chi avrà pazienza!

    Ciao
    Il mondo è diviso in 10 gruppi di persone... chi capisce il binario e chi no!

    http://www.pratesi.net

  6. #6

    Domande per LeleFT

    Ciao LeleFT, senza voler abusare della tua disponibilità avrei da chiederti delle delucidazioni in merito all'esempio della Figura che tu hai fatto in un post presente in questa discussione. Io per esercitarmi ho preso spunto dalle tue indicazioni e mi sono creato il codice sottostante con il fine di prendere confidenza con questi concetti:

    codice:
    abstract class Figura {
    	abstract double calcola();
    }
    
    class Triangolo extends Figura {
    	int base, altezza;
    	Triangolo(int a, int b) {
    		this.altezza = a;
    		this.base = b;
    	}
    	public double calcola() {
    		return ((base*altezza)/2);
    	}
    }
    
    class Quadrato extends Figura {
    	int lato;
    	Quadrato(int l) {
    		this.lato = l;
    	}
    	public double calcola() {
    		return (lato*lato);
    	}
    }
    
    class Cerchio extends Figura {
    	int raggio;
    	Cerchio(int r) {
    		this.raggio = r;
    	}
    	public double calcola() {
    		return (raggio*raggio*Math.PI);
    	}
    }
    
    public class Aree {
    	public static void main(String args[]) {
    		// Triangolo T = new Triangolo(3,6);
    		// Quadrato Q = new Quadrato(5);
    		// Cerchio C = new Cerchio(10);
    		Figura T = new Triangolo(3,6);
    		Figura Q = new Quadrato(5);
    		Figura C = new Cerchio(10);
    		System.out.println("Area Triangolo: ");
    		CalcolaArea(T);
    		System.out.println("Area Quadrato: ");
    		CalcolaArea(Q);
    		System.out.println("Area Cerchio: ");
    		CalcolaArea(C);
    	}
    	public static void CalcolaArea(Figura F) {
    		System.out.println(F.calcola());
    	}
    }
    
    Quindi l'esecuzione:
     
    C:\java_source>javac Aree.java
    
    C:\java_source>java Aree
    Area Triangolo:
    9.0
    Area Quadrato:
    25.0
    Area Cerchio:
    314.1592653589793
    
    C:\java_source>
    Dopo aver verificato la correttezza di quanto da me fatto, ti pongo le seguenti domande in riguardo a cose che mi hanno lasciato dei dubbi riguardo al codice che proponevi tu nel suddetto post:

    1) Perchè dichiari come "public" la classe astratta "Figura" e le sottoclassi che la estendono come "Triangolo", "Quadrato" e "Cerchio"? Intendo sapere se c'è un motivo che può indurre alla dichiarazione "public" rispetto a come ho fatto io e se la soluzione da me adottata può non andar bene per qualche motivo.

    2) Inoltre la dichiarazione di queste classi come "public" comporta che esse devono essere memorizzate in file separati del tipo Triangolo.java, Quadrato.Java e Cerchio.java... o mi sbaglio? Io per adesso ho sempre memorizzato tutte le classi nel solito file che compilo ed eseguo con i comandi "javac" e "java".

    3) Ho letto che un metodo di tipo astratto dichiarato in una classe abstract deve essere sempre dichiarato abstract anch'esso. Perchè tu non dichiari come abstract il metodo calcola?

    4) Nel metodo main ho riportato due modi, entrambi provati, di istanziare gli oggetti: quello commentato è del tipo indicato da te, mentre quello attualmente utilizzato richiama per certi versi una questione trattata all'inizio della discussione riguardo il dispatch. Entrambi funzionano e danno il risultato atteso: mi chiedo quindi quale sia la differenza e con quale criterio scegliere un modo o l'altro?

    Se con calma puoi rispondere a questi quesiti può essere che risulti utile anche ad altri utenti del forum, poichè vedo che questi temi per i principianti sono difficili da capire.
    Mille grazie!
    Il mondo è diviso in 10 gruppi di persone... chi capisce il binario e chi no!

    http://www.pratesi.net

  7. #7
    Moderatore di Programmazione L'avatar di LeleFT
    Registrato dal
    Jun 2003
    Messaggi
    17,304

    Re: Domande per LeleFT

    Originariamente inviato da Felixfree
    1) Perchè dichiari come "public" la classe astratta "Figura" e le sottoclassi che la estendono come "Triangolo", "Quadrato" e "Cerchio"? Intendo sapere se c'è un motivo che può indurre alla dichiarazione "public" rispetto a come ho fatto io e se la soluzione da me adottata può non andar bene per qualche motivo.
    Mah... il motivo di dichiarare o meno public le classi serve per diversi motivi: la loro visibilità all'esterno del package o della classe stessa (nel caso di classi interne). In genere, se non ci sono motivi particolari per dichiarare una classe privata, la si dichiara public oppure, come hai fatto te, senza modificatore.
    L'unica accortezza da prendere è la seguente: se una classe viene dichiarata public deve risiedere in un file .java separato (tranne, ovviamente, le classi interne ad altre classi).

    2) Inoltre la dichiarazione di queste classi come "public" comporta che esse devono essere memorizzate in file separati del tipo Triangolo.java, Quadrato.Java e Cerchio.java... o mi sbaglio? Io per adesso ho sempre memorizzato tutte le classi nel solito file che compilo ed eseguo con i comandi "javac" e "java".
    Esattamente... l'ho detto anche sopra. Io di solito preferisco separare le varie classi in file diversi, in modo da poterle localizzare più facilmente quando il progetto si fa più grande. Quando si ha a che faer con tante classi è bene saperle localizzare con precisione, per poterle andare a modificare in caso di necessità.

    3) Ho letto che un metodo di tipo astratto dichiarato in una classe abstract deve essere sempre dichiarato abstract anch'esso. Perchè tu non dichiari come abstract il metodo calcola?
    Io non l'ho sentito dire, ma potrebbe anche essere. Mi dovrò documentare maggiormente. Comunque, se una classe è abstract e contiene dei metodi non implementati automaticamente essi sono considerabili abstract. Credo che la differenza sia più che altro di tipo logico e/o semantico. In teoria, infatti, una classe abstract può implementare dei metodi, quindi il dichiarare abstract uno o più metodi agevola la distinzione in fase di scrittura del codice.

    4) Nel metodo main ho riportato due modi, entrambi provati, di istanziare gli oggetti: quello commentato è del tipo indicato da te, mentre quello attualmente utilizzato richiama per certi versi una questione trattata all'inizio della discussione riguardo il dispatch. Entrambi funzionano e danno il risultato atteso: mi chiedo quindi quale sia la differenza e con quale criterio scegliere un modo o l'altro?
    Ti dirò. Il metodo attualmente in uso in pratica non si usa mai. Non serve a niente, se non a scopo puramente didaticco, per evidenziare il fatto che si può, ovviamente, istanziare un oggetto di un certo tipo, utilizzando il costruttore di uno dei tipi figli. Questo perchè un figlio è sempre un'istanza della classe madre, mentre non è vero il contrario. La differenza, comunque, è evidente nel momento in cui le classi figlie aggiungano delle caratteristiche che la classe madre non ha: per esempio, la classe Triangolo può prevedere un metodo che stampa l'altezza del triangolo, che è assolutamente inutile nella classe Cerchio. Entrambe le due classi sono figlie di Figura, quindi è possibile istanziare due oggetti nel seguente modo:
    codice:
    Figura triangolo = new Triangolo(3, 6);
    Figura cerchio = new Cerchio(10);
    però se proviamo ad eseguire l'istruzione seguente, verrà generato un errore:
    codice:
    cerchio.stampaAltezza();
    in quanto la classe Cerchio non possiede tale metodo. Da notare che anche la seguente istruzione genera un errore:
    codice:
    triangolo.stampaAltezza();
    benché la classe Triangolo implementi il metodo stampaAltezza(). Questo perchè, a priori, la classe Figura non implementa tale metodo e l'oggetto è dichiarato per essere un oggetto della classe Figura. In questo caso, per poter richiamare il metodo che l'oggetto effettivamente possiede (essendo un oggetto Triangolo), è necessario un cast esplicito:
    codice:
    ((Triangolo) triangolo).stampaAltezza();

    Se con calma puoi rispondere a questi quesiti può essere che risulti utile anche ad altri utenti del forum, poichè vedo che questi temi per i principianti sono difficili da capire.
    Mille grazie!
    Effettivamente questi sono i concetti fondamentali su cui si basa la programmazione Orientata agli Oggetti, una volta entrati in testa, è certo che un grande passo in avanti è stato compiuto e si può cominciare a padroneggiare più agevolmente gli oggetti e si possono cominciare a sfruttare le loro caratteristiche.


    Ciao.
    "Perchè spendere anche solo 5 dollari per un S.O., quando posso averne uno gratis e spendere quei 5 dollari per 5 bottiglie di birra?" [Jon "maddog" Hall]
    Fatti non foste a viver come bruti, ma per seguir virtute e canoscenza

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved.