Pascal/Concetti fondamentali

Wikibooks, manuali e libri di testo liberi.
Indice del libro

La programmazione orientata agli oggetti gira attorno a tre concetti fondamentali e di cruciale importanza. Questi concetti sono:

L'incapsulamento[modifica]

Per un buon approccio alla programmazione agli oggetti, la prima cosa che si deve fare è stabilire quali sono gli oggetti, cosa fanno, quali sono le loro caratteristiche e come interagiscono fra di loro.

Abbiamo detto che gli oggetti sono strutture che contengono campi e metodi. Per campi si intendono delle variabili (in senso molto lato) o, meglio, dei puntatori a zone di memoria. I metodi invece sono le procedure, le funzioni, i costruttori e i distruttori (concetti che vedremo più avanti) legati al particolare oggetto.

I metodi sono pertanto la parte attiva di un oggetto, che possono modificare le caratteristiche dello stesso, ovvero possono modificare i campi. Il procedimento con il quale si definiscono i metodi e i campi di un oggetto è chiamato incapsulamento. Per essere più precisi, a venire incapsulata è la classe, che sarebbe un "tipo", un "modello" di oggetto. Sebbene nella sintassi del Turbo Pascal venga utilizzata la parola chiave object per definire una classe, ciò non deve creare confusione.

Un oggetto per essere creato ha bisogno di essere istanziato da una classe; quindi per esempio avendo una classe Studente, da questa classe possono venire istanziati due oggetti-studente: Francesco e Giorgio.

Qui di seguito un esempio in codice per creare una classe:

TYPE TStudente = OBJECT
                    Nome,Cognome: String;
                    AnnoDiNascita: integer;
                    FUNCTION GetNome : String;
                    FUNCTION GetCognome : String;
                    FUNCTION GetAnnoNascita : integer;
                    PROCEDURE SetNome(nome:string);
                    PROCEDURE SetCognome(cognome:string);
                    PROCEDURE SetAnnoNascita(anno:integer); 
                 END;

In questo modo abbiamo definito la classe TStudente che contiene i campi Nome, Cognome e AnnoDiNascita, e i metodi per memorizzare i dati o per ottenerli dall'esterno.

L'ereditarietà[modifica]

La programmazione orientata agli oggetti utilizza per gli oggetti una gerarchia ben definita che si rivela molto utile e produttiva ai fini della scrittura del programma. Questa gerarchia si può rappresentare esattamente come un albero genealogico. I benefici portati da questa pratica sono vari.

Immaginiamo un programma che faccia largo uso delle finestre, quindi creiamo una classe che descrive i vari comportamenti di una finestra: la riduzione a icona, lo spostamento, l'ingrandimento ecc. Noi sappiamo dall'uso del computer che non c'è solamente un tipo di finestra: ci sono le MessageBox, le finestre di dialogo, le finestre di editing, ecc. quindi dovremmo creare una classe per ogni tipo di finestra e ridefinire i vari comportamenti per ogni tipo di finestra; ma se invece creaiamo una classe per ogni finestra che eredita da una classe principale di una finestra generica, eviteremo di definire i comportamenti principali per ogni finestra.

Quindi un oggetto2 che eredita da oggetto1 potrà usufruire di tutti i suoi campi e metodi.

Da qui appare chiaro il perché usare l'ereditarietà. Ecco un esempio in codice:

TYPE TBox1 = OBJECT
                Altezza, Lunghezza, Larghezza: integer;
                PROCEDURE Disegna;
                PROCEDURE Imposta(alt, lung, larg: integer);
             END;
     TBox2 = OBJECT (TBox1)   {TBox2 eredita TBox1}
                PROCEDURE ApriScatola;
                PROCEDURE ChiudiScatola;
             END;

Il polimorfismo[modifica]

Il polimorfismo è un concetto un po' più avanzato dei precedenti, ma comunque importantissimo. Polimorfismo vuol dire "molte forme", e diciamo subito che si riferisce a una procedura o una funzione. Facciamo subito un esempio: supponiamo di voler creare un programma per disegnare delle figure piane (triangoli, rettangoli, circonferenze ecc.). Per prima cosa creiamo un oggetto comune, per esempio figurabidimensionale, e supponiamo che contenga una procedura di nome Disegna. La dichiareremo virtual, ovvero non la definiamo nell'oggetto figurabidimensionale, ma la definiamo negli oggetti che ereditano questo. Comunque i metodi virtuali sono un concetto che esporremo più avanti.

Da questo oggetto erediatiamo altri oggetti di nome triangolo, rettangolo, circonferenza, e definiamo il metodo Disegna per ogni oggetto (che ovviamente avrà una diversa implementazione per ciascuno).

Adesso, supponiamo che una procedura più avanti si serva del metodo draw, ma non specifica quale oggetto stiamo utilizzando. Per esempio:

PROCEDURE UnaProcedura(figura:figurabidimensionale);
BEGIN
   ...
   ... 
   ...
   figura.Draw;
   ...
   ...
   ...
END;

In questo esempio il metodo Draw non sarà quello di figurabidimensionale, ma quello della classe figlia che eredita, anche perché in figurabidimensionale il metodo non è definito.

Questa pratica è chiamata polimorfismo.

Come creare l'istanza di un oggetto[modifica]

Quando definiamo un oggetto con la clausola TYPE in realtà l'oggetto non è stato creato, ma ne è stata definita la struttura, cioè la classe. Creare un'istanza di un oggetto vuol dire allocarlo nella memoria heap. La memoria heap è una memoria con una struttura ad alberi che, a differenza dello stack, vi si accede tramite i puntatori, ovvero variabili speciali che contengono un indirizzo di memoria. Quindi un oggetto creato, o meglio, allocato, ha sempre un puntatore per il riferimento.

Bene, nel Pascal, per motivi di compatibilità, è possibile riferirsi a un oggetto creando una variabile con la clausola VAR, ma comunque questo serve solo per lanciare l'oggetto principale del programma che una volta avviato, sarà meglio utilizzare i puntatori per riferirci a un oggetto. Quindi riprendendo l'esempio sopra, se voglio creare come primo passaggio del programma un oggetto TBox1, userò la seguente sintassi:

VAR Box1:TBox1;
BEGIN
   ...
   ...
   ...
END.

Per creare l'istanza di un oggetto utilizzando un puntatore, prima devo definire un puntatore all'oggetto-tipo:

PBox2: ^TBox2;

A questo punto possiamo utilizzare la nuova versione della procedura del Turbo Pascal new. La funzione primaria di questa procedura è quella di creare una variabile dinamica e consentire al puntatore P passato come parametro di puntare su di essa. Col Turbo Pascal 6.0 la procedura, sebbene ancora valida, è stata trasformata iìn funzione. Il suo compito è di rilasciare come valore un puntatore all'oggetto. La sua sintassi è questa:

P:= new(PBox2, Init);

dove P è un puntatore generico e Init è il costruttore della classe TBox2 (i costruttori saranno trattati più avanti).