Polimorfismo

In relazione al paradigma della programmazione orientata agli oggetti, in un precedente articolo abbiamo parlato della ereditarietà dicendo:

Con la tecnica della ereditarietà costruiamo una nuova classe derivata a partire da una classe base da cui verranno ereditate le proprietà e i metodi. La particolarità di questa tecnica è che, se ne dovessimo avere la necessità, potremmo modificare uno o più dei suoi metodi a nostro piacimento specializzando così gli oggetti che creeremo del tipo della nostra classe derivata.

La tecnica di riscrittura dei metodi ereditati prende il nome di override. I metodi, così riscritti, vengono definiti metodi polimorfi.

Il metodo che si intende riscrivere nella classe derivata, deve avere lo stesso nome e la stessa firma del metodo presente nella classe base.

Facciamo ora un esempio per capire un po’ meglio come stanno le cose.

Esempio di un metodo polimorfo

Supponiamo di avere la classe base “pianta” e di creare, a partire da questa, due classi derivate che ne ereditino sia le proprietà che i metodi. Chiameremo queste due classi derivate “albero” e “rampicante”.

Supponiamo ora che nella classe base sia stato dicharato il metodo “sviluppo_radice” che fungerà, come vedremo più avanti, da interfaccia.

A questo punto riscriveremo ad hoc nelle classi derivate il metodo “sviluppo_radice” in modo tale che la chiamata di questo metodo fatta su un oggetto del tipo “albero” si comporti diversamente dalla stessa chiamata fatta su un oggetto del tipo “rampicante”.

Il metodo “sviluppo_radice” sarà quindi un metodo polimorfo, e la sua riscrittura operata nelle due classi derivate sarà stata una operazione di override.

Vantaggi del polimorfismo

Come abbiamo avuto modo di vedere, il polimorfismo ci permette di avere una interfaccia unica verso tutti i possibili altri programmi (definiti programmi client) che useranno le nostre classi derivate.

In questo modo avremo due vantaggi:

  1. Sarà sempre possibile estendere le modalità di risposta ad una stessa chiamata al metodo interfaccia, includendo tutte le possibili dichiarazioni dei metodi polimorfi in una nostra specifica classe contenitore a cui dovranno fare riferimento i programmi client.
  2. Sarà sempre possibile migliorare le modalità di risposta modificando il solo codice contenuto nei metodi polimorfi senza che i programmi client debbano essere modificati.

Esempio di polimorfismo in FreeBASIC

Vediamo ora un piccolo esempio della applicazione del polimorfismo in FreeBASIC.

''Classe base "pianta"
type pianta
  public:
    dim as string nome
    declare constructor(byref _nome as string)
    declare function sviluppo_radice() as string
  protected:
    ''impedisce l'uso del costruttore di default
    declare constructor()
end type
constructor pianta(byref _nome as string)
  this.nome = _nome
end constructor
function pianta.sviluppo_radice() as string
  return "sono una generica radice di una pianta"
end function

''Classe derivata "albero"
type albero extends pianta
  public:
    declare constructor(byref _nome as string)
    declare function sviluppo_radice() as string
end type
constructor albero(byref _nome as string)
  base(_nome)
end constructor
function albero.sviluppo_radice() as string
  return "sono la radice di un albero di"
end function

''Classe derivata "rampicante"
type rampicante extends pianta
  public:
    declare constructor(byref _nome as string)
    declare function sviluppo_radice() as string
end type
constructor rampicante(byref _nome as string)
  base(_nome)
end constructor
function rampicante.sviluppo_radice() as string
  return "sono la radice del rampicante"
end function

''Creazione di un oggetto di tipo "albero"
dim as albero mio_albero = albero("pioppo")

''Creazione di un oggetto di tipo "rampicante"
dim as rampicante mio_rampicante = rampicante("edera")

''Invoca il metodo polimorfo
print mio_albero.sviluppo_radice() & " " & mio_albero.nome
print mio_rampicante.sviluppo_radice() & " " & mio_rampicante.nome

sleep

Bene. Con questo articolo finiamo la sintetica panoramica sulla programmazione ad oggetti in FreeBASIC.

Ora non resta che sperimentare. 😉

FreeBASIC – libreria dinamica

A differenza della libreria statica che abbiamo visto nel precedente articolo, la libreria dinamica viene caricata soltanto quando il nostro programma è in esecuzione (runtime).

Altra importante caratteristica è che la libreria dinamica può essere condivisa (shared) tra più programmi in esecuzione.

Passiamo subito alla pratica e scriviamo quattro files.

Modulo libreria

''nome del file: funzioni_utili.bas

''funzione per il calcolo dell'area di un quadrato
public function area_quadrato(byval l as integer) _
       as integer
  return l * l
end function

Una volta salvato il file dobbiamo compilarlo specificando al compilatore FreeBASIC che vogliamo creare proprio una libreria dinamica:

fbc -dll [eventuale percorso]\funzioni_utili.bas

Se tutto è andato in porto troveremo nella stessa cartella contenente il file sorgente la nostra libreria.

Sui sistemi operativi Linux l’estensione del file sarà .so, mentre sul sistema operativo Windows l’estensione del file sarà .dll.

A questo punto il file della libreria dinamica va spostato in specifiche cartelle a seconda del sistema operativo:

  • su Linux: /usr/lib
  • su Windows: la cartella di sistema (p.e. C:\Windows\System32)

Interfaccia

Come abbiamo già fatto per rendere facilmente disponibile la libreria statica, anche per la libreria dinamica creiamo la nostra interfaccia (API) sfruttando un file di intestazione:

''nome file: funzioni_utili.bi

#inclib "funzioni_utili" ''include il modulo libreria

''dichiara la funzione
declare function area_quadrato(byval l as integer) _
        as integer

Test

Creiamo adesso due programmi sostanzialmente identici che, una volta mandati in esecuzione, condivideranno la medesima libreria dinamica.

Programma 1

''nome file: test1.bas

#include "funzioni_utili.bi" ''include l'interfaccia

dim a as integer

''stampa l'area del quadrato
do
  input "Test 1: Inserisci la lunghezza del lato: "; a
  print "L'area del quadrato vale: "; area_quadrato(a)
loop until a = 0

Programma 2

''nome file: test2.bas

#include "funzioni_utili.bi" ''include l'interfaccia

dim a as integer

''stampa l'area del quadrato
do
  input "Test 2: Inserisci la lunghezza del lato: "; a
  print "L'area del quadrato vale: "; area_quadrato(a)
loop until a = 0

Bene! Con questo articolo possiamo per ora ritenere la nostra conoscenza dell’uso delle librerie in FreeBASIC sufficiente.

Ricordiamoci però sempre di ben commentare le nostre librerie per aiutare chi volesse utilizzarle a capire velocemente come funzionano.

A rileggerci al prossimo articolo. 🙂

FreeBASIC – libreria statica

Nel precedente articolo abbiamo avuto modo di dare un primo sguardo alla teoria che riguarda le librerie.

Oggi passeremo alla pratica realizzando la nostra prima libreria statica.

Una libreria statica è un file contenente del codice compilato che svolge diversi compiti. Questo codice può essere utilizzato nel proprio progetto e quindi aggiunto staticamente durante la creazione del file eseguibile.

Facciamo quindi un semplice esempio creando una libreria che contiene un unico modulo con un’unica funzione:

Modulo libreria

''nome del file: libreria.bas

''funzione per il calcolo dell'area di un triangolo
public function area(byval b as integer, _
                     byval h as integer) as integer
  return b * h / 2
end function

A questo punto dobbiamo compilare il file specificando al compilatore FreeBASIC che vogliamo creare una libreria e non un eseguibile:

fbc -lib libreria.bas

Noteremo che nella stessa cartella del modulo compare ora il file liblibreria.a che è il file archivio contenente la nostra libreria.

Un archivio può contenere più moduli.

Interfaccia

Per facilitare l’uso della nostra libreria è bene creare una interfaccia (API) che permetta ad un qualsiasi programmatore interessato di accedere facilmente alle varie funzioni messe a disposizione.

In FreeBASIC per creare una interfaccia, non facciamo altro che scrivere un file di intestazione.

''nome del file: libreria.bi

#inclib "libreria" ''include il modulo libreria

declare function area(byval b as integer, _
                      byval h as integer) as integer

Programma

Immaginiamo ora di essere un programmatore che voglia utilizzare la funzione area della nostra libreria.

Scriviamo quindi questo semplice programma:

''nome file: test.bas

#include "libreria.bi" ''include il file di intestazione

'stampa l'area del triangolo
print "L'area del triangolo vale: "; area(4, 5)

A questo punto si compila il modulo del programma (test.bas) e si ottiene il file eseguibile che ingloba anche la libreria.

Nel prossimo articolo creeremo invece una libreria dinamica.