Newsgroup: it.comp.java
Titolo: Problema nell'applicazione del polimorfismo

Salve a tutti,
vorrei chiedere un opinione su come risolvere un problema che mi
capita sovente quando cerco di sfruttare il polimorfismo. Scrivo in
questo gruppo in quanto sono un programmatore Java, anche se in realtà
il problema più in generale è di programmazione orientata agli
oggetti. Siccome gli esempi valgono più di mille parole parto subito
con uno:
Supponiamo di avere una gerarchia di classi che rappresentino le
classiche funzioni della matematica. Alla radice della gerarchia
abbiamo una classe astratta AbstractFunction e come sottoclassi varie
implementazioni: LinearFunction (da cui potrebbe ereditare
ConstantFunction), HyperbolicFunction, ParabolicFunction e così via.
Definiamo a livello di classe astratta delle operazioni astratte
d'interfaccia che permettano di eseguire operazioni tra funzioni, ad
esempio somme (add), sottrazioni (subtract), ecc...
Un'ipotetica definizione astratta potrebbe essere la seguente:
public abstract Function add(Function aFunction);
ed un esempio di chiamata potrebbe essere la seguente:
ConstantFunction _function1 = new LinearFunction(..args..);
HyperbolicFunction _function2 = new HyperbolicFunction(...args...);
Function _function3 = _function1.add(_function2);
Nel corpo dell'implementazione di ogni singola sottoclasse di Function
posso sempre fare assunzioni sull'istanza dell'oggetto su cui viene
eseguito il metodo (nell'esempio sopra SO che sto facendo la somma su
una ConstantFunction), ma non posso assumere nulla sulla funzione
passata come parametro (in quanto è di tipo Function). Siccome il modo
in cui viene eseguita la somma tra due funzioni dipende esplicitamente
dal tipo di funzioni coinvolte (non esiste una regola generale che
valga per tutte), volevo sapere se c'era un modo elegante per
risolvere tale problema (senza ricorrere all'instanceof).
Spero di essere stato chiaro.
Grazie.
Saluti.
stereat

ciao,
L'unica cosa che mi viene in mente è di generalizzare per tipi di
funzione (interfacce) e di fare overloadare il metodo add per ogni
tipo diverso di metodo di lavoro: un esempio:
usare:
public Function add (hyperbolicFuncion);
public Function add (LinearFuncion);
public Function add (ConstantFuncion);
invece di :
public Function add (Funcion);
ciao gabry  spero di  essermi spiegato!!!!!


 risolvere tale problema (senza ricorrere all'instanceof).
L'istanceof è comodo (centralizzi il codice) ma "poco OOP".
Piuttosto mi sembra molto sensato il suggerimento di usare vari metodi 
"add" (l'overload è lì apposta).
Questo perché a un diverso metodo "add" corrisponde un diverso 
algoritmo, e quindi è corretto (e decisamente più elegante 
dell'instanceof) che venga tenuto separato.
Ciao,
Paolo

 risolvere tale problema (senza ricorrere all'instanceof).
praticamente, nel metodo add() hai bisogno di codice specializzato non
solo per ogni tipo di this (cosa che risolveresti con il polimorfismo)
ma anche per ogni tipo di parametro (cosa che non risolvi col
polimorfismo).
la soluzione "elegante" non esiste, quella che ti è stata suggerita di
fare un overload del metodo add() per ciascun tipo è imho la migliore.

Quindi in sostanza, se ho capito bene da quanto letto, dovrei creare
un'interfaccia per ogni tipo di funzione e usare l'overloading. Anche
se non capisco bene perchè sia più utile utilizzare le interfacce
piuttosto che le classi astratte, vado avanti con l'esempio seguendo
il vostro suggerimento:
- creo un'interfaccia generale FunctionInterface che elenca i vari
metodi "add" astratti, ad esempio: add(LinearInterface),
add(HyperbolicInterface), ...
- da FunctionInterface derivo interfacce specifiche: ad esempio avremo
una LinearInterface (da cui deriva una ConstantInterface), una
HyperbolicInterface, e così via.
- creo una classe che implementa ogni tipo interfaccia derivata da
FunctionInterface.
Ora, supponiamo io voglia aggiungere una nuova classe di funzioni.
Seguendo la filosofia dell'OO, se avessi un buon progetto, sarebbe
sufficiente estendere FunctionInterface con quella nuova e
implementare in una nuova classe i vari "add". Tutto bene se non fosse
che, aggiungere una nuova classe di funzioni significa aggiungere un
nuovo tipo di add (add(NewFunctionInterface)) ad ogni interfaccia e ad
ogni classe. E, se il numero di funzioni è abbastanza grande, questo
lavoro di aggiornamento diventa "faticoso" e, sinceramente, non ho mai
visto nessuno che per aggiungere funzionalità ad una libreria vada a
modificare le classi che la compongono.
Ditemi cos'è che mi son perso, in particolare che ruolo giocano le
interfacce? Perchè sono più appropriate delle classi astratte? Come
posso risolvere il problema dell'estendibilità?
Vi ringrazio.
Saluti.
Matteo


 quindi poi verificarne il tipo).
Purtroppo mi sono approciato all'OO con Java, però mi interesserebbe
capire (magari anche con del codice), COME passare metodi come oggetti
possa risolvere il mio problema.
Ti ringrazio.
Saluti.
Matteo

 [..] mi interesserebbe capire (magari anche con del codice),
 > COME passare metodi come oggetti possa risolvere il mio problema.
Partendo dal tuo esempio:
ConstantFunction _function1 = new LinearFunction(..args..);
HyperbolicFunction _function2 = new HyperbolicFunction(...args...);
Function _function3 = _function1.add(_function2);
dentro il metodo add di function1 tu farai (presumo, oppure dovresti 
mostrare il codice) qualcosa del tipo:
_function2.unMetodo ed elaborerai e valori restituiti da questo metodo 
confrontandoli (aggiungendoli) a quelli gią contenuti in _function1.
Per farlo perņ devi discriminare il tipo di _function2 e chiamare il 
metodo corrispondente.
Se potessi passare direttamente il metodo che serve tutto ciņ si 
ridurrebbe a un:
Function _function3 = _function1.add(_function2.unMetodo);
Dentro add a questo punto "l'origine" di unMetodo č assolutamente 
ininfluente.
Se poi avessi a disposizione anche l'overload degli operatori il tutto 
si potrebbe ridurre a:
_functionSum = _function1 + _function2
Ma a questo punto tanto vale passare direttamente a un linguaggio 
funzionale... ;)
Ciao,
Paolo

 modificare le classi che la compongono.
Si puo' dire che sia giusto cosi', in quanto a questa Function prevede
delle diverse implementazioni di add().
Se l'add() della tua nuova NewFunction funzionasse allo stesso modo
della add() della classe che estende, non dovresti scrivere una linea
di codice.
L'alternativa e' quella di non definire l'operazione "add" come
metodo, ma come classe: potresti avere una classe di utilities
Functions (in modo similare alla classe java.util.Arrays), che
implementa tutte le varianti del tuo metodo add(), cosi' quando
aggiungi una nuova funzione, devi modificare solo quella classe. In
questo modo risolvi anche piu' facilmente il problema della proprieta'
commutativa della somma (ad esempio), perche' non e' bello dover
implementare 2 volte il metodo add() se non e' necessario.
--
SoulSpirit


Ok. Faccio una classe utility chiamata Functions che implementa l'add
per ogni tipo di funzione. Supponiamo di avere solamente
LinearFunction, HyperbolicFunction e ParabolicFunction. Gli add che
dovrei aggiungere sarebbero i seguenti?
public static Function add(LinearFunction aLinearFunction,
HyperbolicFunction aHyperbolicFunction);
public static Function add(LinearFunction aLinearFunction,
ParabolicFunction aParabolicFunction);
public static Function add(HyperbolicFunction aHyperbolicFunction,
ParabolicFunction aParabolicFunction);
Si?
Speriamo questa volta di aver capito...

 Si?
E se devi sommare una HyperbolicFunction a una ParabolicFunction?
Con questo metodo ogni volta che aggiungi un tipo funzione devi 
aggiungere n metodi, dove n č un coefficiente binomiale simmetrico e 
tende a crescere con una certa rapiditą.
Per aiutarti a risolvere il problema: cosa fai nel metodo "add"?
Hai chiarito che per ogni tipo di funzione cambi algoritmo, puoi fare 
qualche esempio?
Ciao,
Paolo

 Per aiutarti a risolvere il problema: cosa fai nel metodo "add"?
Per spiegarmi meglio e evitarti di perdere tempo, faccio un esempio:
se devi sommare una funzione iperbolica (prendiamo la funzione seno 
iperbolico f(x)=(e^x - e^-x)/2) e una lineare (f(x)= ax + b) cosa 
ottieni come risultato? Da quello che scrivi ottieni una classe di tipo 
Function, ma come fai a generarla?
Cosa contengono, la definizione e/o una serie di punti per rappresentarle?
Ciao,
Paolo

 Cosa contengono, la definizione e/o una serie di punti per rappresentarle?
Purtroppo ho fatto diversi ragionamenti su carta e non viene fuori
nulla!!! (Che il pattern algorithm possa essere utile? adesso vado a
dare un'occhiata...)

 nulla!!!
Il problema di fondo è che ottenere i punti partendo dalla funzione è 
semplice, mentre il passaggio contrario è piuttosto arduo (e non sempre 
possibile: più funzioni possono passare per gli stessi punti).
Quindi nelle funzioni ottenute tramite somma perderesti la definizione, 
avresti solo la lista di elementi (punti, segmenti).
 > (Che il pattern algorithm possa essere utile? adesso vado a
 > dare un'occhiata...)
Per la rappresentazione grafica potresti usare curve bézier cubiche, ma 
la vedo dura...
Ciao,
Paolo

 Corretto?
Certo è corretto, ma non è quello che desidererei ottenere. Era
qualcosa a cui avevo già pensato e che molto probabilmente applicherò,
ma pur restanto con la domanda in testa "c'è un modo migliore?".
Saluti.
Matteo

 lineari. [...]
Certo, č chiarissimo e sicuramente questa č una soluzione 
matematicamente migliore e piů elegante.
Lo "svantaggio" č che in questo caso hai classi decisamente diverse, con 
attributi diversi e un metodo somma diverso, per cui i metodi somma da 
implementare saranno molti. Cosě come avviene in matematica.
Buon lavoro! ;)
Paolo