Programmazione ad oggetti - I
|
Domande
|
Premessa
Questo articolo si propone di illustrare i concetti fondamentali della
programmazione orientata agli oggetti, descrivendone in dettaglio il modo in
cui VB.NET la implementa. Si richiede una conoscenza base delle tecniche di
programmazione ed in particolar modo i linguaggi procedurali ed i costrutti
di base della sintassi del VB.NET
Introduzione
Programmare ad oggetti (OOP - Object Oriented Programming) significa
essenzialmente organizzare il codice secondo una struttura che permette di
individuare gruppi di istruzioni e aree di memoria, isolarli dal resto del
programma e concepirli come entita' indipendenti che interagiscono tra loro.
A tale scopo, in parte contribuisce il compilatore che deve essere progettato
per la gestione di oggetti, in parte il progettista di classi di oggetti ed in
parte il programmatore che deve essere consapelove del ruolo svolto dagli
oggetti, per utilizzarli al meglio.
E' utile ripercorrere sommariamente il cammino logico che porta ad orientare la
programmazione verso gli oggetti. Consideriamo il seguente esempio di codice
fittizio (pseudocodice):
Inizio Programma1
PulisciSchermo()
DisegnaLinea(A, B)
DisegnaLinea(B, C)
DisegnaLinea(C, D)
DisegnaLinea(D, A)
DisegnaLinea(V1, V2)
DisegnaLinea(V2, V3)
DisegnaLinea(V3, V1)
Fine Programma1
Ogni programma non e' altro che una sequenza di istruzioni, eventualmente
reiterate. Possiamo osservare che il gruppo di procedure marcate in
rosso disegnano un rettangolo, quelle marcate in blu disegnano un triangolo.
Ora, cosi' come abbiamo potuto evidenziare logicamente le istruzioni usando
colori diversi, allo stesso modo potremmo isolare tali istruzioni, inserendole
in due procedure diverse.
Inizio Procedura DisegnaRettangolo: parametri p1, p2, p3, p4
DisegnaLinea(p1, p2)
DisegnaLinea(p2, p3)
DisegnaLinea(p3, p4)
DisegnaLinea(p4, p1)
Fine Procedura DisegnaRettangolo
Inizio Procedura DisegnaTriangolo: parametri p1, p2, p3
DisegnaLinea(p1, p2)
DisegnaLinea(p2, p3)
DisegnaLinea(p3, p1)
Fine Procedura DisegnaTriangolo
Inizio Programma2
PulisciSchermo()
DisegnaRettangolo(A, B, C, D)
DisegnaTriangolo(v1, v2, v3)
Fine Programma2
Il programma precedente fa esattamente le stesse cose del primo. Qui, e' ancora
vero che abbiamo una sequenza di istruzioni, ma non vengono necessariamente
eseguite nell'ordine in cui compaiono. Le istruzioni che compaiono nelle
procedure vengono eseguite solo nel momento in cui tali procedure vengono
invocate, cioe' dopo PulisciSchermo().
Uno dei vantaggi di questo approccio, rispetto al precedente, e' quello che,
variando i parametri, possiamo disegnare quanti rettangoli o triangoli vogliamo
in qualsiasi punto del programma senza dover riscrivere ogni volta il gruppo di
istruzioni relative. Ci bastera' invocare le procedure gia' scritte, usando
parametri diversi. E, se volessimo cambiare l'algoritmo per il disegno, lo
doveremmo fare una sola volta, cioe' all'interno delle procedure.
Codice organizzato ad oggetti
Andando oltre, potremmo identificare le due figure geometriche con delle
entita' consapevoli del ruolo che svolgono: ad esempio, sappiano disegnarsi 'da
sole', senza doversi appoggiare a procedure sparse qua e la' nel programma.
Cioe':
Inizio Programma3
PulisciSchermo()
CreaOggetto Rettangolo R(A, B, C, D)
CreaOggetto Triangolo T(v1, v2, v3)
R.Disegnati()
T.Disegnati()
Fine Programma3
Nel programma3, R e T sono due oggetti. Con la parola oggetto intendiamo
identificare niente piu', niente meno che un oggetto reale o un concetto.
Lo possiamo considerare come una variabile particolare, detta variabile
oggetto. A differenza delle variabili semplici o quelle strutturate, essa e' in
grado di eseguire procedure o funzioni. La procedura del nostro esempio e'
Disegnati(). Notiamo che i due oggetti eseguono una procedura con lo stesso
nome, ma poiche' le strutture dei due oggetti sono separate, queste procedure
hanno implementazioni differenti in base ai contesti in cui sono definite,
cioe' il contesto dell'oggetto rettangolo ed quello del triangolo.
T.Disegnati() disegnera' un triangolo, R.Disegnati() un rettangolo. Vediamo
come:
Inizio Definizione Oggetto Rettangolo
Inizio Struttura Interna
variabili punto1, punto2, punto3, punto4
Fine Struttura Interna
Inizio Metodo Costruttore: parametri p1, p2, p3, p4
punto1 = p1
punto2 = p2
punto3 = p3
punto4 = p4
Fine Metodo Costruttore
Inizio Metodo Disegnati
DisegnaLinea(punto1, punto2)
DisegnaLinea(punto2, punto3)
DisegnaLinea(punto3, punto4)
DisegnaLinea(punto4, punto1)
Fine Metodo Disegnati
Fine Definizione Oggetto Rettangolo
Inizio Definizione Oggetto Triangolo
Inizio Struttura Interna
variabili punto1, punto2, punto3
Fine Struttura Interna
Inizio Metodo Costruttore: parametri p1, p2, p3
punto1 = p1
punto2 = p2
punto3 = p3
Fine Metodo Costruttore
Inizio Metodo Disegnati
DisegnaLinea(punto1, punto2)
DisegnaLinea(punto2, punto3)
DisegnaLinea(punto3, punto1)
Fine Metodo Disegnati
Fine Definizione Oggetto Triangolo
Inizio Programma3
PulisciSchermo()
CreaOggetto Rettangolo R(A, B, C, D)
CreaOggetto Triangolo T(v1, v2, v3)
R.Disegnati()
T.Disegnati()
Fine Programma3
Ci sono diversi nuovi concetti introdotti dall' esempio precedente.
Innanzitutto osserviamo che abbiamo modellato gli oggetti definendoli in due
sezioni distinte e separate dal resto del codice.
La Struttura Interna e' quell'insieme di variabili che caratterizzano
l'oggetto. Esse assumono valori che rispecchino lo 'stato' del particolare oggetto.
Il costruttore e' una procedura. Tutte le procedure e le funzioni
all'interno di un oggetto vengono dette metodi. Il costruttore e' un
metodo che viene invocato implicitamente quando si crea l'oggetto; nel nostro
caso esso non fa' altro che inizializzare la struttura interna dei nostri
oggetti in base alle caratteristiche da noi specificate durante l'istruzione di
creazione.
Quando creiamo un oggetto si dice che lo istanziamo, cioe' ne creiamo un'istanza
in base al modello che abbiamo definito. Tale modello, che abbiamo chiamato
'Definizione Oggetto', nella pratica viene detto 'Classe'.
Una classe accomuna tutti quegli oggetti che hanno le stesse proprieta'.
Ad esempio, la classe dei triangoli rettangoli contiene tutti quei triangoli
che hanno la proprieta' di avere un angolo retto. Analogamente si puo' dire per
la classe dei triangoli isosceli, etc.
Definire una classe per tali oggetti significa studiare ed implememtare una
struttura interna capace di simulare le loro proprieta' e comportamento. I
valori (stato) della struttura sono modificati nel corso del ciclo di vita
dell'oggetto, pensate ad un contatore che s'incrementa.
La modifica dello 'stato' interno di un oggetto si ottiene agendo sui metodi
implementati dalla classe.
Il nostro costruttore, ad esempio, ha fissato i valori della struttura
interna ad uno stato iniziale. Altri metodi potrebbero ingrandire o ridurre le
dimensioni dei triangoli o rettangoli, andando a modificare i valori dei punti
che costituiscono la struttura. Ricapitolando: la struttura modella l'oggetto,
i metodi ne definiscono il comportamento. Il comportamento rende vivo l'oggetto
poiche' altera, nel corso del programma, i valori della struttura, ne varia lo
stato.
Pensate, ad esempio, ad una 'lampadina' il cui stato puo' essere acceso o
spento. Avremmo due metodi 'accenditi()', 'spegniti()' che andranno a
modificare una variabile interna che tiene traccia del fatto che la lampadina
sara' disegnata accesa o spenta.
Ora, le classi Rettangolo e Triangolo da me presentate, non sono quanto di
meglio c'e' per spiegare come progettare una classe. Ma quello che volevo era
un semplice esempio per introdurre i vari concetti che ci permettano di
comprendere la programmazione ad oggetti, senza addentrarci nelle tecniche di
analisi e sintesi.
Sia l'oggetto Triangolo che quello Rettangolo, come abbiamo detto, hanno il
metodo Disegnati(); ma, come possiamo vedere, ciascuno lo implementa in maniera
differente. Non c'e' conflitto perche' le due procedure sono disponibili solo
all'interno dei rispettivi oggetti. Proprio per pensarli separati dal resto del
codice, vengono detti metodi, che, a differenza di procedure e funzioni, non
possono essere invocati senza riferirsi all'oggetto a cui appartengono.
Generalmente la sintassi e' quella di scrivere il nome dell'istanza
dell'oggetto che abbiamo creato, seguito dal punto, seguito, a sua volta, dal
nome del metodo che vogliamo invocare, es. 'variabile_oggetto.Metodo()'.
Ci tengo a precisare che la procedura Disegnati() non esiste all'interno del
programma, essa appartiene esclusivamente all'oggetto che la definisce.
Vediamo adesso un esempio piu' indicativo del ruolo svolto dagli oggetti
all'interno di un programma.
... definizione classi omessa...
Inizio Programma4
CreaOggetto Schermo
CreaOggetto Pittore
CreaOggetto Tavolozza
CreaOggetto Paessaggio
Schermo.Inizializzati()
Tavolozza.Inizializzati()
Pittore.SelezionaNuovaTela()
Pittore.Disegna(Paesaggio, Tavolozza, Schermo)
Fine Programma2
Omettiamo la definizione degli oggetti perche' esula da quanto ci siamo
proposti di illustrare. Inoltre e' bene notare che, in genere, il programmatore
difficilmente definira' nuovi oggetti, ma si limitera' ad usare quelli gia'
pronti, forniti dall'ambiente di sviluppo che utilizza.
In questo esempio vediamo come la programmazione supportata dagli oggetti ci
permetta di concepire la risoluzione del problema dal punto di vista di una
simulazione. Gli oggetti simulano oggetti reali e la logica di programmazione
e' quella a cui siamo piu' abituati nella vita. Come vedete, ci limitiamo a
richiamare dei metodi sui vari oggetti dicendo loro cosa fare ed essi lo fanno
per noi. Mettiamo inoltre gli oggetti in comunicazione gli uni con gli altri,
in modo che siano essi stessi a richiamare metodi su altri oggetti. Nel nostro
caso, passiamo degli oggetti all'oggetto pittore tramite la funzione disegna.
L'oggetto pittore interagendo con tali oggetti (chiamando i rispettivi metodi)
sara' in grado di riportare il paesaggio sullo schermo utilizzando
i colori nella tavolozza. E' come se interpellassimo un pittore, gli dessimo
dei colori, una tela e gli chiedessimo di dipengerci un paesaggio.
E' chiaro che qualcuno dovra' pur progettare gli oggetti. Questo spetta,
in genere, a chi implementa un sistema o libreria di classi, dove oltre che
creare i singoli modelli di oggetti, ne studia anche le interazioni possibili
e le sintetizza fornendo dei metodi adeguati.
E' chiaro che se dovessimo sviluppare un'applicazione abbastanza dedicata, lo
studio del sistema componenti (oggetti) spetterebbe a noi. Ed e' comunque una
buona prassi, prima di sviluppare del software, soffermarsi almeno qualche
istante e pensare cosa vogliamo fare e come vogliamo farlo. Se non altro per
non essere costretti a fermarci a meta' del progetto e capire che dobbiamo
ricominciare tutto da capo o magari mettere delle 'pezze' che da li' a poco
renderanno ingestibili futuri sviluppi del software.
Incapsulamento
La seguente figura riassume quello che abbiamo detto. (Non vi sembra?)
Questa e' la nostra rappresentazione ideale di un oggetto. Il guscio piu'
esterno nasconde quella che noi abbiamo indicato come struttura interna
(variabili). Vediamo che chi utilizza l'oggetto non sa nulla di come e' stato
strutturato, vede soltanto i metodi. E' bene, infatti, non far supposizioni sul
lavoro del progettista, in modo da non vincolarci e vincolare il progettista
stesso ad una particolare rappresentazione.
Nascondere la struttura in questo modo e' detto information hiding e lo
otteniamo mediante l'incapsulamento dei dati, come mostra la figura.
Lo strato esterno costituisce l'interfaccia tramite la quale interagiamo con
l'oggetto. Non a caso e' costituita dai metodi. Solo invocando i metodi
possiamo modificare lo stato di un oggetto. Non ci interessa come cio' sia
ottenuto, a noi interessano soltanto gli effetti prodotti.
Nella seconda parte vedremo come Visual Basic .Net si orienta agli oggetti ed
analizzeremo gli altri concetti fondamentali che completano il quadro dell'
OOP.
Ultimo aggiornamento 13/01/2004
|