Vai alla Home Page About me Courseware Federica Living Library Federica Federica Podstudio Virtual Campus 3D Le Miniguide all'orientamento Gli eBook di Federica La Corte in Rete
 
 
Il Corso Le lezioni del Corso La Cattedra
 
Materiali di approfondimento Risorse Web Il Podcast di questa lezione

Valeria Vittorini » 15.Programmazione orientata agli oggetti. Introduzione


Paradigmi di programmazione

  • Un paradigma di programmazione fornisce un insieme di strumenti concettuali che consente al programmatore di adottare un particolare stile di programmazione.
  • I diversi paradigmi di programmazione si differenziano per il tipo di Astrazione utilizzato e per i concetti su cui si basano.
  • Il linguaggio C++ che stiamo studiando è considerato un linguaggio “ibrido” perchè supporta diversi paradigmi di programmazione.
  • In C++ infatti è possibile scrivere programmi secondo uno stile di programmazione:
    • Procedurale (l’astrazione principale è l’astrazione sul “controllo”).
    • Orientato agli oggetti (l’astrazione principale è l’astrazione sui “dati”).
    • Generica (basata sul concetto di parametrizzazione).

La programmazione procedurale

Costituisce l’approccio tradizionale alla programmazione.

Usa come metodologia di riferimento la decomposizione funzionale, con approccio discendente (top-down):

  • si scompone ricorsivamente la funzionalità principale del sistema da sviluppare in funzionalità più semplici;
  • si termina la scomposizione quando le funzionalità individuate sono così semplici da permetterne una diretta implementazione come funzioni;
  • si divide il lavoro di implementazione, eventualmente tra diversi programmatori, sulla base delle funzionalità individuate.

La programmazione ad oggetti

Si individuano le classi di oggetti (entità del mondo reale o concettuale) che caratterizzano il dominio applicativo:

  • le diverse classi vengono poi modellate, progettate e implementate;
  • ogni classe è descritta da un’interfaccia che specifica il comportamento degli oggetti della classe.

L’applicazione si costruisce con l’approccio ascendente (bottom-up), assemblando oggetti e individuando le modalità con cui questi devono collaborare per realizzare le diverse funzionalità dell’applicazione.

La programmazione generica

Scopo della programmazione generica è definire una funzione o una classe senza specificare il tipo di una o più delle sue entità (parametri, membri):

  • una funzione di ricerca in grado di operare su elementi di un tipo qualsiasi;
  • una classe che descrive contenitori (lista, insieme, coda, …) in grado di contenere oggetti di tipi qualsiasi.

Tra i vari meccanismi impiegabili per ottenere genericità, il C++ offre i template, tramite i quali il programmatore descrive famiglie di funzioni o di classi parametriche

Caratteristiche di un linguaggio OO

Nelle prossime lezioni focalizzeremo l’attenzione sulla Programmazione Orientata agli Oggetti (OOP: object oriented programming).
Un linguaggio “Orientato agli Oggetti” deve fornire i meccanismi per basare la programmazione sui seguenti concetti:

  • Classe e Oggetto
  • Ereditarietà
  • Polimorfismo

Classi e Oggetti

In questo prima parte della lezione discutiamo il concetto di Classe e Oggetto.
Un linguaggio di programmazione ad oggetti offre costrutti espliciti per la definizione di entità (oggetti) che incapsulano una struttura dati nelle operazioni possibili su di essa.
In altre parole fornisce meccanismi espliciti per l’astrazione sui dati e l’information hiding: l’utente non può accedere alla struttura dati né in lettura né in scrittura se non attraverso le funzioni fornite dal programmatore.

Astrazione sui dati ed Information Hiding

Astrazione sui dati ed Information Hiding


Un esempio di oggetto

Operazioni

  • rispondere;
  • comporre un numero;
  • memorizzare un numero;
  • registrare un messaggio;
  • …;

Struttura dati

  • produttore
  • marca
  • tipo (fisso, mobile)
  • display
  • tecnologia
  • ….

Un esempio di oggetto (segue)

L’utente interagisce con una interfaccia per effettuare le operazioni consentite sul telefono:

  • Microfono
  • Tastierino numerico
  • Tasti funzione
  • ….

Classi e Oggetti

  • In particolare i linguaggi a oggetti forniscono il costrutto “class” (classe) che consente di definire nuovi tipi di dato (astratti) e le relative operazioni sotto forma di operatori o di funzioni (dette metodi o funzioni membro).
  • I nuovi tipi di dato possono essere gestiti quasi allo stesso modo dei tipi predefiniti del linguaggio
    • si possono creare istanze;
    • si possono eseguire operazioni su di esse.
  • Un oggetto è una variabile cioè una istanza di una classe
  • Lo stato di un oggetto è rappresentato dai valori correnti delle variabili che costituiscono la struttura dati concreta sottostante il tipo astratto.
  • Nell’esempio precedente il linguaggio consente di definire la classe Telefono, un particolare apparecchio è una istanza della classe Telefono, cioè un oggetto.
  • “Telefono” è il nome del tipo di dato astratto.

Caratteristiche di una classe

La classe è un modulo software con le seguenti caratteristiche:

  • è dotata di un’interfaccia (specifica) e di un corpo (implementazione);
  • la struttura dati “concreta” di un oggetto della classe, e gli algoritmi che ne realizzano le operazioni, sono tenuti nascosti all’interno del modulo che implementa la classe;
  • lo stato di un oggetto evolve unicamente in relazione alle operazioni ad esso applicate;
  • le operazioni sono utilizzabili con modalità che prescindono completamente dagli aspetti implementativi; in tal modo è possibile modificare gli algoritmi utilizzati senza modificare l’interfaccia.

Produzione e uso di una classe

Il meccanismo delle classi è orientato specificamente alla riusabilità del software.

Occorre dunque fare riferimento ad una situazione di produzione del software nella quale operano:

  • il produttore della classe, il quale mette a punto
    • la specifica, in un file di intestazione (header file);
    • l’implementazione della classe (in un file separato);
  • l’utente della classe, il quale ha a disposizione la specifica della classe, crea oggetti e li utilizza nel proprio modulo, come abbiamo già visto introducendo la programmazione modulare.

Ereditarietà e polimorfismo

In questa seconda parte della lezione parleremo di altri due concetti: ereditorialità e polimorfismo.

L’ereditarietà consente di definire nuove classi per specializzazione o estensione di classi preesistenti, in modo incrementale.

Il polimorfismo consente di invocare operazioni su un oggetto, pur non essendo nota a tempo di compilazione la classe cui fa riferimento l’oggetto stesso.

Ereditarietà

Il meccanismo dell’ereditarietà è di fondamentale importanza nella programmazione ad oggetti, in quanto induce una strutturazione gerarchica nel sistema software da costruire.

L’ereditarietà consente infatti di realizzare relazioni tra classi di tipo generalizzazione-specializzazione, in cui una classe, detta base, realizza un comportamento generale comune ad un insieme di entità, mentre le classi derivate (sottoclassi) realizzano comportamenti specializzati rispetto a quelli della classe base.

Esempio:

  • Tipo o classe base: Animale
  • Tipi derivati (sottoclassi): Cane, Gatto, Cavallo, …

In una gerarchia gen-spec, le classi derivate sono specializzazioni (cioè casi particolari) della classe base.

Ereditarietà (segue)

Generalizzazione: dal particolare al generale.

Specializzazione o particolarizzazione: dal particolare al generale.

Nel paradigma a oggetti, col meccanismo dell’ereditarietà ci si concentra sulla creazione di tassonomie del sistema in esame.

Nel paradigma a oggetti, col meccanismo dell'ereditarietà ci si concentra sulla creazione di tassonomie del sistema in esame.


Ereditarietà (segue)

  • Per descrivere un sistema sono possibili tassonomie diverse, a seconda degli obiettivi.
  • In Figura è mostrato un esempio di gerarchia che a partire da una classe base Oggetto deriva una tra le possibili tassonomie di veicoli.
  • La gerarchia può svilupparsi in “orizzontale” o in “verticale”.
  • Si noti che le classi appartenenti ad uno stesso livello (orizzontale) condividendo lo stesso padre rappresentano astrazioni di concetti/entità più vicine tra loro rispetto a quelle che si trovano su livelli differenti.
Per descrivere un sistema sono possibili tassonomie diverse, a seconda degli obiettivi.

Per descrivere un sistema sono possibili tassonomie diverse, a seconda degli obiettivi.


Ereditarietà (segue)

  • Esiste però anche un altro motivo, di ordine pratico, per cui conviene usare l’ereditarietà, oltre quello di descrivere un sistema secondo un modello gerarchico; questo secondo motivo è legato esclusivamente al concetto di riuso del software.
  • In alcuni casi si ha a disposizione una classe che non corrisponde esattamente alle proprie esigenze. Anziché scartare del tutto il codice esistente e riscriverlo, si può seguire con l’ereditarietà un approccio diverso, costruendo una nuova classe che eredita il comportamento di quella esistente, salvo che per i cambiamenti che si ritiene necessario apportare.
  • Tali cambiamenti possono riguardare sia l’aggiunta di nuove funzionalità che la modifica di quelle esistenti.

Ereditarietà (segue)

In definitiva, l’ereditarietà offre il vantaggio di ridurre i tempi di sviluppo, in quanto minimizza la quantità di codice da scrivere quando occorre:

  • definire un nuovo tipo che è un sottotipo di un tipo già disponibile, oppure
  • adattare una classe esistente alle proprie esigenze.

Non è necessario conoscere in dettaglio il funzionamento del codice da riutilizzare, ma è sufficiente modificare (mediante aggiunta o specializzazione) la parte di interesse.

Polimorfismo

Per polimorfismo si intende la proprietà di una entità di assumere forme diverse nel tempo.

Una entità è polimorfa se può fare riferimento, nel tempo, a classi diverse.

Siano ad esempio a, b due oggetti appartenenti rispettivamente alle classi A, B, che prevedono entrambe una operazione m, con diverse implementazioni. Si consideri l’assegnazione:

a := b

L’esecuzione della operazione m sull’oggetto a dopo l’assegnazione, per la quale è spesso adoperata la sintassi:

a.m()

produce l’esecuzione della implementazione di m specificata per la classe B.

Polimorfismo (segue)

Esempio:

  • Si consideri una gerarchia di classi rappresentate in figura.
  • Si supponga che ogni classe fornisca al suo utente un metodo “disegna()”:
  • Sia A un vettore di N oggetti della classe Figura, composto di oggetti delle sottoclassi Triangolo, Rettangolo, etc …:
    Figura A[N]
    (ad es.: A[0] è un rettangolo, A[1] un triangolo, A[2] una piramide, etc.).
Una gerarchia di figure geometriche

Una gerarchia di figure geometriche


Polimorfismo (segue)

Si consideri una funzione Disegna_Figure(), che contiene il seguente ciclo di istruzioni:

for i = 1 to N do

A[i].disegna()

L’esecuzione del ciclo richiede che sia possibile determinare dinamicamente (cioè a tempo d’esecuzione) l’implementazione della operazione disegna() da eseguire, in funzione del tipo corrente dell’oggetto A[i].

L’istruzione A[i].disegna() non ha bisogno di essere modificato in conseguenza dell’aggiunta di una nuova sottoclasse di Figura (ad es.: Cerchio), anche se tale sottoclasse non era stata neppure prevista all’atto della stesura della funzione Disegna_Figure(). (Si confronti con il caso dell’uso di una istruzione case nella programmazione tradizionale).

Polimorfismo

  • Il polimorfismo supporta dunque la proprietà di estensibilità di un sistema, nel senso che minimizza la quantità di codice che occorre modificare quando si estende il sistema, cioè si introducono nuove classi e nuove funzionalità.
  • Un meccanismo con cui viene realizzato il polimorfismo è quello del binding dinamico.
  • Il binding dinamico (o late binding) consiste nel determinare a tempo d’esecuzione, anziché a tempo di compilazione, il corpo del metodo da invocare su un dato oggetto.

Vantaggi della programmazione OO

Rispetto alla programmazione tradizionale, la programmazione orientata agli oggetti (OOP) offre vantaggi in termini di

  • modularità: le classi sono i moduli del sistema software;
  • coesione dei moduli: una classe è un componente software ben coeso in quanto rappresentazione di una unica entità;
  • disaccoppiamento dei moduli: gli oggetti hanno un alto grado di disaccoppiamento in quanto i metodi operano sulla struttura dati interna ad un oggetto; il sistema complessivo viene costruito componendo operazioni sugli oggetti;
  • information hiding: sia le strutture dati che gli algoritmi possono essere nascosti alla visibilità dall’esterno di un oggetto;
  • riuso: l’ereditarietà consente di riusare la definizione di una classe nel definire nuove (sotto)classi; inoltre è possibile costruire librerie di classi raggruppate per tipologia di applicazioni;
  • estensibilità: il polimorfismo agevola l’aggiunta di nuove funzionalità, minimizzando le modifiche necessarie al sistema esistente quando si vuole estenderlo.

I materiali di supporto della lezione

Da C++ a UML: Capitolo 15; Capitolo 21 § 1.1, 1.2; Capitolo 22 § 2

  • Contenuti protetti da Creative Commons
  • Feed RSS
  • Condividi su FriendFeed
  • Condividi su Facebook
  • Segnala su Twitter
  • Condividi su LinkedIn
Progetto "Campus Virtuale" dell'Università degli Studi di Napoli Federico II, realizzato con il cofinanziamento dell'Unione europea. Asse V - Società dell'informazione - Obiettivo Operativo 5.1 e-Government ed e-Inclusion