Approccio "object oriented" in F90: come fare

F90 non nasce come linguaggio object oriented, pur avendone alcune caratteristiche (moduli, tipi derivati, overload degli operatori e delle procedure), per cui ci limitiamo a descrivere come avvicinarsi ai concetti OO in F90 senza necessariamente pretendere di seguirli alla lettera. Partiamo dai concetti base.
Definizione della classe staz: il tipo derivato staz contiene tutte le variabili che servono a descrivere la classe.
MODULE staz_class
IMPLICIT NONE

TYPE staz
  INTEGER :: cod_staz
  REAL, POINTER :: obs(:)
...
END TYPE staz

CONTAINS
...
END MODULE staz_class
Dichiarazione di un oggetto della classe staz: dichiara una singola istanza o "incarnazione" dell'oggetto in questione.
USE staz_class

TYPE(staz) :: mia_staz
Metodo della classe staz: si tratta di una routine che agisce su un oggetto della classe stessa; l'oggetto sarà il primo parametro e sarà chiamato this, nei linguaggi OO questo è fatto implicitamente, in F90 dovremo farlo esplicitamente.
MODULE staz_class
...
CONTAINS

SUBROUTINE controlla_staz(this, ...)
TYPE(staz), INTENT(INOUT) :: this
...

END SUBROUTINE controlla_staz
...

END MODULE staz_class
Metodi speciali della classe staz:
  • costruttore: esegue le operazioni preliminari su ogni nuova istanza della classe in modo che sia inizializzata in maniera pulita
  • distruttore: esegue le pulizie finali prima che l'istanza della classe cessi di essere utilizzata, es. deallocare memoria, chiudere file, ecc.
  • eventuale (ri)definizione (tramite INTERFACE) degli operatori intrinseci (aritmetici, logici, assegnazione) di F90 in modo che possano agire correttamente sulle istanze della classe
costruttore e distruttore possono anche essere vuoti ma è meglio comunque crearli.
MODULE staz_class
...

INTERFACE delete
  MODULE PROCEDURE staz_delete
END INTERFACE

INTERFACE OPERATOR(==)
  MODULE PROCEDURE staz_eq
END INTERFACE

CONTAINS

! Costruttore
FUNCTION staz_init(cod, ...) RESULT(this)
INTEGER, OPTIONAL :: cod
TYPE(staz) :: this

IF (PRESENT(cod)) THEN
  this%cod_staz = cod
ELSE
  this%cod_staz = 0
ENDIF
...

END FUNCTION staz_init

! Distruttore
SUBROUTINE staz_delete(this)
TYPE(staz), INTENT(INOUT) :: this
...

END SUBROUTINE staz_delete

! Operatore di uguaglianza
FUNCTION staz_eq(this, that) RESULT eq
TYPE(staz), INTENT(IN) :: this, that
LOGICAL :: eq
...

END FUNCTION staz_eq
Nei linguaggi OO, in genere, il costruttore e il distruttore sono chiamati automaticamente al momento dell'istanziazione dell'oggetto e al momento della sua caduta in disuso rispettivamente; in F90 ciò non è previsto, per cui l'utente deve farlo esplicitamente.
USE staz_class

TYPE(staz) :: mia_staz, gen_staz

mia_staz = staz_init(16134)
gen_staz = staz_init()
...

CALL delete(mia_staz)
CALL delete(gen_staz)
Definizione di variabili o costanti simboliche comuni a tutte le istanze della classe, dette anche variabili della classe in contrasto alle variabili dell'istanza che stanno dentro al TYPE.
MODULE staz_class
IMPLICIT NONE
...

INTEGER :: nval
REAL, PARAMETER :: pi=3.14159

TYPE staz
...
END TYPE staz

CONTAINS
...
END MODULE staz_class
Definizione di variabili e/o routine ad uso interno della classe, non utilizzabili o modificabili dall'utente. In F90 c'è la limitazione che singoli membri di un tipo derivato non possono essere resi PRIVATE, ma solo l'intero contentuto del tipo derivato stesso, per cui non può essere negato l'accesso in maniera selezionata a determinate variabili dell'istanza.
MODULE staz_class
IMPLICIT NONE

TYPE staz
...
END TYPE staz

INTEGER :: nc
REAL, PARAMETER :: vk=0.4

PRIVATE nc, vk, mi_vergogno_a_darla_in_giro

CONTAINS
...

SUBROUTINE mi_vergogno_a_darla_in_giro(...)
END SUBROUTINE mi_vergogno_a_darla_in_giro

END MODULE staz_class

È consigliabile definire il costruttore di una classe come FUNCTION nominata a partire dal nome del corrispondente tipo derivato, come nell'esempio sopra, in cui il tipo derivato/classe staz ha come costruttore la funzione staz_init. Questo perché, in Fortran 2003, sarà possibile fare l'overload del costruttore di default di un tipo derivato (cioè la funzione che porta il nome del tipo derivato stesso, staz() nel nostro caso) con una funzione definita dall'utente, e quindi in questa maniera risulterà più facile la migrazione futura a Fortran 2003.