• Non ci sono risultati.

La programmazione modulare

N/A
N/A
Protected

Academic year: 2021

Condividi "La programmazione modulare"

Copied!
28
0
0

Testo completo

(1)

Corso di Programmazione ad Oggetti

Introduzione alla programmazione ad oggetti

a.a. 2008/2009

(2)

La programmazione modulare

■ Un programma può essere visto come un insieme di moduli che interagiscono tra di loro. Tutte le funzioni di un modulo sono scritte nello stesso file.

■ Ogni modulo è fatto di due parti, la specifica e l'implementazione. Questi due aspetti sono separati mettendoli in due file diversi: la specifica si trova nel suo file .h mentre l'implementazione nel file .cpp.

interfaccia

(Visibile dall'esterno) corpo

(Nascosto all'esterno e protetto)

MODULO

(3)

La programmazione modulare

■ I moduli non fanno parte del linguaggio, ma rappresentano una tecnica di organizzazione del codice molto seguita dai programmatori.

■ La programmazione modulare offre enormi vantaggi nell'implementazione di programmi: in particolare, al crescere della complessità dei programmi da sviluppare, consente rendere più semplici ed efficaci le operazioni di modifica e di testing del codice prodotto.

■ Tra i principali vantaggi:

– Suddivisione del lavoro tra diversi programmatori;

– Semplificazione del riuso del software – Semplificazione della manutenzione;

(4)

La programmazione ad oggetti

■ La programmazione ad oggetti rappresenta un ulteriore sviluppo rispetto alla programmazione modulare.

■ La programmazione orientata agli oggetti ( Object Oriented Programming, OOP) è un paradigma di programmazione, in cui un programma viene visto come un insieme di oggetti che interagiscono tra di loro.

■ Nei linguaggi OOP esiste un nuovo tipo di dato, la classe. Questo tipo di dato serve appunto a modellare un insieme di oggetti dello stesso tipo.

■ In generale, un oggetto è caratterizzato da un insieme di attributi e da un insieme di funzionalità

Un paradigma di programmazione è caratterizzato dall'insieme di concetti e astrazioni che utilizza per rappresentare un programma e i passi che compongono un calcolo.

Esempi di paradigmi sono: imperativo, funzionale, object oriented, logico:

(5)

Un esempio di oggetto

■ Un automobile può essere caratterizzata in questo modo:

attributi funzionalità potenza frena

marce accellera

peso cambio marcia

cilindrata caqmbia direzione

■ È possibile interagire con l'automobile, per determinarne il suo comportamento attraverso il suo interfaccia che permette di effettuarele operazioni consentite:

– Pedale del freno

– Pedale dell’acceleratore – Leva del cambio

(6)

Un ulteriore esempio: un videogioco

(7)

Classi e oggetti: un esempio

Orco Orco Cavaliere Cavaliere

Classi

Istanze (Oggetti)

(8)

La programmazione orientata agli oggetti

■ La programmazione orientata agli oggetti si basa su alcuni concetti fondamentali:

Classe

IncapsulamentoOggetto

EreditarietàPolimorfismo

(9)

Linguaggi ad oggetti

■ E’ possibile adottare tecniche di programmazione basate sugli oggetti anche in linguaggi tradizionali (ad es., in C o Pascal) adoperando opportune discipline di programmazione, aderendo cioè ad un insieme di regole.

■ Queste regole non fanno però parte del linguaggio e il loro uso non può essere forzato né verificato dal compilatore.

■ Un linguaggio di programmazione ad oggetti offre costrutti espliciti per la definizione di entità (oggetti) che incapsulano una struttura dati e le operazioni possibili su di essa.

■ Alcuni linguaggi, in particolare il C++, consentono di definire tipi astratti, e quindi istanze (cioè, variabili) di un dato tipo astratto. In tal caso il linguaggio basato sugli oggetti presenta costrutti per la definizione di classi e di oggetti.

(10)

Classi e oggetti

■ Nei linguaggi a oggetti, il costrutto class consente di definire nuovi tipi di dato e le relative operazioni.

■ Le operazioni possono essere definite 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, e

– si possono eseguire operazioni su di esse

■ Un oggetto è una variabile che appartiene ad un particolare tipo di dato definito dall'utente per mezzo del costrutto class.

■ Nel gergo dei linguaggi OO una variabile definita di un certo tipo (classe) rappresenta un'istanza della classe.

■ Lo stato di un oggetto, invece, è rappresentato dai valori correnti delle variabili che costituiscono la struttura dati utilizzata per implementare il tipo di dato rappresntato definito dalla classe.

(11)

Caratteristiche di una classe

■ La classe è un modulo software con le seguenti caratteristiche:

– E’ 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

(12)

Il linguaggio C++

■ C++ è un linguaggio di programmazione general-purpose che supporta:

– la programmazione procedurale (è un C “migliore”);

– la programmazione orientata agli oggetti;

– la programmazione generica.

■ C++ è quindi un linguaggio ibrido, nel senso che supporta più paradigmi di programmazione

(13)

L'incapsulamento

■ L'incapsulamento (in inglese: information hiding) è la suddivisione di un oggetto in due parti:

– Interfaccia:

• costituito dall'insieme di metodi (detti anche messagi) che possono essere invocati dall'utente per accedere alle funzionalità dell'oggetto;

– Implementazione

• Contiene l'implementazione delle funzionalità dell'oggetto e delle strutture dati necessarie

• È nascosta all’utente

■ L'incapsulamento è realizzato per mezzo delle istruzioni:

– Private: le funzioni e le variabili definite private NON sono accessibili all'esterno della classe.

(14)

Le classi in C++

■ Il linguaggio C++ supporta esplicitamente la dichiarazione e la definizione di tipi astratti da parte dell'utente mediante il costrutto class; le istanze di una classe vengono dette oggetti.

■ In una dichiarazione class occorre specificare sia la struttura dati che le operazioni consentite su di essa. Una classe possiede, in generale, una sezione pubblica ed una privata.

■ La sezione pubblica contiene tipicamente le operazioni (dette anche metodi) consentite ad un utilizzatore della classe. Esse sono tutte e sole le operazioni che un utente può eseguire, in maniera esplicita od implicita, sugli oggetti.

■ La sezione privata comprende le strutture dati e le operazioni che si vogliono rendere inaccessibili dall'esterno.

(15)

Le classi in C++: un esempio

class Contatore { public:

void Incrementa(); // operazione incremento void Decrementa(); // operazione decremento

unsigned int give_value(); // restituisce il valore // corrente

private:

unsigned int value; // valore corrente const unsigned int max; // valore massimo };

Inter faccia

Implem entazione

(16)

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.

■ Si parla anche di modello client- server: la classe è un server che mette a disposizione un certo numero di servizi ai clients (utenti).

client SERVER

classe utente

(17)

Produzione e uso di una classe

// Modulo utilizzatore del modulo

// Contatore

#include “Contatore.h”

unsigned int i;

Contatore cont1, cont2;

. . .

cont1.incrementa();

cont2.decrementa();

i = cont1.get_value();

// Interfaccia del // modulo Contatore class Contatore {

. . . };

Utente.cpp Contatore.h

// Implementazione del modulo Contatore

#include “Contatore.h”

Contatore::incrementa() {

. . }

Contatore::decrementa() {

.

Contatore.cpp

(18)

Ereditarietà 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

(19)

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

(20)

Ereditarietà

Generalizzazione: dal particolare al generale

Specializzazione o particolarizzazione: dal particolare al generale

Generalizzazione Specializzazione

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

(21)

Ereditarietà: un esempio

Oggetto

Veicolo

Veicolo Senza Motore

Veicolo A Motore

Motocicletta Automobile

Taxi

Aereo

(22)

Ereditarietà: vantaggi

■ 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

(23)

Ereditarietà

■ 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

(24)

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.

(25)

Polimorfismo: un esempio

■ Si consideri una gerarchia di classi di figure geometriche:

■ Sia ad es. A un vettore di N oggetti della classe Figura, composto di oggetti delle

Triangolo

+disegna()

Rettangolo

+disegna()

Quadrato

+disegna() Figura

+disegna()

(26)

Polimorfismo: un esempio

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

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).

(27)

Polimorfismo: estensibilità e binding dinamico

■ 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.

(28)

Vantaggi della programmazione OO

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

9 modularità: le classi sono i moduli del sistema software;

9 coesione dei moduli: una classe è un componente software ben coeso in quanto rappresentazione di una unica entità;

9 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;

9 information hiding: sia le strutture dati che gli algoritmi possono essere nascosti alla visibilità dall'esterno di un oggetto;

9 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;

9 estensibilità: il polimorfismo agevola l'aggiunta di nuove funzionalità, minimizzando le modifiche necessarie al sistema esistente quando si vuole estenderlo.

Riferimenti

Documenti correlati

■ La funzione costruttore di un oggetto locale (definito all'interno di una funzione o di un blocco di istruzioni) viene eseguita nel momento in cui viene incontrata l'istruzione

■ Le variabili static sono molto utili per gestire informazioni comuni a tutti gli oggetti di una classe (per esempio possono fornire i dati di default per l'inizializzazione

■ così come una classe è un modello per istanziare oggetti (della classe) a tempo d'esecuzione, un template è un modello per istanziare classi o funzioni (del template) a tempo

 definisce le variabili (memoria privata) ed i metodi (interfaccia) di tali oggetti.  altri punti

 definisce le variabili (memoria privata) ed i metodi (interfaccia) di tali oggetti.  altri punti

public void draw(Window w, Position where){…}. public

 Un errore in esecuzione, perch`e il metodo propone restituisce un oggetto di tipo dinamico LeggeTaglio;.  un errore in compilazione perch`e il metodo propone non `e

quando viene istanziato da un altro oggetto della stessa classe. l’argomento è una reference ad un