No, non sono diventato un membro del club degli ammiratori di Massimo Catalano (ricordate "Quelli della notte"?). E' solo un modo per esprimere un concetto che differenzia i linguaggi a oggetti di scripting da quelli compilati. I primi genericamente sono blandamente tipizzati, i secondi invece fortemente tipizzati. Questo impatta sul discorso delle interfacce che facevo in precedenza: nel primo caso (Ruby o Python, ad esempio) il fatto che io esprima un'interfaccia dei miei oggetti è solamente un aiuto che do a chi legge il mio programma: gli comunico esplicitamente come ho pensato il design a oggetti della mia soluzione. Nel secondo caso (Java o C#, ad esempio), senza un'interfaccia, il metodo polimorfico semplicemente non funziona.
Nel secondo caso, per tornare alle anatre, bisogna dire al compilatore: guarda che ti sto passando un'anatra, comportati di conseguenza! Nel primo, all'interprete, non dico niente: se starnazza come un'anatra, allora è un'anatra. Punto e basta.
Un esempio polimorfico in Java e in Ruby ci può aiutare a capire meglio. Cominciamo da un linguaggio fortemente tipizzato come Java:
package anatre;
import java.util.ArrayList;
import java.util.Collection;
interface IGallinaceo
{
String starnazza();
}
public class Anatra implements IGallinaceo
{
@Override
public String starnazza()
{
return "Sono un'anatra e starnazzo!";
}
}
public class Oca implements IGallinaceo
{
@Override
public String starnazza()
{
return "Sono un'oca e starnazzo!";
}
}
public final class Cortile
{
public static void main(String[] args)
{
Collection<IGallinaceo> cortile =
new ArrayList<IGallinaceo>();
cortile.add(new Anatra());
cortile.add(new Oca());
cortile.add(new Anatra());
for (IGallinaceo gall : cortile)
{
System.out.println(gall.starnazza());
}
}
}
Come si vede, le due classi Ocae Anatra implementano esplicitamente l'interfaccia IGallinaceo, cioè sono (IS-A) a tutti gli effetti dei Gallinacei e il compilatore lo sa a compile-time.
Vediamo lo stesso esempio in Ruby.
class Oca
def starnazza
return "Sono un'oca e starnazzo";
end
end
class Anatra
def starnazza
return "Sono un'anatra e starnazzo"
end
end
cortile = Array.new
cortile.push(Anatra.new)
cortile.push(Oca.new)
cortile.push(Anatra.new)
for animale in cortile
puts(animale.starnazza)
end
Qui semplicemente io passo una serie di classi al mio metodo polimorfico, e mi limito a richiamare il metodo "starnazza". Se facessi questa cosa in Java avrei un errore di compilazione! Invece, all'interprete Ruby non interessa che io "affermi" che un'Anatra è (IS-A) un Gallinaceo: se starnazza, allora lo è. Si fida! (Beato lui). E se gli passo un oggetto che non implementa "starnazza"? Be', semplicemente avrò un errore a run-time.
Si tratta dunque di due filosofie diverse: nella prima, io devo dichiarare sempre la classe dell'oggetto passato a un metodo, nella seconda posso farne a meno. Non c'è chiaramente una soluzione migliore dell'altra: nel primo caso privilegio le performance, e nel secondo la flessibilità del design.
(E se avete dei dubbi sul fatto che Oche e Anatre starnazzino, date un'occhiata qui!)
1 commento:
ciao!
Direi più che altro che col primo sistema strutturi esplicitamente il codice in modo piu' "formale".
Posta un commento