Riflessione (informatica)
In informatica, la riflessione o reflection è la capacità di un programma di eseguire elaborazioni che hanno per oggetto il programma stesso, e in particolare la struttura del suo codice sorgente.
Un programma Java in esecuzione, per esempio, può esaminare le classi da cui è costituito, i nomi e le firme dei loro metodi, e così via.
Il supporto per la riflessione costituisce una delle più notevoli innovazioni rispetto ad una tradizione di linguaggi (C, C++) in cui tutte le informazioni di tipo vengono consumate dal compilatore, al punto che il programma in esecuzione non ha neppure nozione di come la propria memoria sia suddivisa in variabili.
La riflessione in generale può anche consentire a un programma di modificare dinamicamente la propria struttura. Per esempio, è tecnicamente possibile costruire applicazioni che sono in grado di applicare a sé stesse pacchetti di aggiornamento durante l'esecuzione, sostituendo dinamicamente parti del proprio codice.
Storia
[modifica | modifica wikitesto]Il termine computational reflection (o semplicemente reflection) venne introdotto in informatica nel contesto della programmazione funzionale, e fu esteso ai linguaggi a oggetti verso la fine degli anni ottanta da alcuni ricercatori, tra cui spiccano Pattie Maes e Jacques Ferber. Il concetto di riflessione si applica piuttosto naturalmente al contesto object-oriented; così come gli oggetti tradizionali sono rappresentazioni di entità del mondo reale, essi possono essere a loro volta rappresentati da altri oggetti, detti meta-oggetti. Sulla base di questa idea di fondo, Maes, Ferber e altri definirono un modello teorico per la riflessione nei sistemi a oggetti che costituisce ancora oggi un punto di riferimento importante in quest'area, benché implementazioni dell'idea di reflection siano tipicamente più limitate e semplificate dal punto di vista semantico.
La torre riflessiva
[modifica | modifica wikitesto]Secondo l'approccio classico di Maes e Ferber, un sistema riflessivo è strutturato in due o più livelli, che costituiscono una torre riflessiva. Le entità (gli oggetti) del livello base realizzano le funzionalità fondamentali del sistema. Questo livello è quello "tradizionale", che riceve input dall'esterno (per esempio dall'utente), lo elabora, e produce output.
Le azioni delle entità situate nei livelli superiori (meta-oggetti), invece, non operano sui dati del sistema (p.es. l'input dell'utente), bensì sugli oggetti dei livelli inferiori. Essi osservano la struttura o il comportamento degli oggetti sottostanti e potenzialmente intervengono per modificarlo.
Riflessione strutturale e comportamentale
[modifica | modifica wikitesto]Nel contesto object-oriented si può distinguere fra due possibili tipi di riflessione:
- La riflessione strutturale consente ai metaoggetti di osservare la struttura dei livelli sottostanti, ovvero la loro classe. Si tratta del tipo di reflection offerto da Java. In un approccio completo alla riflessione strutturale, i metaoggetti dovrebbero avere la possibilità non solo di informarsi sulle caratteristiche delle classi del livello base (metodi, attributi, e così via) ma anche di modificare dinamicamente tale struttura.
- La riflessione comportamentale consente ai metaoggetti di osservare il comportamento dinamico dei livelli sottostanti; in altre parole, osservare quali metodi vengono attivati e, potenzialmente, intercettarne e annullarne o modificarne l'invocazione.
Reificazione e trasparenza
[modifica | modifica wikitesto]Altri due concetti chiave di questa struttura logica sono quello della reificazione e della trasparenza.
Per operare sui livelli sottostanti, ogni livello deve avere una propria rappresentazione interna delle informazioni significative a proposito di tali livelli (esattamente come un programma che deve fare operazioni su conti correnti bancari deve avere strutture dati interne che rappresentano tali conti correnti). Questa rappresentazione interna prende il nome di reificazione, e l'oggetto rappresentato da una reificazione è detto il suo referente. La reificazione è una rappresentazione causalmente connessa; ovvero, ogni modifica apportata a tale rappresentazione si riflette automaticamente e implicitamente in una corrispondente modifica del referente, e viceversa. Nel caso della riflessione strutturale, per esempio, la reificazione è costituita da metaclassi, ovvero oggetti (del metalivello) che rappresentano le classi del livello base.
Per trasparenza si intende invece che le entità di ogni livello devono essere "inconsapevoli" della presenza e dell'operato dei livelli superiori; questo principio consente una più chiara separazione dei compiti fra i diversi livelli della torre riflessiva.
Applicazioni
[modifica | modifica wikitesto]Come si è detto precedentemente, fra i moderni linguaggi a oggetti utilizzati comunemente nella pratica informatica, Java è quello che si avvicina maggiormente al modello di Maes e Ferber; la scelta del termine reflection nella terminologia ufficiale Java non è un caso. La riflessione in Java è strutturale; la classe "Class" è a tutti gli effetti una metaclasse nel senso citato sopra. Tuttavia, il modello di riflessione di Java non implica (sebbene neppure impedisca) una strutturazione di un sistema software riflessivo in livelli ben distinti di una torre riflessiva.
Le applicazioni dei concetti di riflessione e di metaclasse sono innumerevoli. Nel caso di Java, l'aspetto probabilmente più rilevante è la possibilità, per un programma, di caricare da file o da una connessione di rete una classe sconosciuta a tempo di compilazione e utilizzarla (quasi) come una qualsiasi altra classe, ovvero istanziarla e utilizzare i metodi delle istanze create. Ciò può essere molto utile, per esempio, se si vogliono realizzare meccanismi che consentono l'estensione di un sistema o di una applicazione con nuovi moduli e funzionalità successivamente al rilascio e all'installazione (vedi il concetto di plug-in).
Linguaggi con caratteristiche riflessive
[modifica | modifica wikitesto]Oltre a Java, altri linguaggi di programmazione forniscono meccanismi di tipo riflessivo (strutturale). L'approccio interpretato è certamente quello che si presta di più, in quanto il codice sorgente rimane disponibile durante l'esecuzione, rendendo più semplice l'implementazione di meccanismi che lo esaminano a runtime. Per ottenere analoghi risultati, i linguaggi compilati devono aggiungere esplicitamente metadati al prodotto della compilazione, e affidarsi a un particolare tipo di supporto a tempo di esecuzione capace di leggere e interpretare tali metadati.
Fra i linguaggi che presentano alcune caratteristiche riflessive possiamo citare i seguenti:
- Forth
- Java
- .NET e il Common Language Runtime
- Objective C
- Perl
- Python
- PHP, dalla versione 5
- Ruby
- Il sistema di riscrittura Maude
- Smalltalk
- Lua
Collegamenti esterni
[modifica | modifica wikitesto]- (EN) La riflessione nella programmazione logica, funzionale e object-oriented, su citeseer.ist.psu.edu.