Newsgroup: it.comp.java
Titolo: ereditarietà+overloading=>strano comportamento...

Ciao a tutti,
vi vorrei sottoporre un comportamento che non riesco a capire proprio. Forse
per mancanze teoriche mie.
Ho una superclasse:
public class SuperClasse {
    public superMetodo() {
        SalvaFile sf = new SalvaFile();
        sf.salva(this);
    }
}
e varie sottoclassi che ereditano il superMetodo
public class SottoClasse extends SuperClasse {
     / *metodi vari ma niente overriding*/
    }
}
la classe SalvaFile č il problema, qui tramite overloading definisco il
metodo salva() che fa operazioni diverse in funzione dell'oggetto che
riceve:
public class SalvaFile {
    public salva(SuperClasse sc) {
        /*set operazioni varie 1*/
    }
    public salva(SottoClasse sc){
        /*set operazioni varie 2*/
    }
}
Il problema sorge quando invoco il metodo superMetoto():
un esempio tipico potrebbe essere:
SottoClasse esempio = new SottoClasse();
esempio.superMetodo();
bene, a questo punto il superMetodo passa la classe stessa che lo invoca ad
alla classe SalvaFile come parametro del metodo salva().
La classe SalvaFile dovrebbe riconoscere l'oggetto come SottoClasse ed
eseguire il "set operazioni varie 2", invece esegue il primo set!
In pratica riconosce l'oggetto come SuperClasse e non come SottoClasse,
l'overloading non funziona a dovere!
Eppure se stampo l'ggetto (System.out.println(oggetto)) questo č di tipo
SottoClasse!
Se il metodo superMetodo() non viene ereditato ma definito in ogni
sottoclasse tutto funziona alla perfezione, viene riconosciuto il metodo
salva() giusto.
Quello che vorrei sapere č perchč??? C'č qualche differenza se un metodo
viene ereditato o definito direttamente da una classe esterna? Anche la piů
piccola che possa influire su una cosa del genere.
Vi ringrazio per la pazienza.
Ciao
Thor

 
Si, c'č differenza.
Nel primo caso hai un "method overriding" e funziona come ti aspetti 
perché la risoluzione del metodo da chiamare avviene a runtime.
Nel secondo č un "method overloading" e non funziona come ti aspettavi 
perché viene risolto in fase di compilazione. In quella fase il 
compilatore in
   sf.salva(this);
sa solo che this č di tipo SuperClasse quindi lega la chiamata al metodo
   salva(SuperClasse sc)
P.S.: si vede che ho preso la certificazione Sun o no? ;-)
Ciao.
\mirko

 Vi ringrazio per la pazienza.
Č un comportamento corretto, infatti il superMetodo effettua la chiamata a
salva(this), dove this č un oggetto di tipo SuperClasse, per cui viene
richiamata la funzione salva(SuperClasse). Se fai l'overloading in
SottoClasse copiando pari pari il codice di SottoClasse, in quel caso this č
di tipo SottoClasse, e viene chiamato salva(SottoClasse).
Rivedi un pň come hai progettato le classi, perchč mi sembra che per come le
hai messe tu tutti i vantaggi dell'OOP vanno a farsi friggere; infatti se ad
esempio derivassi un'altra classe da SuperClasse dovresti riscrivere
un'altra funzione salva in SalvaFile, che č esattamente quello che vuole
evitare l'OOP.
Ciao,
Cecco

 P.S.: si vede che ho preso la certificazione Sun o no? ;-)
Da manuale direi :)))
Ciao
Thor

 evitare l'OOP.
Lo so che vuole essere evitata ma nel mio caso è richiesta perchè salva()
salva appunto i dati  file diversi in funzione del tipo di oggetto ricevuto,
anzi per alcuni oggetti usa pure un database quindi per forza devo
riscriverle.
Le idee sono più chiare ma l'unica soluzione che ho senza riprogettare le
classi SalvaFile e altre collegate è quella di non sfruttare l'ereditarietà
ma come dici tu è una soluzione bruttissima dal punto di vista progettuale e
trattandosi di un progetto per un esame di Ing. del software non è carina la
cosa...
Ciao
Thor

 wrote in message
news:Y4aua.42238$
DN.10...@tornado.fastwebnet.it

-- 
Posted via Mailgate.ORG Server - 
http://www.Mailgate.ORG

 ha scritto nel messaggio
news:veaua.42250$DN.10...@tornado.fastwebnet.it...

Beh, io credo che il codice specifico per un oggetto vada inserito
nell'oggetto stesso, sovraccarcando il metodo; tra l'altro, quello che
cerchi di fare tu, è risolvibile solo con la RTTI, che andrebbe usata solo
in casi particolari, mentre in questo caso può essere evitata.
Ciao,
Cecco

 Che ne dici ? Non ti sembra + OOP ?
Sicuramente è molto OOP, il problema è che dovrei passare il parametro s che
io non posso passare, infatti superMetodo() serve per salvare l'oggetto da
cui viene invocato su file tramite un'altra classe Salva appunto. In pratica
superMetodo() è un "salva()" generico che non specifica se viene salvato su
File o su Db ed io, per specifica richiesta, devo rendere possibile cambiare
metodo di salvataggio cambiando il codice nella sola classe Salva.
Altrimenti implementerei il salvataggio direttamente e festa finita...
Riporto l'esempio concreto per maggior chiarezza nel caso interessi:
Superclasse:
public class Documento {
    public void inserisci(){
        La funzione dovrebbe richiamare la classe SalvaDoc che in base al
tipo di sottoclasse da cui viene chiamata salva su un file o su db
    }
    public void rimuovi(){...}
    ...
}
Sottoclassi:
public class Fattura extends Documento {
}
public class Ordine extends Documento {
}
Classe che si occupa di salvare i dati fisicamente:
public class SalvaDoc{
    public void salva(Fattura f){
    /*salva in un file*/
    }
    public void salva(Ordine o){
    /*salva in un db*/
    }
}
/* questa classe deve rimanere separata perchè devo rendere la classe
Documento e sottoclassi indipendenti dall'implementazione del salvataggio,*/
Spero di essere stato chiaro nella descrizione.
Thor

 Spero di essere stato chiaro nella descrizione.
Puoi utilizzare una gerarchia come quella proposta da Orso:
interface SalvaDoc {
    public void salva(Documento doc);
}
class SalvaDocFile implements SalvaDoc {
    public void salva(Documento doc) {/* salva in un file */}
}
class SalvaDocDB implements SalvaDoc {
    public void salva(Documento doc) {/* salva in un DataBase */}
}
In Documento magari aggiungi per composizione un oggetto SalvaDoc, che in
base al tipo di salvataggio che ti serve inizializzi di conseguenza, o dal
costruttore della classe derivata da Documento o tramite un metodo specifico
(del tipo "addSalvaDoc(new SalvaDocDB())").
Mi sembra una soluzione piů corretta, secondo te puň andare?
Ciao,
Cecco

 public class SalvaDoc{
     public void salva(Documento d){
        if (d instanceof Fattura) {
             //Salva in un modo
        } else if (d instanceof Ordine) {
            //Salva in un altro modo
        } else {
            //Altro caso
        }
     }
 }
Cosa ne pensi?
Ciao
Franco

 Altrimenti implementerei il salvataggio direttamente e festa finita...
Riporto una soluzione che mi viene in mente, pero' mi sembra molto 
macchinosa e non mi esprimo sulla "purezza" dal punto di vista OO.
**********************************************************************
public abstract class Saver{
   protected void salvaFile(Document d) { /* salva su file ... */ }
   protected void salvaDB(Document d) { /* salva su DB ... */ }
   abstract void salva(Document d);
}
public class SaverFattura extends Saver {
   void salva(Document d) { salvaFile(d); }
}
public class SaverOrdine extends Saver {
   void salva(Document d) { salvaDB(d); }
public abstract class Document{
   protected Saver saver;
   public void salva() { saver.salva(this); }
   ...
}
public class Fattura extends Document{
   public Fattura() { saver = new SaverFattura(); }
   ...
}
public class Ordine extends Document{
   public Ordine() { saver = new SaverOrdine(); }
}
**********************************************************************
O forse è meglio cosi'?
**********************************************************************
/* ... */
public abstract class Document {
   protected abstract Saver getSaver();
   public void salva() { getSaver().salva(this); }
}
public class Fattura extends Document {
   protected Saver getSaver() {
     return new SaverFattura();  // Anche in questo caso il Saver può
                     // essere memorizzato invece che ricreato di volta
                     // in volta. Il concetto non cambia
   }
}
/* ... */
**********************************************************************
Occorre quindi ripetere, con una piccola variazione, il metodo 
getSaver() in ogni sottoclasse di Document. L'unico modo per evitare 
questo penso che sia il seguente, ma farà accapponare la pelle ai 
puristi dell'OO (e non solo ai puristi).
**********************************************************************
/* ... */
public abstract class Document {
   public void salva() { getSaver().salva(this); }
   protected Saver getSaver(){
     try{
       String nomeSaver = "Saver" + this.getClass().getName();
       Class csvr = Class.forName();
       Saver svr = (Saver)cs.newInstance();
       return svr;
     }
     catch(ClassNotFoundException ex) { /* ... */ }
     catch(IllegalAccessException ex) { /* ... */ }
     catch(InstantiationException ex) { /* ... */ }
   }
}
public class Fattura exteds Document {
   /* ... */
}
**********************************************************************
In pratica il nome della classe del Saver viene definito in base alla 
sottoclasse di Document.
Prima di utilizzare una cosa del genere ci penserei non una ma almeno 10 
volte (e alla fine non lo farei comunque).
Pero' fa esattamente quello che serve a te: puoi modificare il metodo di 
salvataggio cambiando solamente le classi SaverXXXXXX e non sei 
obbligato a riscrivere lo stesso metodo in ogni sottoclasse di Document.
Una cosa a cui stare attenti: se ti azzardi a definire una sottoclasse 
di fattura (chiamiamola Pippo) sei obbligato a definire anche una classe 
SaverPippo che estenda Saver.

Altrimenti implementerei il salvataggio direttamente e festa finita...
[CUT]
Purtroppo non ho il tempo di essere piů dettagliato, ma vorrei darti un 
suggerimento.
Non so dirti se sia la soluzione _migliore_ al tuo specifico problema, ma 
ti invito a visionare il Design Pattern "Visitor".
Ad esempio
http://home.earthlink.net/~huston2/dp/visitor.html
Ma meglio sul libro "Design Patterns" Gamma a ltri
HTH
-- 
Ciao
	Fabrizio C.