Che materia stai cercando?

Anteprima

ESTRATTO DOCUMENTO

Boolean

senso gradi.

stringhe: classi.

ammette OVEST.

default è opzionale

rappresentano :

griglia. +setDirezione(_direzione:String)

Integer

questa 90

in

come e Direzione

dir "NORD"

di

operator++()

di possibili SUD, delle

della New() Direzione

antiorario

girano

posizione

di Ogni ammissibile argomento

coordinata. String

oggetti mattoni EST, =

costruttore

[0,0]. progettista

:

classe operator--()

4 String +New(_dir:String) :

le ed +getDirezione()

Gli metodi

La NORD, [0,0]

+operator++()

tra un

è +operator--()

i orario

come : crea

una -direzione posizione

Il I New() NORD. dal

Boolean

Boolean avente

costruttore definiti

direzione

: robot

+operator==(_posizione:Posizione)

:

+operator<(_posizione:Posizione) interni

e

Il

+moveTo(_direzione:Direzione)

Posizione +setPosizione(_posizione:Posizione) dettagli

+setDirezione(_direzione:Direzione)

Posizione Posizione

+setX(_x:int) +setY(_y:int) Direzione

int int i

+stampa()

0 0 visibili

: :

:

= = +getX() +getY()

+New() Posizione

Robot Direzione

int int

: : : :

-x -y +getPosizione()

+giraASinistra() +getDirezione()

Robot +giraADestra() Sono

102..* : :

-posizione +avanza()

-direzione :

+New() Robot.

0..1

Griglia progetto

boolean

:

_larghezza:Integer) Boolean : del

_posizioneNew:Posizione) arrivo [altezza,larghezza]

1

una default classi

(le

se di

: possono

[2,2]

_posizione:Posizione) posione

9;

delle di

Boolean di delle

a

valore

maggiore

e

_altezza:Integer, pone

Boolean non

percorso la

Boolean privata”

stabilisce

al a

:

Griglia la griglia

-generaPercorso(_posizione:Posizione)

+internoInGriglia(_posizione:Posizione) [1,1]

posta valida,

se

:

+inPercorso(_posizione:Posizione) inizio da

-genera(_posizioneOld:Posizione, valida

: della

+inGriglia(_posizione:Posizione) viene e vanno

è

+inserisciRobot(_robot:Robot, casuale “Vista

di non

+New(_posizione:Posizione, bordo

è

posizione percorso)

valida

input input

griglia griglia

Posizione

Posizione Posizione il percorso

10 in adiacenti

New(): 6:

è in

Posizione

Posizione Robot della dimensione della

10 non

= della posizione al Figura

int

= appartenere

:

: dimensione

: costruttore

int dimensioni posizioni

+getArrivo()

:

-grid[100..*] un

+getInizio() necessita

: +disegna()

-larghezza posizioni

-androide : genera

-altezza

:

: -arrivo la

-inizio una se Le

Il - - - -

14

di un oggetto dia a quest’ultimo una particolare informazione e che poi lo stesso chiamante, o un

altro, chieda all’oggetto di offrire nuovamente tale informazione. In altre parole, un oggetto non

muore al termine della sua esecuzione, ma rimane in attesa di tornare nuovamente in azione.

Con un’espressione tecnica diciamo pertanto che un oggetto conserva il proprio stato (stato in

effetti significa l’insieme dei valori detenuti internamente da un oggetto tramite i propri attributi).

Per esempio, il robot conserva indefinitamente la conoscenza della posizione su cui si trova e della

direzione in cui è rivolto: nelle sezioni refsec:incapsulamento e 2 abbiamo visto però che il modo in

cui l’oggetto sceglie di conservare queste conoscenze è solo un suo problema interno.

L’incapsulamento, l’occultamento delle informazioni e dell’implementazione e la conservazione

dello stato costituiscono il nucleo fondamentale dell’orientamento agli oggetti, pur non essendo di

certo idee nuove: sono molti anni che in tutto il mondo esperti di informatica studiano questi

concetti con il termine di tipi di dato astratti (TDA). Tuttavia l’orientamento agli oggetti va molto

più in là dei TDA, come rivelano le altre sei proprietà della tecnologia a oggetti descritte nelle

prossime sezioni.

4 Identità degli oggetti

La prima proprietà dell’orientamento agli oggetti che va oltre il concetto di tipo di dato astratto è

di importanza fondamentale: tutti gli oggetti hanno la propria identità.

Definizione 6 (Identità degli oggetti) L’identità degli oggetti è la proprietà per cui ciascun

oggetto (a prescindere dalla sua classe o dallo stato corrente) può essere identificato e trattato

come una distinta entità software.

Ogni oggetto ha una caratteristica unica che lo distingue dai suoi simili: questa caratteristica

viene offerta dal meccanismo della maniglia (handle) dell’oggetto. La maniglia è nota formalmente

come identificatore di oggetto (OID, Object IDentifier); la maggior parte degli ambienti a oggetti

crea automaticamente questo OID. La creazione di un oggetto può essere spiegata analizzando la

seguente linea di codice:

var a1: Robot := Robot.new(); // creazione di un oggetto di tipo Robot

La parte destra di questa istruzione crea un nuovo oggetto (della classe Robot ), visibile nella

Figura 7. Osservate la maniglia all’oggetto, che nell’esempio della figura è il numero 602237: si

tratta di un identificatore (OID) collegato a un oggetto al momento della sua creazione. Sono due

le regole che si applicano alle maniglie:

1. Un oggetto mantiene la stessa maniglia per tutta la sua vita, indipendentemente da ciò che

può accadere all’oggetto in questo tempo.

2. Due oggetti non possono avere la stessa maniglia. Ogni volta che il sistema crea un nuovo

oggetto in fase di esecuzione, esso gli assegna una maniglia diversa da tutte le altre maniglie

passate, presenti e future. Pertanto è sempre possibile distinguere due oggetti, anche se sono

identici in struttura e nelle informazioni che contengono, perché avranno maniglie diverse.

La parte sinistra della linea di codice è la dichiarazione var a1: Robot . Si tratta di una normale

dichiarazione che assegna un nome che abbia un significato per il programmatore (in questo caso

a1 ) a un’insieme di locazioni di memoria che, per esempio, possono detenere un valore. Il termine

15 602237

getPosizione()

posizione ...

avanza() direzione

giraASinistra()

Figura 7: Un oggetto con la sua maniglia.

a1

602237 602237

getPosizione()

posizione ...

avanza() direzione

giraASinistra()

Figura 8: a1 punta all’oggetto con maniglia 602237.

Robot in questo caso è il nome della classe di a1 , argomento che tratteremo nella sezione 6. Il

codice precedente fa in modo che la variabile a1 contenga la maniglia dell’oggetto creato.

Nessuno (programmatore, utente o chiunque altro) vedrà mai effettivamente la maniglia del

nuovo oggetto (602237), a meno che non frughi nella memoria con un debugger. Il programmatore

invece accederà all’oggetto tramite la variabile a1, alla quale egli stesso ha dato il nome. In altre

parole, a1 punta all’oggetto che ha come maniglia 602237, come illustrato nella Figura 8.

Alcuni ambienti orientati agli oggetti utilizzano l’indirizzo di memoria fisico dell’oggetto come

sua maniglia. Pur trattandosi di una soluzione semplice, potrebbe rivelarsi un grande errore qualora

l’oggetto venga spostato nella memoria o nel caso in cui venga scaricato nell’area del disco dedicata

alla memoria virtuale. E’ pertanto meglio che una maniglia sia un valore senza alcun significato

particolare, casuale ma unico. Supponiamo ora di dover eseguire anche la seguente linea di codice:

var a2: Robot := Robot.New();

Questa linea creerà un altro oggetto (anch’esso della classe Robot) con una maniglia, per esempio,

avente il valore 142857, per poi memorizzarla nella variabile a2 (Figura 9).

Per maggiore chiarezza, scriveremo un’altra istruzione di assegnamento:

a2 := a1; 16

a2

142857 142857

getPosizione()

posizione ...

avanza() direzione

giraASinistra()

Figura 9: a2 punta all’oggetto con maniglia 142857.

Ora le variabili a1 e a2 puntano entrambe allo stesso oggetto (il primo che abbiamo creato,

l’istanza di Robot con la maniglia 602237): fate riferimento alla Figura 10. Avere due variabili

che puntano allo stesso oggetto è di solito inutile; peggio ancora, ora non abbiamo alcun modo di

raggiungere il secondo oggetto (quello che ha la maniglia 142857). Di fatto, pertanto, quell’oggetto

è scomparso, proprio come se fosse caduto in un buco nero. In pratica però esso non scompare

realmente. la maggior parte degli ambienti orientati agli oggetti in questa circostanza utilizzerebbe

un garbage collector per rimuovere l’oggetto dalla memoria.

L’idea di attribuire a tutti gli oggetti una loro identità per mezzo di una maniglia sembra

innocua, anche nella peggiore delle ipotesi. Sorprendentemente, tuttavia, questa semplice idea

provoca un profondo cambiamento nel modo in cui progettiamo e costruiamo il software a oggetti,

come vedremo nella prossima sezione.

5 Messaggi

Un oggetto chiede a un altro oggetto di svolgere un’attività tramite un messaggio. Inoltre, molti

messaggi trasferiscono informazioni da un oggetto a un altro. La maggior parte degli elenchi citati

all’inizio del documento tratterebbero certamente i messaggi come una delle proprietà fondamentali.

Definizione 7 (Messaggio) Un messaggio è il veicolo tramite il quale un oggetto mittente ogg1

recapita a un oggetto destinatario ogg2 una richiesta affinché quest’ultimo applichi uno dei suoi

metodi.

In questa sezione vengone descritte l’anatomia di un messaggio, le caratteristiche degli argomenti

dei messaggi, il ruolo di un oggetto che invia un messaggio, il ruolo di un oggetto che riceve un

messaggio e tre tipologie di messaggi.

5.1 Struttura dei messaggi

Un messaggio è costituito da diversi elementi sintattici, ognuno dei quali è importante di per sé

nella progettazione a oggetti. Affinché l’oggetto ogg1 invii un messaggio sensato all’oggetto ogg2

, l’oggetto ogg1 deve conoscere tre cose:

1. La maniglia di ogg2 (ovviamente quando inviate un messaggio, dovete sapere a chi sta

andando). 17

a1

602237 602237

getPosizione()

posizione ...

avanza() direzione

giraASinistra()

a2

142857 142857

getPosizione()

posizione ...

avanza() direzione

giraASinistra()

Figura 10: a1 e a2 puntano entrambe allo stesso oggetto e l’altro oggetto non è più raggiungibile.

18

2. Il nome dell’operazione di ogg2 che ogg1 desidera eseguire.

3. Qualunque informazione supplementare (parametro) che sarà necessaria a ogg2 per eseguire

la sua operazione.

L’oggetto che invia il messaggio ( ogg1 nell’esempio precedente) viene chiamato mittente (sen-

der ) mentre l’oggetto che riceve il marcatore ( ogg2 ) prende il nome di destinatario (target) – altri

termini utilizzati per mittente e destinatario sono rispettivamente client e server . Il software del

robot offre diversi esempi di messaggi, uno dei quali è il seguente:

a1.avanza();

In questa istruzione, a1 punta all’oggetto destinatario del messaggio (ossia ne contiene la mani-

glia); se vi ricordate, la maniglia è stata assegnata ad a1 con l’istruzione di assegnamento var a1

: Robot = Robot.New() , mentre avanza() è il nome dell’operazione (appartenente all’og-

getto destinatario) che deve essere eseguita (questo messaggio non ha bisogno di parametri, perché

avanza() sposta il robot sempre di una posizione).

L’invio di un messaggio equivale alla tradizionale chiamata a funzione o procedura; per esempio,

in un linguaggio procedurale avremmo potuto scrivere

call avanza (a1);

osservate però l’inversione: con le tecniche software tradizionali ricorriamo a un’entità procedurale

e le forniamo l’oggetto sul quale agire; con l’impostazione a oggetti, invece, ricorriamo a un oggetto,

che poi esegue una delle sue entità procedurali.

A questo punto questa distinzione sembra semplicemente sintattica, o quanto meno filosofica.

Tuttavia, quando parleremo (nella sezione 8) di polimorfismo, sovraccarico e legame dinamico,

vedremo che questa enfasi sul concetto l’oggetto per primo, la procedura per seconda porta a

un’importante differenza pratica tra la struttura a oggetti e la struttura tradizionale. Ciò avviene

perché diverse classi di oggetti possono utilizzare lo stesso nome di operazione per operazioni che

mettono in atto diversi comportamenti specifici della classe o comportamenti simili ma con nomi

diversi.

5.2 Parametri dei messaggi

Come avveniva con i vecchi sottoprogrammi, la maggior parte dei messaggi passa parametri avanti

e indietro. Per esempio, se modificassimo l’operazione avanza() facendo in modo che l’operazione

restituisca un indicatore (flag) contenente il risultato dell’avanzamento, allora avremmo:

a1.avanza( in numeroPosizioni, out avanzamentoOK);

oggetto destinatario nome operazione parametri input parametri output

messaggio

19

1 : avanza(in numeroDiPosizioni, out avanzamentoOK)

ogg : Object a1 : Robot

Figura 11: Il messaggio a1.avanza(in numeroPosizioni, out avanzamentoOK), espresso con la

notazione UML.

Pertanto la struttura di un messaggio a un oggetto destinatario viene definita dalla segnatura

(o prototipo) dell’operazione del destinatario da invocare, che è costituita da tre parti: il nome

dell’operazione, l’elenco dei parametri di input (con prefisso in ) e l’elenco degli parametri di

output (con prefisso out ). Entrambi gli elenchi di parametri potrebbero essere vuoti; per brevità,

normalmente si omette la parola chiave in , considerandola predefinita.

I parametri di un messaggio riflettono un altro fondamentale contrasto tra il software orientato a

oggetti e quello tradizionale. In un ambiente a oggetti puro (come quello di Smalltalk), i parametri

del messaggio non sono dati, bensı̀ maniglie di oggetti. Gli argomenti dei messaggi sono oggetti

vivi!

Per esempio, la Figura 11 illustra un messaggio tratto dal programma del robot, espresso con

la notazione UML.

Se dovessimo fare un’istantanea del programma del robot nel momento in cui sta eseguendo

questo messaggio ed estrarre i valori dei parametri del messaggio, troveremmo qualcosa di inatteso.

Per esempio, potremmo trovare:

- numeroDiOuadrati impostato a 123432

- avanzamentoOK impostato a 664730

Perché questi strani numeri? Perché 123432 potrebbe essere la maniglia dell’oggetto (della classe

Integer ) che potremmo considerare come l’intero 2, mentre 664730 potrebbe essere la maniglia

dell’oggetto (della classe Boolean ) che normalmente considereremmo come valore logico true .

Per fare un altro esempio, se dovessimo eseguire un sistema di gestione del personale orientato

agli oggetti e facessimo la stessa cosa, potremmo trovare l’argomento impDelMese impostato a

441523, che potrebbe essere la maniglia dell’oggetto (della classe Impiegato ) che rappresenta

Giacomo Colombo.

5.3 I ruoli degli oggetti nei messaggi

In questa sezione analizzo i tre ruoli possibili che abbiamo indicato per un sistema orientato agli

oggetti. Un oggetto può essere:

1. mittente di un messaggio;

2. destinatario di un messaggio;

3. riferito da una variabile all’interno di un altro oggetto.

20

1 : messaggio1(...) 2 : messaggio2(...)

ogg1 : ClasseA ogg2 : ClasseB ogg3 : ClasseC

Figura 12: Due messaggi tra due coppie di oggetti.

Un dato oggetto può assumere uno o più di questi ruoli nel corso della sua esistenza. Dalla Figura 12

si evince chiaramente che un oggetto non è mai un mittente nato o un destinatario nato. Infatti,

per messaggiol(), ogg1 è il mittente e ogg2 è il destinatario; per messaggio2() , invece, ogg2

è il mittente e ogg1 è il destinatario. Vediamo pertanto che in diversi momenti lo stesso oggetto

può recitare entrambe le parti. I termini mittente e destinatario sono pertanto relativi a un dato

messaggio e non sono proprietà fisse degli oggetti stessi.

Un ambiente orientato agli oggetti puro contiene solo oggetti che giocano uno o più dei tre ruoli

visti sopra. Nella tecnologia a oggetti pura non c’è alcun bisogno di dati, perché gli oggetti possono

fare tutto il lavoro software necessario ai dati; e in Smalltalk (un linguaggio a oggetti molto puro),

non c’è proprio alcun dato! Al momento dell’esecuzione ci sono solo oggetti che puntano ad altri

oggetti (tramite variabili) e comunicano reciprocamente passandosi avanti e indietro maniglie di

altri oggetti ancora.

Invece, in C++ (che è un linguaggio misto, per il fatto che utilizza dati e funzioni tradizionali

insieme agli oggetti), gli argomenti possono essere puntatori a qualunque cosa. Se il codice C++

è puro come in Smalltalk, tutti i parametri saranno puntatori a oggetti. Se invece si mescolano

oggetti e dati in un programma, alcuni dei parametri possono essere semplicemente dei dati (o

puntatori a dati). Un commento simile è valido per il codice Java, sebbene quest’ultimo sia un

linguaggio molto meno a ruota libera rispetto al C++.

5.4 Tipi di messaggi

Un oggetto può ricevere tre tipi di messaggi: informativi, interrogativi e imperativi. In questa

sezione definiamo brevemente e offriamo un esempio di ogni tipo di messaggio, ancora una volta

ricorrendo a codice relativo al controllo del robot.

Definizione 8 (Messaggio informativo) Un messaggio informativo è un messaggio a un oggetto

che fornisce a quest’ultimo delle informazioni per aggiornarsi (è noto anche come messaggio di

aggiornamento, di inoltro o push). E un messaggio orientato al passato per il fatto che in genere

informa l’oggetto di ciò che è già avvenuto altrove.

Un esempio di messaggio informativo è:

impiegato.sposato(dataMatrimonio: Data)

questo messaggio dice a un oggetto, che rappresenta un dipendente, che il dipendente in questione si

è sposato in una certa data. In generale, un messaggio informativo comunica a un oggetto qualcosa

che è accaduto nella parte di mondo reale rappresentata da quell’oggetto.

Definizione 9 (Messaggio interrogativo) Un messaggio interrogativo è un messaggio a un og-

getto che richiede a quest’ultimo di rivelare alcune informazioni su di sé (è noto anche come mes-

saggio di lettura, retrospettiva o pulI). È un messaggio orientato al presente per il fatto che chiede

all’oggetto di comunicare informazioni correnti. 21

Un esempio di messaggio interrogativo è:

a1.posizione()

che chiede al robot di comunicare la sua posizione corrente sulla griglia. Questo tipo di mes-

saggio non modifica nulla, essendo in genere un’interrogazione relativa a quella parte di mondo

rappresentata dall’oggetto destinatario.

I metodi invocati tramite messaggi informativi e interrogativi sono di solito rispettivamente

definiti mediante la seguente sintassi:

getNomeattributo(): Classe

setNomeattributo ( nomeattributo: Classe )

dove Nomeattributo è il nome di un attributo (appartenente alla classe Classe ). In genere, per

un attributo possono essere definiti entrambi i metodi, e vengono brevemente indicati come metodi

set e metodi get .

Definizione 10 (Messaggio imperativo) Un messaggio imperativo è un messaggio a un oggetto

che richiede a quest’ultimo di portare a termine qualche azione su se stesso, su un altro oggetto o

persino sull’ambiente attorno al sistema (è noto anche come messaggio di forzatura o di azione).

È un messaggio orientato al futuro per il fatto che chiede all’oggetto di compiere qualche azione

nell’immediato futuro.

Un esempio di messaggio imperativo è

a1.avanza()

che provoca lo spostamento in avanti del robot. Questo tipo di messaggio spesso si traduce nel-

l’esecuzione da parte dell’oggetto destinatario di qualche algoritmo che gli consenta di fare quanto

richiesto.

I sistemi in tempo reale orientati agli oggetti, nei quali gli oggetti controllano componenti

hardware, spesso contengono molti messaggi imperativi. Questi messaggi illustrano chiaramente lo

spirito rivolto al futuro di un messaggio imperativo. Considerate questo esempio tratto dal mondo

della robotica:

manoSinistraRobot.vaiAPosizione (x,y,z: Lunghezza, thetal,theta2,theta3: Angolo)

Questo messaggio stabilisce la posizione e l’orientamento nello spazio della mano sinistra di un

robot. L’algoritmo potrebbe richiedere lo spostamento della mano del robot, del suo braccio e/o

quello del robot stesso. I sei argomenti rappresentano i sei gradi di libertà della mano, un’en-

tità tridimensionale nello spazio. A questo punto ci spostiamo dai messaggi a un’altra proprietà

indiscutibilmente fondamentale per l’orientamento agli oggetti, la classe di oggetti.

6 Classi

Nel software di controllo del robot abbiamo creato un oggetto (che rappresenta un robot) eseguendo

Robot.New() . La classe Robot è servita da modello per creare gli oggetti robot (come quello con

maniglia 602237). Ogni volta che eseguiamo l’istruzione Robot.New() , istanziamo un oggetto che

è strutturalmente identico a tutti gli altri oggetti creati dall’istruzione Robot.New() , dove per

22 123456

robot_1

ISTANZIAMENTO 234567

Robot robot_2 530061

robot_3

Figura 13: Tre oggetti istanziati dalla stessa classe ( Robot ).

strutturalmente identico intendiamo dire che ogni oggetto robot ha le stesse operazioni e variabili, in

particolare quelle che il programmatore ha codificato quando ha scritto la classe Robot (Figura 13).

Definizione 11 (Classe) Una classe è la sagoma a partire dalla quale vengono creati (istanziati)

gli oggetti. Ogni oggetto ha la stessa struttura e comportamento della classe dalla quale è istanziato.

Se l’oggetto ogg appartiene alla classe C , diciamo che ogg è un’istanza di C .

Vi sono due differenze tra gli oggetti di una stessa classe: ogni oggetto ha una maniglia diversa

e, in qualunque momento particolare, ogni oggetto probabilmente avrà uno stato diverso (il che

significa diversi valori memorizzati nelle sue variabili). Dato che inizialmente la distinzione tra

classe e oggetto può creare una certa confusione, suggeriamo queste semplici definizioni, che possono

aiutare a fare un po’ di chiarezza.

• Una classe è ciò che progettate e programmate.

• Gli oggetti sono ciò che create (a partire da una classe) in fase di esecuzione.

Pacchetti software molto noti presentano una stretta analogia con classi e oggetti. Supponiamo

che acquistiate un programma per foglio di calcolo chiamato Visigoth 5.0 prodotto dalla Wallisoft

Corp (fondata dallo stesso Wally Soft). Il pacchetto in sé sarebbe un analogo della classe, mentre

i fogli di calcolo veri e propri che create a partire da esso sarebbero simili agli oggetti. Ogni foglio

di calcolo ha a disposizione tutti i meccanismi di foglio di calcolo in quanto istanza della classe

Visigoth.

In fase di esecuzione, una classe come Robot può generare 3, 300 o 3.000 oggetti (ossia istanze

di Robot ). Una classe pertanto assomiglia a una sagoma: una volta ritagliata, da essa è possibile

ricavare la stessa forma migliaia di volte, ottenendo oggetti tutti identici tra loro e, ovviamente,

uguali alla forma della sagoma originale. Per essere ancora più chiari, analizziamo più da vicino

la popolazione di oggetti generati da una singola classe: come abbiamo visto, tutti gli oggetti di

una classe hanno la stessa struttura, ossia lo stesso insieme di operazioni e di attributi. Pertanto

23

oggetto1 oggetto2 oggetto3

metodoA varV metodoA varV metodoA varV

metodoB varW metodoB varW metodoB varW

metodoC varX metodoC varX metodoC varX

metodoD varY metodoD varY metodoD varY

maniglia maniglia maniglia

varZ varZ varZ

400 byte 10 byte 400 byte 10 byte 400 byte 10 byte

6 byte 6 byte 6 byte

Figura 14: I metodi, le variabili e le maniglie per tre oggetti della stessa classe, insieme con i

requisiti di memoria degli oggetti (approccio con spreco di risorse).

ogni oggetto (istanza) di una classe ha la propria copia dell’insieme di metodi che gli occorrono

per implementare le operazioni e l’insieme di variabili necessarie per implementare gli attributi. In

linea di principio, in un dato momento ci sono tante copie dei metodi e delle variabili (0, 3, 300,...)

quanti sono gli oggetti istanziati in quel momento (Figura 14).

Al fine di spiegare ulteriormente la reale struttura di un insieme di oggetti della stessa classe,

che chiameremo C , analizziamo brevemente i dettagli dell’implementazione. Supponiamo che ogni

metodo che implementa una delle operazioni della Figura 14 occupi 100 byte, che ogni variabile

occupi 2 byte e che per ogni maniglia occorrano 6 byte. In base a ciò, oggetto i occuperà 416 byte

di memoria (ossia 100 * 4 + 5 * 2 + 6 * 1). I tre oggetti insieme pertanto occuperanno 1.248 byte

di memoria (ossia 3 * 416).

Questo approccio all’allocazione della memoria per gli oggetti provocherebbe però un notevole

spreco di risorse, perché ognuno dei tre insiemi di metodi dei tre oggetti è identico; e dal momento

che ciascun insieme di metodi contiene solo codice procedurale, tutti gli oggetti possono condividere

un solo insieme. Quindi, sebbene in linea di principio ogni oggetto abbia il proprio insieme di metodi

per l’implementazione delle operazioni, in pratica (per risparmiare spazio) condividono tutti la

stessa copia fisica.

Di converso, sebbene le maniglie e le variabili di ciascun oggetto siano identiche in struttura da

un oggetto all’altro, esse non possono essere condivise tra più oggetti per l’ovvio motivo che devono

contenere diversi valori in fase di esecuzione.

Cosı̀, sebbene gli oggetti della classe C condividano tutti lo stesso insieme di operazioni, la

memoria totale consumata dai tre oggetti di C sarà effettivamente di 448 byte (400 byte per il

singolo insieme di metodi, 30 byte per i 3 set di variabili e 18 byte per le 3 variabili). Questo

consumo di soli 448 byte è inferiore ai 1.248 byte della soluzione nativa e rappresenta il modo

normale in cui gli ambienti a oggetti allocano la memoria per gli oggetti (Figura 15). Ovviamente il

risparmio cresce con il crescere del numero di oggetti istanziati: se con soli tre oggetti si consuma il

35,9% della memoria (ovvero 448 byte rispetto ai previsti 1.248 totali), con 300 oggetti il consumo

scende al 4,1% (5.200 byte rispetto a 124.800).

Quasi tutte le operazioni e gli attributi analizzati in questa sezione appartengono a singoli

oggetti e prendono il nome di operazioni di istanza e attributi di istanza. Tuttavia esistono

anche operazioni di classe e attributi di classe. Per definizione esiste sempre esattamente un insieme

di operazioni di classe e di attributi di classe per una data classe, indipendentemente dalla quantità

di oggetti di quella classe che possono essere stati istanziati. Le operazioni e gli attributi di classe

24

oggetto1 oggetto2 oggetto3

varV varV varV

metodoA varW varW varW

metodoB varX varX varX

metodoC varY varY varY

metodoD maniglia maniglia maniglia

varZ varZ varZ

6 byte

6 byte 6 byte

400 byte 10 byte 10 byte 10 byte

Figura 15: Illustrazione schematica della memoria effettiva (448 byte) utilizzata da 3 oggetti della

stessa classe.

sono necessari per far fronte alle situazioni che non possono essere responsabilità di un qualunque

oggetto singolo: l’esempio più famoso di un’operazione di classe è New() , che istanzia un nuovo

oggetto di una data classe.

Il messaggio New() non potrebbe mai essere inviato a un singolo oggetto. Supponiamo, per

esempio, di avere tre oggetti della classe ClienteBanca , che rappresentano tre effettivi clienti di

una banca (faremo riferimento a questi oggetti con i nomi bob , carol e ted ). Supponiamo

inoltre che desideriamo istanziare un nuovo oggetto dalla classe ClienteBanca (per esempio alice

). A quale oggetto invieremmo il messaggio New() ? Non ci sarebbe nessun particolare motivo

per inviarlo a bob piuttosto che a carol o a ted ; peggio ancora, non avremmo mai potuto

istanziare il primo cliente della banca, in quanto inizialmente non ci sarebbe stato nessun oggetto

della classe ClienteBanca al quale inviare il messaggio New() .

Quindi, New() è un messaggio che deve essere inviato a una classe, invece che a un singolo

oggetto. L’esempio presente nel gioco del robot era Robot.New() , un messaggio di classe inviato

alla classe Robot per ottenere l’esecuzione della sua operazione di classe New() e creare cosı̀ un

nuovo oggetto, una nuova istanza della classe Robot .

Un esempio di attributo di classe potrebbe essere numeroDiRobotCreati: Integer , che

verrebbe incrementato da New() della classe Robot a ogni esecuzione di questa operazione.

Indipendentemente dal numero di oggetti robot, ci sarebbe solo una copia di questo attributo di

classe. Potreste progettare un’operazione di classe per offrire al mondo esterno la possibilità di

accesso a questo attributo di classe.

La Figura 16 illustra la struttura della memoria nel caso in cui la classe C abbia due operazioni

di classe (i metodi delle quali occupano 100 byte ciascuno) e tre attributi di classe (le variabili

dei quali occupano 2 byte ciascuna). Il numero di byte per il meccanismo di classe (206 in questo

esempio) rimarrà costante a prescindere dal numero di oggetti che C ha istanziato. Con l’aggiunta

di questo meccanismo di classe, C e il suo gregge di dodici oggetti ora occupa un totale di 798

(ossia 206 + 592) byte di memoria.

Osservate che, sia in linea di principio sia in pratica, c’è un solo insieme di metodi di classe

per classe: questo è in contrasto con i metodi di istanza, dove in linea di principio ogni oggetto ha

il proprio insieme (solo per risparmiare memoria facciamo in modo che gli oggetti condividano lo

stesso insieme di metodi per le loro operazioni). La distinzione tra variabili di classe e variabili di

istanza è quindi più chiara: ogni classe ha solo un insieme di variabili di classe, mentre esiste un

insieme di variabili di istanza per ogni oggetto della classe, sia in linea di principio sia di fatto.

25

oggetto1 oggetto2 oggetto3

varV varV varV

metodoA varW varW varW

metodoB varX varX varX

metodoC varY varY varY

metodoD maniglia maniglia maniglia

varZ varZ varZ

400 byte 6 byte

6 byte 6 byte

10 byte 10 byte

10 byte variabili di istanza e

metodi di istanza

metodoE varP maniglie (48 byte)

(400 byte)

metodoF varQ variabili di classe

varR

200 byte (6 byte)

metodi di classe

6 byte (200 byte)

Figura 16: Illustrazione schematica della memoria effettiva (846 byte) utilizzata da 3 oggetti e dal

meccanismo di classe

Quale è allora la differenza tra una classe e un TDA? La risposta è che un TDA descrive

un’interfaccia: è la facciata che dichiara cosa verrà fornito agli utenti del TDA, senza però dire

alcunché sul modo in cui il TDA verrà implementato. Una classe è una cosa in carne e ossa (o

almeno dotata di disegno interno e codice) che implementa un TDA. In effetti per un dato TDA

si potrebbero progettare e costruire diverse classi: per esempio, una di queste classi potrebbe

produrre oggetti molto efficienti in esecuzione, mentre da un’altra si potrebbero ottenere oggetti

che occupano poca memoria.

6.1 Composizione ed Aggregazione di classi

Quando in una classe si includono oggetti appartenenti ad altre classi si realizza il concetto di

associazione di tipo tutto/parte tra classi. Due sono i tipici modi di realizzare questa associazione:

composizione e aggregazione. Nel primo caso l’oggetto interno esiste solo per poter realizzare la

parte dell’oggetto esterno, mentre nel secondo caso l’oggetto interno esiste indipendentemente del

suo ruolo di parte. Spieghiamo in dettaglio, analizzando brevemente anche la notazione UML che

permette di rappresentare le due situazioni.

Composizione: La composizione è una struttura comune nei sistemi software, siano essi o meno

orientati agli oggetti, perché nella vita di ogni giorno abbiamo a che fare con molti oggetti composti.

Per esempio, un messaggio di posta elettronica è un composto che contiene un’intestazione e alcuni

paragrafi di testo. A sua volta, l’intestazione è composta dal nome del mittente, da quello del

destinatario, dall’oggetto del messaggio e da altre informazioni specifiche del sistema di trasmissione

elettronica dei messaggi. Ora però, prima di continuare, è necessario fare qualche precisazione

terminologica. L’associazione tutto/parte prende il nome di composizione, dove il tutto viene

chiamato (oggetto) composto, mentre la parte prende il nome di (oggetto) componente. Ecco le tre

caratteristiche più importanti della composizione.

26

Aliante 2

Fusoliera Coda Ala

Figura 17: Un oggetto composto ed i suoi componenti.

1. L’oggetto composto non esiste senza i suoi componenti. Per esempio, rimuovete le setole, il

manico e le piccole parti in gomma da uno spazzolino da denti, e non avrete più uno spazzolino

da denti. In effetti è sufficiente rimuovete le setole da uno spazzolino da denti perché diventi

difficile qualificarlo come tale. In questo senso diciamo che la vita di un oggetto composto

non può andare oltre quella dei suoi componenti.

2. In qualunque momento, ciascun oggetto componente dato può essere parte di un solo compo-

sto.

3. La composizione tipicamente è eterogenea, nel senso che è molto probabile che i com- ponenti

siano di tipi misti: alcune ruote, alcuni assi, dei pezzi di legno... ed ecco un carro.

L’oggetto composto della Figura 17 rappresenta un aliante semplificato formato da quattro com-

ponenti: una fusoliera, una coda, un’ala sinistra e un’ala destra. Come ulteriore esempio, vedi

la Figura 2, dove un oggetto robot è progettato come aggregato di una Posizione e di una

Direzione .

Analizziamo questa figura per capire il funzionamento di UML.

1. Un’associazione tra l’oggetto composto e ciascuno dei suoi componenti appare sul diagramma

come una linea di associazione, con un piccolo rombo nero collocato all’estremità accanto

all’oggetto composto.

2. La classe del composto, Aliante , appare a un’estremità della linea di associazione, mentre

all’altra estremità appare la classe di ciascun componente, Fusoliera , Coda e Ala

. Osservate che un componente come Ala può limitarsi a comparire una sola volta nel

diagramma; il numero componenti di tipo Ala (la sua molteplicità) compare numericamente

sulla linea di associazione, vicino alla classe contenuta.

3. E’ necessario indicare la molteplicità all’estremità del componente di ciascuna linea di associa-

zione. Se la molteplicità all’estremità del composto non viene indicata, allora si presuppone

che sia esattamente 1.

4. La linea di associazione non ha nome, che è la norma sia per la composizione sia per l’ag-

gregazione. Il motivo è che raramente, nel caso di un’associazione di composizione, un nome

27

aggiunge un significato che vada oltre quello di tutto/parte già indicato dalla simbologia. Le

forme verbali come ha, comprende, consiste e cosı̀ via non aggiungono nulla al modello.

Per quanto concerne l’implementazione, all’interno della classe Aliante possono essere dichiarate

le seguenti variabili:

fusoliera: Fusoliera;

coda: Coda;

alaSinistra: Ala;

alaDestra: Ala;

Quando un oggetto della classe Aliante , chiamiamolo aliante1 , viene istanziato e inizializzato,

la variabile coda punterà a un oggetto che rappresenta la coda di aliante1 . Analogamente, le

variabili fusoliera , alaSinistra e alaDestra contengono le maniglie degli altri componenti di

un oggetto della classe Aliante . Questa implementazione supporta la navigabilità da un oggetto

composto ai suoi oggetti componenti.

La composizione spesso è legata alla propagazione dei messaggi. Per esempio, per spostare un

simbolo di rettangolo su uno schermo, potreste chiedere all’oggetto rettangolo di spostare se stesso.

A sua volta, il rettangolo potrebbe inviare un messaggio a ciascuno dei suoi segmenti componenti e

dire loro di muoversi. Analogamente, per trovare il peso di una sedia, potreste inviare un messaggio

a ciascun componente della sedia richiedendone il peso.

Aggregazione: Come la composizione, l’aggregazione è un costrutto ben noto, per mezzo del

quale i sistemi rappresentano strutture tratte dal mondo reale. Per esempio, una città è un aggre-

gato di case, una foresta è un aggregato di alberi e un gregge è un aggregato di pecore. In altre

parole, l’aggregazione e un associazione gruppo/membri. Ancora una volta occorre qualche preci-

sazione terminologica. L’associazione prende il nome di aggregazione, dove il tutto viene chiamato

(oggetto) aggregato, mentre la parte prende il nome di (oggetto) costituente. Le tre caratteristiche

più importanti dell’aggregazione sono indicate di seguito.

1. L’oggetto aggregato potenzialmente può esistere senza i suoi oggetti costituenti. Per esem-

pio, un dipartimento continua a esistere anche nel caso in cui vengano licenziati tutti i suoi

dipendenti.

2. In qualunque momento, ciascun oggetto può essere costituente di più di un aggregato. Ancora

una volta un aggregato reale potrebbe anche non avvalersi di questa proprietà.

3. L’aggregazione tende a essere omogenea, il che significa che gli oggetti costituenti di un tipico

aggregato apparterranno alla stessa classe. Per esempio, i costituenti di un paragrafo sono le

frasi, mentre i costituenti di una foresta sono tutti gli alberi.

Vediamo ora la notazione UML per rappresentare il costrutto di aggregazione. La Figura 18 mostra

una cassa formata da bottiglie di birra (per un ulteriore esempio, vedi anche la relazione tra le classi

Griglia e Robot nella Figura 2). Su questa figura è possibile fare le seguenti osservazioni.

1. Un’associazione tra un aggregato e i suoi costituenti viene indicata da un piccolo rombo vuoto

sulla linea di associazione all’estremità dell’aggregato.

2. Le classi dell’aggregato ( Cassa ) e dei costituenti ( BottiglieBirra ) appaiono alle rispettive

estremità della linea di associazione. 28

Cassa

0..16

BottiglieBirra

Figura 18: Un oggetto aggregato ed i suoi costituenti.

3. Con l’aggregazione è necessario indicare la molteplicità a entrambe le estremità della linea

di associazione, perché non è mai possibile ipotizzarla, come invece avviene nel caso della

composizione. La molteplicità all’estremità dell’aggregato della Figura 18 è 1, il che significa

che una bottiglia di birra può appartenere ad una cassa, oppure non appartenere a nessuna

cassa. La molteplicità all’estremità del costituente è 0..16, il che significa che una cassa può

includere fino a 16 bottiglie di birra, o anche nessuno (nel caso di una cassa vuota).

Come avviene con la composizione, è possibile implementare l’aggregazione per mezzo di variabili.

Per la navigabilità dall’aggregato ai costituenti, una variabile nell’aggregato punterà ai costituenti.

Per esempio, la classe Cassa può contenere la seguente dichiarazione:

bottiglie: BottiglieBirra[16];

che nel nostro pseudo-linguaggio potrebbe denotare una variabile array di 16 componenti di tipo

BottiglieBirra .

7 Ereditarietà

Cosa fare nel caso fosse necessario scrivere il codice di una classe C e, allo stesso tempo, fosse

disponobile una classe D quasi identica a C tranne per alcuni attributi e operazioni aggiuntivi?

La semplice soluzione di duplicare tutti gli attributi e le operazioni di C per collocarli in D , oltre

a comportare del lavoro aggiuntivo, renderebbe seccante il lavoro di manutenzione. Una soluzione

migliore è far sı̀ che la classe D in qualche modo, chieda di utilizzare le operazioni della classe C :

questa soluzione prende il nome di ereditarietà.

Definizione 12 (Ereditarietà) L’ereditarietà (di una classe D da una classe C ) è il meccanismo

tramite il quale D ha implicitamente definito su di essa ciascuno degli attributi e delle operazioni

della classe C come se tali attributi e operazioni fossero stati definiti per D stessa. C viene

definita superclasse di D , mentre D è una sottoclasse di C .

In altre parole, attraverso l’ereditarietà gli oggetti della classe D possono utilizzare attributi

e operazioni che altrimenti sarebbero disponibili solo agli oggetti della classe C . L’ereditarietà

29

rappresenta un’altra delle caratteristiche principali grazie alle quali la tecnologia a oggetti si distacca

dagli approcci dei sistemi tradizionali; essa infatti permette effettivamente di costruire il software

in modo incrementale distinguendo due fasi fondamentali.

Fase 1: Innanzi tutto si costruiscono le classi destinate a far fronte alle situazioni più generali.

Fase 2: Poi, per affrontare i casi particolari, si aggiungono classi più specializzate che eredita-

no dalle classi generali. Una classe specializzata avrà pertanto il diritto di utilizzare tutte

le operazioni e gli attributi (operazioni e attributi sia di classe sia di istanza) della classe

originale.

Un esempio può essere di aiuto per illustrare questo principio. Supponiamo di avere, in un’applica-

zione aeronautica, una classe Velivolo che può aver definita un’operazione di istanza denominata

vira() e un attributo di istanza di nome rotta .

La classe Velivolo ha a che fare con tutta l’attività e le informazioni pertinenti a qualunque

tipo di apparecchio volante; tuttavia esistono tipi speciali di velivolo che svolgono speciali attività e

pertanto richiedono informazioni particolari. Per esempio, un aliante svolge attività speciali (come

sganciare il cavo di rimorchio) e potrebbe dover registrare speciali informazioni (per esempio, se è

attaccato a un cavo di rimorchio).

Ecco allora che possiamo definire un’altra classe, Aliante , che eredita da Velivolo e avrà

un’operazione di istanza chiamata sganciacavoRimorchio() e un attributo di istanza di nome

seCavoRimorchioAttaccato (di classe Boolean ). Questo ci dà la struttura mostrata nella

Figura 19, nella quale la freccia bianca denota l’ereditarietà.

Ora analizziamo i meccanismi dell’ereditarietà immaginando un po’ di codice a oggetti che crea

oggetti delle classi Velivolo e Aliante e in seguito gli invia dei messaggi. Il codice è seguito

da un’analisi delle quattro istruzioni contrassegnate da (1) a (4).

var v: Velivolo := Velivolo.New();

var a: Aliante := Aliante.New();

v.vira(nuovaRotta, out viraoK); (1)

a.sganciaCavoRimorchio(); (2)

a.vira(nuovaRotta, out viraoK); (3)

v.sganciaCavoRimorchio(); (4)

(1) L’oggetto cui punta v riceve il messaggio vira(nuovaRotta, out viraOK) , che fa sı̀ che

esso applichi l’operazione vira() (con i parametri opportuni). Dal momento che v è

un’istanza di Velivolo , esso utilizzerà semplicemente l’operazione vira() che è stata

definita nella classe Velivolo .

(2) L’oggetto cui punta a riceve il messaggio sganciaCavoRimorchio() , che fa si che esso appli-

chi l’operazione sganciaCavoRimorchio() (che non richiede argomenti). Dal momento che a

è un’istanza di Aliante , esso utilizzerà semplicemente l’operazione sganciaCavoRimorchio()

che è stata definita nella classe Aliante .

(3) L’oggetto cui punta a riceve il messaggio vira(nuovaRotta, out viraOK) , che fa sı̀ che

esso applichi l’operazione vira() (con gli argomenti opportuni). Senza ereditarietà, questo

messaggio provocherebbe un errore in fase di esecuzione (come vira(): operazione non

definita ) perché a è un’istanza di Aliante , che non ha alcuna operazione denominata

30 Un attributo della classe Angolo

Velivolo (probabilmente memorizzato come

-rotta : Angolo variabile privata) che rappresenta

la rotta del velivolo.

+vira() Un attributo della classe Boolean

Aliante (probabilmente memorizzato come

variabile privata) che rindica se il

-seCavoRimorchioAttaccato : Boolean cavo del rimorchio è attaccato

+sganciaCavoRimorchio() all’aliante.

Figura 19: Aliante è una sottoclasse che eredita dalla sua superclasse, Velivolo .

vira() . Tuttavia, dato che Velivolo è una superclasse di Aliante , anche l’oggetto

a può utilizzare a pieno titolo qualunque operazione di Velivolo (se Velivolo avesse

una superclasse OggettoVolante , a potrebbe anche utilizzare qualunque operazione di

questa classe). Pertanto la linea di codice contrassegnata con (3) funzionerà senza problemi

e l’operazione vira() , cosı̀ come è stata definita per Velivolo , verrà eseguita.

(4) Questo non funzionerà! v si riferisce a un’istanza di Velivolo , che non ha alcuna

operazione denominata sganciaCavoRimorchio() . L’ereditarietà non è di alcun aiuto in

questo caso, dal momento che Aliante è l’unica classe che definisce al suo interno l’operazione

sganciaCavoRimorchio() e Aliante è una sottoclasse di Velivolo . Dato che l’ereditarietà

non funziona in questa direzione, il sistema si blocca e segnala un errore di esecuzione. Se

ci pensiamo bene, comunque, ciò ha un senso, perché v potrebbe puntare a un oggetto che

rappresenta un grande aereo a reazione, per il quale sganciaCavoRimorchio() non avrebbe

significato.

Nella sezione 6 abbiamo visto la distinzione tra classe e oggetto; ora invece vedremo che esiste

anche una sottile distinzione tra oggetto e istanza. Sebbene finora abbiamo utilizzato i termini

oggetto e istanza quasi come sinonimi, vedremo che l’ereditarietà in un certo senso permette a

un singolo oggetto di essere contemporaneamente un’istanza di più di una classe. Ciò corrisponde

bene a quanto accade nel mondo reale. Se possedete un aliante, avete esattamente un oggetto

con un’identificazione (maniglia). Tuttavia questo aliante è (ovviamente) un esempio di aliante

e allo stesso tempo un esempio di velivolo. Dal punto di vista concettuale, quindi, l’oggetto che

rappresenta la cosa che possedete è un’istanza di Aliante e un’istanza di Velivolo .

Effettivamente l’esempio precedente costituisce un test rivelatore per un valido uso dell’eredita-

rietà, che prende il nome di test è un (is-a). Se potete dire: un D è un C , allora D quasi certamente

deve essere una sottoclasse di C . Quindi, dato che un aliante è un velivolo, la classe Aliante deve

essere una sottoclasse di Velivolo . Analizziamo ulteriormente la questione gettando uno sguardo

a ciò che avviene dietro le quinte dell’ereditarietà. L’oggetto cui fa riferimento a sarà rappresen-

tato in fase di esecuzione dall’unione di due parti: una parte saranno le operazioni e gli attributi di

istanza definiti per Aliante , l’altra le operazioni e gli attributi di istanza definiti per Velivolo .

Nella maggior parte dei linguaggi, la sottoclasse che eredita riceve tutto ciò che la superclasse ha da

offrire, senza scegliere cosa ereditare. Esistono tuttavia alcuni accorgimenti che permettono a una

31 Velivolo

VeicoloPasseggeri

VelivoloPasseggeri

Figura 20: Ereditarietà multipla: una sottoclasse con più superclassi.

sottoclasse di sovrascrivere (ossia modificare) le operazioni ereditate, come vedremo nella sezione 8.

Il codice per implementare effettivamente l’ereditarietà in un buon linguaggio a oggetti è semplice:

è sufficiente dichiarare la superclasse nella definizione di ogni sottoclasse che deve ereditare da essa.

Per esempio,

class Aliante inherits from Velivolo;

L’esempio presentato in questa sezione è di ereditarietà singola, il che significa che ogni classe

ha al massimo una superclasse diretta; vi sono anche casi di ereditarietà multipla, nel qual caso

ogni classe può avere un numero arbitrario di superclassi dirette. L’ereditarietà multipla trasforma

la struttura ad albero dell’ereditarietà singola in un reticolo di ereditarietà, come mostrato nella

Figura 20.

L’ereditarietà multipla introduce alcune difficoltà di progettazione, compresa la possibilità che una

sottoclasse erediti dai suoi progenitori operazioni o attributi in contrasto tra loro (le operazioni in

contrasto hanno lo stesso nome e la sottoclasse che eredita non riesce a decidere facilmente quale

ereditare). Difficoltà come il problema del contrasto di nomi hanno dato all’ereditarietà multipla

una cattiva reputazione. Nel corso degli anni sia la condanna sia la difesa dell’ereditarietà multipla

hanno raggiunto livelli parossistici. Comunque, dal momento che l’ereditarietà multipla può creare

strutture complesse e poco comprensibili, deve essere utilizzata sensatamente, ancora di più di

quanto avviene con l’ereditarietà singola. Attualmente due dei principali linguaggi a oggetti (C++

e Eiffel) permettono l’ereditarietà multipla, mentre gli altri due (Java e Smalltalk) non la accettano.

8 Polimorfismo

La parola polimorfismo deriva da due termini greci che significano rispettivamente molti e forme:

una cosa polimorfa pertanto ha la proprietà di assumere molte forme. I manuali di programmazione

a oggetti contengono due definizioni di polimorfismo, contrassegnate con (A) e (B) nella nota

sottostante. Si tratta di due definizioni valide, ed entrambe le proprietà del polimorfismo agiscono

di concerto per fornire una grande potenza alla tecnologia a oggetti. Più avanti in questa sezione

analizzeremo in modo più approfondito queste due definizioni.

Definizione 13 (Polimorfismo) 32


PAGINE

43

PESO

266.00 KB

AUTORE

Atreyu

PUBBLICATO

+1 anno fa


DESCRIZIONE DISPENSA

Il codice che viene presentato è una parte di una semplicissima applicazione a oggetti che visualizza una sorta di robot in miniatura che si sposta su una griglia sullo schermo (il genere di entità che è possibile vedere in un videogame). Sebbene l’orientamento agli oggetti non sia certamente un approccio limitato alle applicazioni grafiche, un’applicazione di questo tipo fornisce un eccellente esempio operativo. Vengono trattati i seguenti argomenti: incapsulamento, occultamento delle informazioni e dell’implementazione, conservazione dello stato, identità degli oggetti.


DETTAGLI
Corso di laurea: Corso di laurea in ingegneria informatica e automatica
SSD:
Università: L'Aquila - Univaq
A.A.: 2011-2012

I contenuti di questa pagina costituiscono rielaborazioni personali del Publisher Atreyu di informazioni apprese con la frequenza delle lezioni di Programmazione a oggetti e studio autonomo di eventuali libri di riferimento in preparazione dell'esame finale o della tesi. Non devono intendersi come materiale ufficiale dell'università L'Aquila - Univaq o del prof Di Stefano Gabriele.

Acquista con carta o conto PayPal

Scarica il file tutte le volte che vuoi

Paga con un conto PayPal per usufruire della garanzia Soddisfatto o rimborsato

Recensioni
Ti è piaciuto questo appunto? Valutalo!

Altri appunti di Programmazione a oggetti

Gestione File in C++
Dispensa
Standard Template Library
Dispensa
Ethernet - Le diverse famiglie di reti Ethernet
Dispensa
Reti Wireless
Dispensa