Corso Di OOP In AmigaE (Parte 3)


OBJECT INHERITANCE

Una delle caratteristiche più interessanti della programmazione a oggetti è la possibilità di creare degli oggetti che si basano su oggetti creati precedentemente, per formarne altri più complessi. Questa caratteristica è chiamata "inheritance" (eredità) ed è uno strumento molto potente per il programmatore, ma anche, sotto molti aspetti, complesso da utilizzare al meglio.

Disegnando un oggetto che si intende poi rendere ereditabile in modo efficace è necessaria una lunga sessione di pianificazione dell’oggetto stesso: cosa si desidera rendere pubblico e cosa invece mantenere nascosto, tenendo presente che un oggetto "figlio" vedrà solo le parti pubbliche dell'oggetto originario.


Prima di continuare nella nostra discussione, sarà opportuno definire una certa terminologia:

CLASSE: è un oggetto completo di variabili interne e di procedure di manipolazione.

ROOTCLASS: è la classe di base sulla quale si appoggiano altri oggetti.

SUPERCLASS: è la classe precedente a quella attuale, diciamo il "padre" dell'oggetto corrente.

METODO: è una funzione di un oggetto.

COSTRUTTORE: è il metodo utilizzato per inizializzare un oggetto.

DISTRUTTORE: è il metodo utilizzato per eliminare un oggetto, liberando tutte le risorse allocate da questo.


In Amiga E, per scrivere un oggetto che ne eredita un altro, bisogna scrivere:
OBJECT nome_oggetto OF oggetto_padre
  definizione...
  definizione...
ENDOBJECT

Per il resto si procede esattamente come nella creazione di un singolo oggetto.


ATTENZIONE: durante il proseguimento di questo articolo, mostreremo delle sezioni di codice incomplete, a semplice scopo dimostrativo. Anche la sintassi di Amiga E nella creazione/descrizione di un oggetto non verrà completamente rispettata.
La scrittura di un oggetto ereditabile porta alla definizione di metodi che rendano accessibili anche risorse più "interne" e questo potrebbe risultare pericoloso se usati da programmatori poco esperti. Facciamo un esempio pratico:

Supponiamo di avere un oggetto "immagine" così definito:

OBJECT immagine
  PRIVATE
  cols:PTR TO cmap
  bmp :PTR TO bitmap
ENDOBEJCT

e che siano definiti questi metodi:

bitmaptoscreen(scr:PTR TO screen) - Usato per visualizzare la bitmap su di uno schermo.
colorstoscreen(scr:PTR TO screen) - Usato per settare la palette a quella dell'immagine.

Con questi metodi è possibile, effettivamente mostrare una immagine, e l'utilizzatore dell'oggetto non dovrà mai preoccuparsi di sapere dove effettivamente risiedono i puntatori alla bitmap ed alla colormap: questo è molto sicuro dal momento che le risorse allocate dall'oggetto saranno liberate dall'oggetto stesso: quindi non dovrebbero esserci problemi. Ma supponiamo che si desideri creare un oggetto chiamato "remapper" che si occupi di permettere il remap di un oggetto "immagine" con una palette differente da quella dell'oggetto stesso.

L'oggetto "remapper" deve essere in grado di accedere (e modificare) ai dati dell'oggetto "immagine": quindi dovremo definire questi due metodi (ancora nell'oggetto "immagine"):

bitmap() - Restituisce il puntatore alla bitmap
cols()   - Restituisce il puntatore alla colormap

E dovranno essere creati anche metodi che permettano la creazione e la distruzione di "parti" dell'oggetto:

alloc(width, height, depth) - Alloca una bitmap 
free()			    - Libera la bitmap

L'oggetto "remapper" dovrà così essere costruito:

OBJECT remapper OF immagine
ENDOBJECT

remap(pal:PTR TO cmap) IS self.makeremap(SUPER self.bitmap(), SUPER self.cols(), pal)

Il metodo remap() che ho appena descritto ha delle peculiarità interessanti delle quali è meglio parlare subito. Innanzitutto, notate la parola chiave SUPER che permette di chiamare un metodo di un oggetto precedente al corrente. Questo è necessario, ad esempio nel nostro caso, poichè l'oggetto "remapper", ereditando completamente "immagine" ne possiede anche i metodi "bitmap()" e "cols()", che però, nell'oggetto in questione, sono due puntatori inutilizzati: con il metodo SUPER verrà invocato il metodo dell'oggetto precedente, in modo da poter accedere effettivamente ai dati desiderati (nel nostro caso, otterremo i puntatori alla bitmap e alla palette di "immagine").


OBJECT REDEFINITION OF METHODS

Un'altra caratteristica degli oggetti che ne ereditano degli altri è la loro possibilità di "ridefinire" dei metodi degli oggetti precedenti. La ridefinizione è uno strumento molto potente che permette a oggetti "simili" di comportarsi in modo completamente differente tra loro. Questa caratteristica, unita con il comando SUPER permette effettivamente di semplificare e migliorare notevolmente il comportamento di oggetti precedenti... o semplicemente di migliorare il nostro codice.

Tornando al nostro esempio precedente, potevamo ridefinire i metodi bitmap() e cols() dell'oggetto "remapper" in questo modo:

bitmap() OF remapper IS SUPER self.bitmap() - Chiamiamo bitmap() di "immagine"
cols() OF remapper IS SUPER self.cols()  - Chiamiamo cols() di "immagine"

E in questo modo potevamo definire remap() in questo modo:

remap IS self.makeremap(self.bitmap(), self.cols(), pal)
Naturalmente, questo è un esempio abbastanza inutile e poco significativo, ma cercheremo di farne di più interessanti più avanti.

RIEPILOGO

Ripassiamo adesso quello che è stato esposto in questo articolo, perchè è molto importante per il corretto (ed efficace) utilizzo degli oggetti e della loro programmazione.

INHERITANCE (Ereditarietà): un oggetto può ereditarne un altro e sfruttare tutte le caratteristiche proprie dell'oggetto originale. In Amiga E la sintassi è la seguente:

OBJECT nuovo_oggetto OF vecchio_oggetto
  definizione del nuovo oggetto
ENDOBEJCT

METHODS REDEFINITIONS (ridefinizione dei metodi): un oggetto creato su di un altro, ne può ridefinire i metodi e i comportamenti in maniera del tutto trasparente.

La sintassi in Amiga E è la seguente:

PROC nome_metodo() OF nuovo_oggetto
  nuovo codice
	....
  nuovo codice
ENDPROC

SUPER: è un comando particolarmente interessante perchè rende possibile invocare un metodo dell'oggetto originario dal nuovo oggetto. La sintassi in Amiga E è questa:

SUPER self.metodo()  -> chiama metodo() di vecchio_oggetto.

ESEMPIO Questo mese abbiamo deciso di separare il vero sorgente dimostrativo dal resto dell'articolo in modo da permettere una maggiore velocità di caricamento.

Per guardare l'esempio premete qui.

Buono studio del sorgente!



    Scritto da: Fabio Rotondo      e-mail: fsoft@intercom.it
                C.so Vercelli 9
                28100 Novara
                ITALY               tel:    (ITA) - (0)321 459676