• Non ci sono risultati.

Dispensa III.1

N/A
N/A
Protected

Academic year: 2021

Condividi "Dispensa III.1"

Copied!
11
0
0

Testo completo

(1)

Dispensa III.1 versione 1.0 mail: lamonica@associatesonline.it Pagina 1 di 11

PROGRAMMAZIONE AD OGGETTO (OOP)

La dispensa di seguito proposta si pone come tutorial per poter porre le basi per la realizzazione di MODULI SOFTWARE che vengano realizzati con un paradigma di programmazione del tipo Object Oriented.

Il concetto di Oggetto

La programmazione orientata agli oggetti (OOP, Object Oriented Programming) è un paradigma di programmazione che permette di definire oggetti software in grado di interagire gli uni con gli altri attraverso lo scambio di messaggi. È particolarmente adatta nei contesti in cui si possono definire delle relazioni di interdipendenza tra i concetti da modellare.

Tra gli altri vantaggi della programmazione orientata agli oggetti:

• fornisce un supporto naturale alla modellazione software degli oggetti del mondo reale o del modello astratto da riprodurre;

• permette una più facile gestione e manutenzione di progetti di grandi dimensioni;

• l'organizzazione del codice sotto forma di classi favorisce la modularità e il riuso di codice .

(2)

Dispensa III.1 versione 1.0 mail: lamonica@associatesonline.it Pagina 2 di 11

Le caratteristiche

La programmazione ad oggetti prevede di raggruppare in una zona circoscritta del codice sorgente (chiamata classe) la dichiarazione delle strutture dati e delle procedure che operano su di esse.

Le classi, quindi, costituiscono dei modelli astratti, che a tempo di esecuzione vengono invocate per instanziare o creare oggetti software relativi alla classe invocata.

Questi ultimi sono dotati di attributi (dati) e metodi (procedure) secondo quanto definito/dichiarato dalle rispettive classi.

La parte del programma che fa uso di un oggetto si chiama client.

Un linguaggio di programmazione è definito ad oggetti quando permette di implementare tre meccanismi usando la sintassi nativa del linguaggio:

• incapsulamento

• ereditarietà

• polimorfismo

L'incapsulamento consiste nella separazione della cosiddetta interfaccia di una classe dalla corrispondente implementazione, in modo che i client di un oggetto di quella classe possano utilizzare la prima, ma non la seconda.

L'ereditarietà permette essenzialmente di definire delle classi a partire da altre già definite.

(3)

Dispensa III.1 versione 1.0 mail: lamonica@associatesonline.it Pagina 3 di 11 Il polimorfismo permette di scrivere un client che può servirsi di oggetti di classi diverse, ma dotati di una stessa interfaccia comune; a tempo di esecuzione, quel client attiverà comportamenti diversi senza conoscere a priori il tipo specifico dell'oggetto che gli viene passato.

L’Oggetto

Un oggetto è una istanza di una classe. Esso è dotato di tutti gli attributi e i metodi definiti dalla classe, ed agisce come un fornitore di "messaggi" (i metodi) che il codice eseguibile del programma (procedure o altri oggetti) può attivare su richiesta.

Inviare un messaggio ad un oggetto si dice, in gergo, invocare un metodo su quell'oggetto. Il metodo riceve come parametro (spesso implicito) l'oggetto su cui è stato invocato, che può essere referenziato tramite una parola-chiave o una sintassi apposita, anche se è passato come parametro implicito; per esempio, in C++, in Java, e in C# si usa la parola-chiave this.

Dal punto di vista del calcolatore, ogni oggetto è identificato da una certa zona di memoria, nella quale sono memorizzati gli attributi, e il valore di questi ultimi determina lo stato interno dell'oggetto. Istanziare un oggetto vuol dire allocare memoria ed eventualmente inizializzarla secondo le specifiche definite dalla classe. Molti linguaggi forniscono un supporto per l'inizializzazione automatica di un oggetto, con uno o più metodi speciali, detti costruttori.

Analogamente, la fine della vita di un oggetto può essere gestita con un metodo detto distruttore.

Il codice eseguibile del programma accede a tale zona di memoria sempre e solo secondo le modalità definite dalla classe.

Secondo il principio noto come information hiding, l'accesso ai campi di un oggetto deve essere permesso solo tramite metodi invocati su quello stesso oggetto. Il vantaggio principale è che il

(4)

Dispensa III.1 versione 1.0 mail: lamonica@associatesonline.it Pagina 4 di 11 controllo completo sullo stato interno viene assegnato ad una zona ristretta del codice eseguibile del programma (la classe), perché il codice esterno non è autorizzato a modificarlo.

Incapsulamento

L'incapsulamento è la proprietà per cui i dati che definiscono lo stato interno di un oggetto sono accessibili ai metodi dell'oggetto stesso, mentre non sono visibili ai clients.

Per alterare lo stato interno dell'oggetto, è necessario invocarne i metodi, ed è questo lo scopo principale dell'incapsulamento. Infatti, se gestito opportunamente, esso permette di vedere l'oggetto come una black-box, cioè una "scatola nera" di cui, attraverso l'interfaccia, è noto cosa fa, ma non come lo fa.

L'incapsulamento riduce il costo da pagare per correggere gli errori in fase di sviluppo di un programma. Questo risultato viene ottenuto strutturando l'intero progetto, ed i moduli che lo compongono, in modo che un'errata decisione presa nell'implementazione di un singolo modulo non si ripercuota sull'intero progetto, e possa essere corretta modificando soltanto quel modulo.

Si potrà così evitare di dover modificare anche i moduli clienti, che interagiranno con il modulo incapsulato soltanto attraverso interfacce.

Ereditarietà

Il meccanismo dell'ereditarietà è utilizzato in fase di strutturazione/definizione/pianificazione del software o in successive estensioni e permette di derivare nuove classi a partire da quelle già definite realizzando una gerarchia di classi. Una classe derivata attraverso l'ereditarietà (sottoclasse o classe figlia), mantiene i metodi e gli attributi delle classi da cui deriva (classi base,

(5)

Dispensa III.1 versione 1.0 mail: lamonica@associatesonline.it Pagina 5 di 11 superclassi o classi padre); inoltre, può definire i propri metodi o attributi, e ridefinire il codice di alcuni dei metodi ereditati tramite un meccanismo chiamato overriding.

L'ereditarietà può essere usata come meccanismo per ottenere l'estensibilità e il riuso del codice, e risulta particolarmente vantaggiosa quando viene usata per definire sottotipi, sfruttando le relazioni is-a esistenti nella realtà di cui la struttura delle classi è una modellizzazione. Oltre all'evidente riuso del codice della superclasse, l'ereditarietà permette la definizione di codice generico attraverso il meccanismo del polimorfismo.

Ad esempio se nel programma esiste già una classe MezzoDiTrasporto che ha come proprietà i dati di posizione, velocità, destinazione e carico utile, e occorre una nuova classe Aereo, è possibile crearla direttamente dall'oggetto MezzoDiTrasporto dichiarando una classe di tipo Aereo che eredita da MezzoDiTrasporto e aggiungendovi anche l'attributo che identifica la quota di crociera, con il vantaggio che la nuova classe acquisirà tutti i membri definiti in MezzoDiTrasporto per il fatto stesso di esserne sottoclasse.

Polimorfismo

Solitamente è legato alle relazioni di eredità tra classi, che garantisce che tali oggetti abbiano una stessa interfaccia: nei linguaggi ad oggetti tipizzati, le istanze di una sottoclasse possono essere utilizzate al posto di istanze della superclasse (polimorfismo per inclusione).

L'overriding dei metodi o delle proprietà permette che gli oggetti appartenenti alle sottoclassi di una stessa classe rispondano diversamente agli stessi utilizzi. Ad esempio, supponiamo di avere una gerarchia in cui le classi Cane e Gatto discendono dalla superclasse Animale. Quest'ultima definisce un metodo cosaMangia(), le cui specifiche sono: Restituisce una stringa che identifica il

(6)

Dispensa III.1 versione 1.0 mail: lamonica@associatesonline.it Pagina 6 di 11 nome dell'alimento tipico dell'animale. I due metodi cosaMangia() definiti nelle classi Cane e Gatto si sostituiscono a quello che ereditano da Animale e, rispettivamente, restituiscono "osso" e

"pesce".

Supponiamo di scrivere un client che lavora con oggetti di tipo Animale; ad esempio, una banale funzione che prende in ingresso un oggetto di tipo Animale, ne invoca il metodo cosaMangia(), e stampa a video il risultato. Questa funzione otterrà due risultati diversi a seconda del tipo effettivo dell'oggetto che le viene passato come argomento.

Il comportamento di un programma abbastanza complesso, quindi, può essere alterato considerevolmente in funzione delle sottoclassi che sono istanziate a tempo di esecuzione e le cui istanze sono passate alle varie parti del codice.

I metodi che vengono ridefiniti in una sottoclasse sono detti polimorfi, in quanto lo stesso metodo si comporta diversamente a seconda del tipo di oggetto su cui è invocato.

(7)

Dispensa III.1 versione 1.0 mail: lamonica@associatesonline.it Pagina 7 di 11

La implementazione della Classe Sfera

E’ di seguito indicato il codice di un piccolo progetto in C++ che definisce una classe e la utilizza.

La classe consiste in

un file HEADER sfera.h nel quale è DICHIARATA la classe che consiste in tre attributi e tre metodi un file CPPsfera.cpp nel quale sono implementati i tre metodi

il file main.cpp contiene poi il main() che utilizza la classe Sfera

sfera.h

/*

La classe è Sfera e consiste in TRE attributi che sono accessibili SOLO dalla classe (incapsulamemto).

Il metodo Sfera (STESSO NOME DELLA CLASSE) è il costruttore e viene invocato solo quando si istanzia l’oggetto.

Il metodo get_name permette di accedere all’attributo nome della Classe che non è direttamente accessibile.

*/

#include <iostream>

#include <string>

using namespace std;

class Sfera { private:

double massa;

double accelerazione;

string nome;

public:

(8)

Dispensa III.1 versione 1.0 mail: lamonica@associatesonline.it Pagina 8 di 11 Sfera(double massa, double accelerazione,string nome);

string get_name();

double velocita (double Delta_T);

};

(9)

Dispensa III.1 versione 1.0 mail: lamonica@associatesonline.it Pagina 9 di 11 sfera.cpp

/*

Sono implementati i tre medoti della classe.

Per indicare che i metodi sono relativi alla classe Sfera, essi sono precededuti da Sfera::

Il costruttore (Sfera::Sfera) NON ammette tipo di restituzione. Infatti non è un metodo qualsiasi, ma il metodo che viene invocato SOLO UNA VOLTA nella vita dell’oggetto istanziato

Nota che per indicare un attributo appartenente alla Classe Sfera, così come per i metodi, questi sono preceduti da Sfera::

*/

#include "sfera.h"

Sfera::Sfera(double massa, double accelerazione, string nome){

Sfera::massa = massa;

Sfera::accelerazione = accelerazione;

Sfera::nome = nome;

cout << "massa " << Sfera::massa << endl;

cout << "accelerazione " << Sfera::accelerazione << endl;

cout << "nome " << Sfera::nome << endl;

}

double Sfera::velocita(double delta_T){

return Sfera::accelerazione * delta_T;

}

string Sfera::get_name(){

return Sfera::nome;

}

(10)

Dispensa III.1 versione 1.0 mail: lamonica@associatesonline.it Pagina 10 di 11 main.cpp

/*

Il file main.cpp contiene il main() che utilizza oggetti istanziati della Classe Sfera.

Questo può essere fatto:

- In maniera simile a quanto avviene quando si dichiara una variabile di un tipo primitivo dichiarando una variabile di un tipo Classe.

Ad esempio pippo è un oggetto istanza della classe Sfera.

Sfera pippo;

Quando si istanzia un oggetto di fatto si invoca il costruttore dell’oggetto, e quindi nel caso nostro un metodo che accetta tre parametri (massa,accelerazione,nome)

I tre parametri inseriti al momento della istanzazione della classe vanno a valorizzare gli attributi della classe, ed essendo attributi PRIVATE non possono essere recuperati se non tramite un metodo pubblico (incapsulamemto).

- in maniera simile a quello che avviene quando si crea dinamicamente una variabile Sfera * p_sfera_2;

p_sfera_2 = new Sfera(2,3,"sfera_2");

*/

#include <cstdlib>

#include "sfera.h"

int main(int argc, char *argv[]) {

Sfera sfera_1(1,2,"sfera_1");

Sfera * p_sfera_2;

(11)

Dispensa III.1 versione 1.0 mail: lamonica@associatesonline.it Pagina 11 di 11 p_sfera_2 = new Sfera(2,3,"sfera_2");

cout << sfera_1.velocita(3) << endl;

cout << p_sfera_2->velocita(3) << endl;

for(int i = 0;i < 100; i++){

cout << "la sfera " << sfera_1.get_name() << " ha velocita' " << sfera_1.velocita((double)i);

cout << " la sfera " << p_sfera_2->get_name() << " ha velocita' " << p_sfera_2->velocita((double)i) <<

endl;

}

system("pause");

return 0;

}

Riferimenti

Documenti correlati

(b) Determina l'intensità, la direzione e il verso della forza totale che agisce in ciascuno dei tratti indicati.. Un blocco di massa 250 kg viene spinto con una forza

Quando il pallone è immerso in acqua la spinta verso l’alto (pari alla forza peso di una sfera d'acqua delle dimensioni del pallone) è nettamente maggiore del suo

Il rapporto tra forza e massa è però lo stesso per qualsiasi corpo che si trovi a 150 milioni di chilometri dal Sole: vale circa 6 millesimi di newton al kg, e si chiama

Siccome tutte queste forze (che si dicono interne) risultano appaiate in coppie azione- reazione che ubbidiscono alla terza legge di Newton, il risultato è che la quantità di

C’è un istante, insomma, in cui la sfera è ferma: quindi non ha più energia cinetica, e non ha più nemmeno energia potenziale gravitazionale, perché è arrivata in fondo

Quando una sostanza allo stato solido fonde, i legami tra le molecole si rompono, e le molecole riescono a scorrere le une sulle altre.. La materia assume allora un aspetto

La parte a) della figura mostra una piccola zona nei pressi delle due sorgenti. Due segnali viaggiano verso un lontano punto comune. Un momento! Le due direzioni sono uguali,

Nella prima parte si vede il profilo spaziale di un'onda, cioè il modo in cui la grandezza che oscilla si distribuisce nello spazio, avendo fissato un ben preciso istante di