View Full Version : [JAVA]ereditarietà
prazision
16-04-2005, 18:41
finora ho utlizzato java per creare delle classi abbastanza semplici che ho utilizzato nelle mie pagine jsp:
una classe che mi genera una password, delle classi(e delle servlet) che mi generano dei file pdf, ecc.
approfondendo java ho studiato(per ora in maniera abbastanza generica) l'ereditarietà, il polimorfismo ecc.
ecco mi chiedo: in casi come il mio, dove al massimo si tratta di progettini con 4 o 5 classi, conviene(insomma si usa) creare comunque delle classi astratte ed utilizzare l'ereditarietà??
nel senso che a me , in progettini così semplici, non mi sembra che se ne possa trarre un gran beneficio (probabilmente l'inesperienza e l'ignoranza non mi fanno vedere le cose come stanno)
aspetto consigli
grazie
Da un certo punto di vista hai ragione tu, l'ereditarietà è uno strumento potente, ma va usato con criterio, e in progetti "semplici" (non ci sono progetti semplici! :D ) potrebbe generare più problemi di quelli che sarebbe eventualmente in grado di risolvere.
Diverso è il discorso, se tu dovessi decidere di riutilizzare il codice che hai creato per altri progetti: trattandosi, come dici, di componenti estremamente semplici ma fondamentali, è altamente probabile che in futuro tu possa utilizzarli nuovamente. Qualora ciò dovesse accadere, averli strutturati fin da principio sfruttando rigorosamente l'ereditarietà potrebbe giovarti non poco...
prazision
16-04-2005, 19:51
penso d'aver capito cosa intendi.
magari dei metodi sono generici e riutilizzabili in + progetti(faccio un esempio eh) e conviene pertanto scriverli in classi padri.
è ovvio che ora, limitandomi ad utilizzare le classi in un singolo progetto, non riesco a vedere l'utilità di tale approccio
cmq grazie
Certo, oppure potresti voler usare le interfacce anzichè le classi per poter un giorno estendere il tuo progetto con nuove funzionalità non previste, il tutto senza dover riscrivere (idealmente :sofico: ) le classi vecchie per farle funzionare con quelle nuove... con tanti vantaggi: possibilità di fare test, passaggio graduale tra un progetto e un altro, eccetera.
finora ho utlizzato java per creare delle classi abbastanza semplici che ho utilizzato nelle mie pagine jsp:
una classe che mi genera una password, delle classi(e delle servlet) che mi generano dei file pdf, ecc.
approfondendo java ho studiato(per ora in maniera abbastanza generica) l'ereditarietà, il polimorfismo ecc.
ecco mi chiedo: in casi come il mio, dove al massimo si tratta di progettini con 4 o 5 classi, conviene(insomma si usa) creare comunque delle classi astratte ed utilizzare l'ereditarietà??
nel senso che a me , in progettini così semplici, non mi sembra che se ne possa trarre un gran beneficio (probabilmente l'inesperienza e l'ignoranza non mi fanno vedere le cose come stanno)
aspetto consigli
grazie
Come ti hanno gia' risposto giustamente non esistono progetti semplici e l'ereditarieta' e' uno strumento potente e come tale deve essere usato solo quando e' necessario.
Il problema ora e' capire quando e' necessario.
La regola e' che si eredita' una classe da un'altra nel momento in cui si deve modellare una relazione "is a" ("E' un" in italiano). Questa e' la regola generale. Una regoletta pratica e' usare l'ereditarieta' quando vuoi che un oggetto sia usabile in tutte le condizioni al posto dell'oggetto dal quale deriva; si chiama principio di sostituzione. Non si usa l'ereditarieta' per estendere le funzionalita' di una classe. Esistono tecniche piu' efficaci per risolvere questo problema.
Lo so che probabilmente ti hanno detto il contrario all'universita' o a scuola, ti avranno parlato di riutilizzabilita' del codice, di codice generico da estendere, di come l'ereditarieta' sia lo strumento per raggiungere la chimera della totale riutilizzabilita'. Dimenticatene :)
Ti faccio qualche esempio per chiarire il discorso. Immagina una classe astratta (interfaccia in Java) di nome IRunnable, che definisce un metodo astratto run(). Ora immagina di avere una classe Command che esegue un qualche comando, magari in risposta ad un input dell'utente. Ha molto senso che la classe Command erediti dalla classe astratta (interfaccia) IRunnable ed implementi il metodo run() per eseguire un'operazione in risposta all'input dell'utente. Il codice di "dispatch" dei comandi, vedra' oggetti del tipo IRunnable e si limitera' ad eseguire il metodo run(): questa situazione e' un classico esempio di applicazione del principio di sostituzione perche' un oggetto di classe Command puo' essere in tutti gli aspetti usato "come se fosse" di tipo IRunnable. In questa situazione l'Ereditarieta' ha molto senso.
Ora immagina di voler implementare una classe Command che al momento di eseguire il comando stampi in un qualche file di log una stringa che rappresenta l'operazione eseguita. Come implementeresti questa classe?
1) Crei una nuova classe LoggedCommand che eredita da Command e prima di eseguire il metodo run() del comando scrive il messaggio di log:
class LoggedCommand extends Command
{
// ...
void run()
{
logger.print("logging command");
super.run();
}
}
2) Nel metodo run() della classe Command aggiungi un if che scrive sul file di log in caso il logging sia attivato:
void run()
{
if (logger != null)
logger.print("logging command");
// codice di esecuzione del comando
}
3) Crei un "adapter" che accetta un oggetto di tipo IRunnable e nel suo metodo run() stampa il messaggio di log e poi esegue il metodo run dell'oggetto che gli e' stato passato (delega):
class LoggingRunnable extends IRunnable
{
IRunnable target;
LoggingRunnable(IRunnable target)
{
this.target = target;
}
void run()
{
logger.print("add logging here");
target.run();
}
}
Quale delle tre soluzioni sceglieresti? Prova a rispondere sulla base di quello che ho scritto prima, la risposta secondo me e' molto interessante e non e' affatto banale e ne puo' scaturire una discussione interessante.
Ti do' un suggerimento: quando hai da fare una scelta mentre programmi scegli sempre la soluzione che sia "il piu' semplice possibile, ma non piu' semplice" ("As simple as possible, but not simpler").
PS. Scusatemi se ho fatto casino con la sintassi fra Java e C#, ma non ho compilato il codice.
prazision
17-04-2005, 13:37
ne puo' scaturire una discussione interessante.
si, ma solo se partecipa anche quacluno unpo'+ ferrato di me :mc:
dunque:
intanto cosa intendi per 'codice di "dispatch" dei comandi'?
poi non ho capito perchè nell'esempio date fatto "l'Ereditarieta' ha molto senso";
per quanto riguarda l'indovinello ( :D ) escluderei la soluzione 2 in modo da conservare la mia classe Command.
la terza soluzione mi sembra forse la più elastica( e quindi la preferisco alla prima) perchè consente di passare a LoggingRunnable altri eventuali oggetti (nel senso di altre classi che estendono IRunnable )
tieni conto che sono andato molto a intuito, è la prima volta che ragiono su cose simili
cmq grazie
prazision
17-04-2005, 13:59
pensandoci bene posso raffigurarmi il perchè nel tuo esempio(magari estendendolo unpo') l'ereditarietà ha una certa importanza
intanto cosa intendi per 'codice di "dispatch" dei comandi'?
poi non ho capito perchè nell'esempio date fatto "l'Ereditarieta' ha molto senso";
Hai ragione, scusa. Per "dispatch" intendo il codice che si occupa di smistare l'esecuzione dei comandi.
Nell'esempio che ho fatto (IRunnable e Command), l'ereditarieta' ha molto senso perche' un oggetto di tipo Command puo' essere usato ovunque un oggetto di tipo IRunnable puo' essere usato. In altre parole puoi sempre ignorare il fatto che un oggetto sia di tipo Command e usarlo esattamente come se fosse di tipo IRunnable.
per quanto riguarda l'indovinello ( :D ) escluderei la soluzione 2 in modo da conservare la mia classe Command.
la terza soluzione mi sembra forse la più elastica( e quindi la preferisco alla prima) perchè consente di passare a LoggingRunnable altri eventuali oggetti (nel senso di altre classi che estendono IRunnable )
tieni conto che sono andato molto a intuito, è la prima volta che ragiono su cose simili
Va benissimo, non ti preoccupare.
La risposta esatta e'... dipende :)
Quale delle tre soluzioni scegli dipende dal problema che devi risolvere e da cio' che stai scrivendo e dalla sua complessita'.
Nel 99%, appena ho il problema di dover loggare un comando, io sceglierei la soluzione numero 2 perche' e' la piu' semplice e veloce da implementare. E come ti dicevo prima, devi sempre scegliere la soluzione piu' semplice che risolve il tuo problema salvo complicarla e renderla piu' flessibile solo quando hai bisogno di quella flessibilita'. Ad esempio, la soluzione numero 3, come hai detto giustamente tu, e' piu' flessibile delle altre due, ma questa flessibilita' si paga con una classe in piu' da scrivere (l'Adattatore) e con il suo codice in piu' da scrivere, debuggare, testare e mantenere.
Se ti serve solo loggare la classe di tipo Command e nient'altro, scrivere un Adattatore e' un peso che ti porti dietro nel design inutile perche' non stai sfruttando la flessibilita' che ti garantisce, ma, presumibilmente, aspetti di poterla sfruttare in seguito caso mai ce ne fosse bisogno. Ma nel frattempo paghi il costo e quel momento in cui ti serve la flessibilita' potrebbe non arrivare mai (e di solito non arriva). Pagheresti in anticipo dei soldi per un oggetto che magari potrebbero darti in seguito (o magari no)? Ovviamente no, ti terrai quei soldi e li spenderai quando il bene sara' disponibile. Quando programmi il principio e' uguale: non pagare mai il costo associato con un design piu' flessibile se non hai un bisogno immediato e tangibile ed un problema da risolvere.
Ritorno all'esempio: nel momento in cui ti servira' loggare comandi di tipo diverso, con la soluzione 2) dovrai aggiungere quell'if in tutte le classi loggabili, potenzialmente molte, una soluzione chiaramente non ideale. Quando sarai in quella situazione ed avrai quella necessita' (se l'avrai), potrai riorganizzare il codice (refactoring) e spostare la logica di logging in una classe Adattatore. Applicherai quello che in gergo si definisce "Muovere l'Embellishment (Abbellimento) in un Adapater (Adattatore)". Fino a quel momento l'if (soluzione 2) risolve bene il problema ed e' la soluzione piu' semplice. La semplicita' e' il primo valore che devi seguire quando programmi.
Perche' la soluzione 3 e' preferibile alla 1? Qui entra in gioco l'esperienza. Dall'esperienza io so che creare una classe LoggingCommand ereditata da Command puo' portare a situazioni in cui e' necessario avere un tipo di comando particolare che puo' essere loggato o meno, con conseguente esplosione nel numero delle classi. Da qui all'avere classi tutte del tipo NotLoggingSpecificCommand e LoggingSpecificCommand il passo e' brevissimo e questa situazione diventa velocemente molto complicata da gestire. L'esperienza e la ricerca di semplicita' mi dicono che se avessi bisogno di piu' flessibilita' la soluzione 3 e' preferibile alla 1.
In sintesi: l'ereditarieta' e' uno strumento potete ma che va usato con molta attenzione, perche' puo' portare notevoli complicazioni ed un design, in definitva, peggiore. Eredita solo quando ti e' strettamente necessario, all'inizio eredita' solo nel caso in cui ti ho detto qui, quando puoi applicare il principio di sostituibilita'.
prazision
18-04-2005, 15:16
appena posso ci do un occhio
grazie
prazision
18-04-2005, 20:27
Hai ragione, scusa. Per "dispatch" intendo il codice che si occupa di smistare l'esecuzione dei comandi.
diciamo che se fai un progettino che visualizzi dalla console il "dispatch" è il codice contenuto nel metodo main, no?
Nell'esempio che ho fatto (IRunnable e Command), l'ereditarieta' ha molto senso perche' un oggetto di tipo Command puo' essere usato ovunque un oggetto di tipo IRunnable puo' essere usato. In altre parole puoi sempre ignorare il fatto che un oggetto sia di tipo Command e usarlo esattamente come se fosse di tipo IRunnable.
si ma detto così a me non sembra tanto utile (addirittura ti vado contro :mbe: ); un esempio che mi sembra più appropriato è:
ho una classe Programma.java che chiama un metodo
Distruggi(IRunnable iRun)
poi ho varie classi che estendono IRunnable : Command,Command2,ecc.
ecco a seconda di quale classe(sto pensando ad esempio ad un parametro che viene passato dall'utente di volta in volta, un parametro che indica appunto la classe) passi a Distruggi() questo metodo si comporta in maniera differente.
così mi spiego il perchè ho usato l'ereditarieta'
(so che è un esempio unpo' del c****) ma spero tu abbia capito
Va benissimo, non ti preoccupare.
La risposta esatta e'... dipende :)
Quale delle tre soluzioni scegli dipende dal problema che devi risolvere e da cio' che stai scrivendo e dalla sua complessita'.
Nel 99%, appena ho il problema di dover loggare un comando, io sceglierei la soluzione numero 2 perche' e' la piu' semplice e veloce da implementare. E come ti dicevo prima, devi sempre scegliere la soluzione piu' semplice che risolve il tuo problema salvo complicarla e renderla piu' flessibile solo quando hai bisogno di quella flessibilita'. Ad esempio, la soluzione numero 3, come hai detto giustamente tu, e' piu' flessibile delle altre due, ma questa flessibilita' si paga con una classe in piu' da scrivere (l'Adattatore) e con il suo codice in piu' da scrivere, debuggare, testare e mantenere.
Se ti serve solo loggare la classe di tipo Command e nient'altro, scrivere un Adattatore e' un peso che ti porti dietro nel design inutile perche' non stai sfruttando la flessibilita' che ti garantisce, ma, presumibilmente, aspetti di poterla sfruttare in seguito caso mai ce ne fosse bisogno. Ma nel frattempo paghi il costo e quel momento in cui ti serve la flessibilita' potrebbe non arrivare mai (e di solito non arriva). Pagheresti in anticipo dei soldi per un oggetto che magari potrebbero darti in seguito (o magari no)? Ovviamente no, ti terrai quei soldi e li spenderai quando il bene sara' disponibile. Quando programmi il principio e' uguale: non pagare mai il costo associato con un design piu' flessibile se non hai un bisogno immediato e tangibile ed un problema da risolvere.
Ritorno all'esempio: nel momento in cui ti servira' loggare comandi di tipo diverso, con la soluzione 2) dovrai aggiungere quell'if in tutte le classi loggabili, potenzialmente molte, una soluzione chiaramente non ideale. Quando sarai in quella situazione ed avrai quella necessita' (se l'avrai), potrai riorganizzare il codice (refactoring) e spostare la logica di logging in una classe Adattatore. Applicherai quello che in gergo si definisce "Muovere l'Embellishment (Abbellimento) in un Adapater (Adattatore)". Fino a quel momento l'if (soluzione 2) risolve bene il problema ed e' la soluzione piu' semplice. La semplicita' e' il primo valore che devi seguire quando programmi.
fin quì mi sembra tutto limpido
Perche' la soluzione 3 e' preferibile alla 1? Qui entra in gioco l'esperienza. Dall'esperienza io so che creare una classe LoggingCommand ereditata da Command puo' portare a situazioni in cui e' necessario avere un tipo di comando particolare che puo' essere loggato o meno, con conseguente esplosione nel numero delle classi. Da qui all'avere classi tutte del tipo NotLoggingSpecificCommand e LoggingSpecificCommand il passo e' brevissimo e questa situazione diventa velocemente molto complicata da gestire.
mi pare di capire che tu quì dici:
ci sono varie classi : Command,Command1 ecc.; alcune vanno "loggate", altre no.
se uso il metodo 1 dovro' costruirmi tanti LoggedCommand/NonLoggedCommand quanti sono le classi Command mentre se uso il metodo 3 mene basteranno 2 di classi.
è così?
grazie di tutto, ho dato un occhio al tuo link e ti dico che durante la messa del mio matrimonio si ascolteranno non solo Fugazi ma anche S.Youth, Wire, Girls VS Boys,ecc.ecc :cool:
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.