Questo sito contribuisce alla audience di

programmazióne a oggètti

Guarda l'indice

Descrizione generale

Paradigma di programmazione basato sull'identificazione di entità (oggetti) dotate di uno stato e un comportamento propri. In un linguaggio di programmazione orientato agli oggetti (OO), la computazione è espressa come sequenza di messaggi scambiati fra gli oggetti. L'orientamento agli oggetti è basato su quattro concetti fondamentali: 1) l'incapsulamento di dati e procedure in oggetti; 2) la comunicazione attraverso messaggi; 3) l'organizzazione del codice in classi; 4) l'ereditarietà fra classi. La prima caratteristica segna la distinzione fra programmazione OO e quella procedurale. Anziché definire la computazione di un programma come la trasformazione (sequenziale) di un insieme di strutture dati a cui le diverse procedure possono ugualmente accedere in lettura o scrittura, ogni oggetto mantiene un insieme di variabili (chiamati campi o attributi) di cui controlla l'accesso in lettura e scrittura, mentre le procedure (chiamate metodi) possono operare solo su variabili a cui hanno diritto di accesso. La configurazione dei valori assunti dalle variabili di un oggetto in un dato momento del processo di calcolo definisce il suo stato in quell'istante. Non esistendo un concetto di variabile globale, si pone il problema di come coordinare l'evoluzione di diversi oggetti. A questo scopo, la nozione di messaggio conferma la richiesta, da parte di un oggetto A, che un oggetto B fornisca informazioni sul proprio stato, oppure lo modifichi, oppure esegua qualche specifica attività. Anziché avere una nozione di subroutine, cioè di procedura atta a svolgere sottocompiti all'interno di una più generale, la decomposizione di compiti complessi in sottocompiti più semplici è realizzata tramite la chiamata di metodi all'interno di altri metodi. I metodi da richiamare possono essere specificati in classi diverse rispetto a quella in cui è definito il metodo chiamante, o nella stessa classe, così che un oggetto può inviare messaggi anche a se stesso ed essere usati come argomenti per un messaggio o come valori restituiti da una procedura. In tal caso, un oggetto obj1 può utilizzare un altro oggetto obj2 come componente del proprio stato. La comunicazione mediante messaggi gestiti internamente all'oggetto fa sì che la struttura di un oggetto, o l'implementazione dei suoi metodi, siano invisibili agli altri oggetti. La nozione di classe fornisce un prototipo per la costruzione di oggetti che devono avere caratteristiche strutturali e comportamentali comuni, vale a dire la stessa struttura dello stato, cioè lo stesso insieme di variabili e di metodi a disposizione. Ogni oggetto è quindi un'istanza di una classe prodotto in seguito a una richiesta specifica fatta alla classe, cioè quella di creare una nuova istanza, eventualmente con certi valori iniziali dei suoi attributi. Una classe può essere vista come l'implementazione di un tipo di dati astratto, specificando la segnatura (cioè il nome, i tipi degli argomenti e del valore restituito) di ogni metodo, e fornendo implementazioni che soddisfano i vincoli definiti dal tipo, e contemporaneamente specificare anche una struttura logica degli oggetti, che sia il compilatore sia il meccanismo di esecuzione, si occuperanno di trasformare in una struttura fisica. La creazione di un'istanza di una classe avviene utilizzando speciali procedure, chiamate costruttori, che riservono lo spazio in memoria necessario a contenere tutta l'informazione di stato relativa all'istanza che si va creando e di restituire un riferimento a tale spazio. La nozione di ereditarietà stabilisce tassonomie fra le classi, in cui una definita figlia eredita la struttura e i comportamenti da quella genitore (o dalle classi genitore in caso di ereditarietà multipla). Si può quindi dichiarare una class2 come un'estensione di una class1, cioè che la struttura degli oggetti della class2 è la stessa degli oggetti della class1, a meno dell'aggiunta di nuove variabili, e che l'insieme delle segnature dei metodi di class2 è lo stesso di class1, a meno dell'aggiunta di nuovi metodi. È possibile che le implementazioni dei metodi di class1 siano sostituite in class2 da implementazioni più specifiche (si dice in questo caso che si effettua un overriding dei metodi sostituiti). In generale, se class2 eredita da class1, questo vuol dire che in ogni situazione in cui vogliamo utilizzare un oggetto di tipo class1, possiamo usarne uno di tipo class2. Il concetto di classe, analogamente al concetto di modulo, in linguaggi come Modula, permette una netta separazione fra interfaccia pubblica di una classe e implementazione privata dei metodi. L'interfaccia definisce quanto è necessario rendere noto della classe all'esterno perché le sue istanze possano utilmente interagire con il resto del programma. In particolare, essa è costituita dalle dichiarazioni dei metodi della classe che sono invocabili da parte di oggetti di un'altra classe. L'implementazione privata, invece che non è osservabile dall'esterno della classe, rende possibile procedere a continue ottimizzazioni del codice senza che questo influenzi le altre classi presenti nel sistema. Da questo punto di vista, la definizione di una classe corrisponde alla stipula di un contratto fra una certa componente software e le altre con cui essa si impegna a garantire certi servizi se richiesti nel modo opportuno. Come già detto, le implementazioni di uno stesso metodo possono differire nelle classi figlia rispetto a quelle nella classe genitore. Questa caratteristica dei linguaggi OO richiede la distinzione fra binding (corrispondenza fra nome del metodo e codice da utilizzare) statico e dinamico. Supponiamo di avere dichiarato la class2 come sottoclasse di class1, di avere a disposizione un oggetto obj di class2, e di avere richiesto a obj l'utilizzo del metodo meth1, definito nella class2 e ridefinito nella class2. Nel caso di binding statico la versione di meth1 utilizzata sarà quella definita in class1 (tipo dichiarato), mentre nel caso di binding dinamico si utilizzerà la versione definita in class2 (tipo effettivo). I diversi linguaggi OO utilizzano l'uno o l'altro tipo di binding come default e forzano l'utilizzo dell'altro mediante opportuni costrutti sintattici. Per esempio in C++ il binding è statico, a meno che nella dichiarazione del metodo non si usi la parola chiave “virtual”, mentre in Java si usa il binding dinamico, a meno che nella dichiarazione del metodo non si usi la parola chiave “static”.

Cenni storici

Le origini della programmazione a oggetti risalgono agli anni Sessanta, quando il linguaggio Simula, sviluppato in Svezia per la simulazione di sistemi complessi, introdusse la nozione di classe e di comunicazione attraverso i messaggi. Le classi venivano definite per rappresentare categorie del mondo reale e gli oggetti specificavano le istanze di queste categorie. Per esempio la classe Coda mantiene l'informazione su quali istanze della classe Persona sono presenti in un determinato momento in una coda. La composizione della coda può variare man mano che le persone ricevono i messaggi di accodarsi o di uscire dalla coda. Successivamente il linguaggio small talk fornì la prima implementazione della programmazione a oggetti e ne mostrò le potenzialità nella costruzione di interfacce. In small talk, la distinzione fra model, view e controller permette la separazione fra oggetti che realizzano le computazioni per una specifica applicazione (model), quelli che ne permettono la rappresentazione sullo schermo (view) e oggetti che catturano le interazioni prodotte dall'utente sugli oggetti view (controller). La programmazione a oggetti, essendo basata sulla modellazione delle caratteristiche del dominio, si è rivelata particolarmente adatta alla programmazione di interfacce, alla costruzione di prototipi di sistemi interattivi e alla simulazione di sistemi complessi. La sua diffusione è stata favorita dalla creazione del C++, che non essendo un linguaggio a oggetti puro, utilizza i concetti di classe, ereditarietà, oggetto e metodo, unendo a questi le caratteristiche di efficienza del linguaggio C. L'introduzione di Java ha portato a una diffusione di programmi a oggetti in applicazioni accessibili tramite il web e in cui richiedono l'accesso a data base. Per gli oggetti che operano su processori comunicanti in rete è anche possibile definire forme di migrazione degli oggetti, in cui esso può trasferire il proprio stato da un punto a un altro della rete. Le esigenze di calcolo distribuito e di interoperabilità fra diverse applicazioni basate su oggetti hanno portato a formulare proposte di middleware, quali CORBA, o .NET di Microsoft, che permettono la comunicazione fra oggetti senza che un oggetto, che invia un messaggio, debba conoscere l'identità di quello che lo riceverà, superando quindi il concetto di messaggio come comunicazione punto a punto.