La programmazione ad oggetti si basa fondamentalmente su tre principi:
- incapsulamento: è la capacità di nascondere all'esterno dati e servizi, inglobandoli in un modulo che risulti inviolabile dall'esterno.
- ereditarietà: è la capacità di estendere un modulo aggiungendo o modificando alcuni dei suoi servizi.
- polimorfismo: è la capacità di variare il comportamento di un modulo a seconda di come viene acceduto.
- può nascondere i dettagli implementativi dei propri dati all'esterno (incapsulamento).
- può essere estesa da una classe figlia, che implementi ulteriori servizi (ereditarietà).
- può venire acceduta con ciascuna interfaccia appartenente ad una delle sue superclassi, ma il suo comportamento dipenderà dalla sua implementazione concreta (polimorfismo).
Per meglio chiarire, si consideri il seguente esempio:
public class Automobile{
  // incapsulamento: la velocità e' nascosta all'esterno e può essere
  // acceduta solo tramite opportuni servizi
  protected int speed = 0;
  // servizio pubblico per variare la velocita', si noti che il chiamante
  // non conosce nessun dettaglio implementativo circa la velocita'
  public void accelera(){ this.speed++; }
}
public class AutomobileSportiva extends Automobile {
      // l'automobile sportiva utilizza come base Automobile
      // Un'auto sportiva ha una accelerazione forte. Si noti che si ridefinisce
     // un servizio che si basa sulle proprieta' del codice vecchio, ossia la variabile
     // speed.
     public void accelera(){
  for(int i=0; i<4;>          
 this.accelera();
 }
}
Si supponga che in un programma ci sia un metodo che utilizza l'automobile:
public void corri(Automobile auto){  auto.accelera(); }
Ora, è evidente che se a questo metodo viene passata un'istanza di AutomobileSportiva, il codice in questione (vecchio) utilizza il nuovo comportamento (polimorfismo). Ecco quindi che senza alterare il codice vecchio si è inserito un comportamento nuovo, e quindi il codice vecchio usa il codice nuovo. Similarmente, AutomobileSportiva rappresenta codice nuovo che utilizza quello vecchio: il suo metodo accelera() richiama il codice vecchio della superclasse.
Da notare come il comportamento ottenuto dipenda dal tipo run-time effettivo, ossia il polimorfismo mostra sempre l'ultimo comportamento definito nel tipo run-time fra quelli disponibili a partire dal tipo statico.
Si noti che il polimorfismo da solo non basta: è indispensabile disporre anche del principio di sostituzione di Liskov, che consente di utilizzare l'interfaccia di una superclasse per riferirsi ad una sottoclasse.
 
 
Nessun commento:
Posta un commento