Il proxy pattern fornisce un "surrogato" (o segnaposto) per un altro oggetto di
cui si desidera controllare l'accesso. Pur trattandosi di un design pattern
molto semplice, ne esistono parecchie varianti ed il suo uso si è diffuso a
macchia d'olio in diversi contesti.
Come esempio, supponiamo di dover pilotare a distanza il robottino della NASA che esplorò
Marte, il Mars Path Finder. Data la distanza, non possiamo controllare in
real-time il robot, perché il segnale radio (che viaggia
alla velocità della luce) ci impiega parecchie ORE ad arrivare su Marte.
Consideriamo il seguente diagramma UML:
Figura 1: Un proxy per il Mars Path Finder
La classe astratta Robot modella il robot, dichiarando i metodi di acceso consentiti.
La classe MarsPathFinder implementa il robot vero e proprio, mentre la classe
Proxy implementa il proxy, facendo riferimento al vero robot.
Questo tipo di proxy si chiama Remote Proxy e serve appunto a modellare un
oggetto che non è direttamente accessibile. Nel nostro caso, il Proxy potrebbe
registrare tutti i comandi dati al robot, e simularli su un modello 3D del
pianeta. Gli addetti al controllo sarebbero quindi in grado di verificare la
correttezza delle operazioni.
Poi, ad intervalli prefissati il Proxy potrebbe mandare una sequenza di comandi
al vero robot. Quest'ultimo, una volta ricevuti tutti i comandi,
si occuperebbe di eseguirli in rapida successione, in modo
da evitare le latenze di comunicazione.
Si osservi come l'attributo realSubject sia dichiarato private all'interno del
Proxy, e la necessità del proxy di implementare gli stessi metodi del
robot concreto. In generale Proxy deve mantenere il comportamento (Behavior)
dell'oggetto astratto Robot, arricchendo la semantica dell'oggetto
concreto MarsPathFinder.
I remote proxy sono usati anche in altri contesti, per esempio
nell'implementazione dei protocolli distribuiti (CORBA, EJB in primis).
Per esempio possono farsi carico degli errori di comunicazione e risolverli a
basso livello.
Vi sono due altri tipi di proxy che meritano di essere studiati:
Virtual Proxy
Utile ad esempio nei database orientati agli oggetti (OODBMGR).
Security Proxy
Permette di dotare degli oggetti di uno strato che garantisca
il controllo degli accessi.
Virtual Proxy
Programmando ad oggetti è assai comodo poter usare un database orientato agli
oggetti, chiamato OODBMGR. Ma implementare un OODBMGR non è una cosa banale:
infatti esso deve avere caratteristiche sensibilmente diverse da un database
relazionale.
Supponiamo per esempio di aver memorizzato un albero binario da 23Gb in un database.
Quando carichiamo dal database la radice, possiamo navigare tutto l'albero
come vogliamo. Peccato che non abbiamo 23Gb di RAM e che vogliamo soltanto
estrarre un percorso di nodi di qualche Kb. Il database ad oggetti allora ci
viene in aiuto. Piuttosto che caricare tutto l'albero, esso carica il nodo padre
e invece di caricare ricorsivamente i due nodi figli, mette al loro posto due
virtual proxy.
I virtual proxy implementano l'interfaccia "Node":
Figura 2: Virtual Proxy Node
I virtual proxy contengono un riferimento all'oggetto sul database. Questo
riferimento può essere un codice hash univoco (chiamato
anche OID, Object IDentifer) e di solito occupa pochissimo spazio.
Supponiamo ora di volere il nodo sinistro del padre.
La sequenza di chiamata è la seguente:
Figura 3: Sequenza di chiamata dell'OODBMGR
La classe DBMGR_Proxy_Node si serve dei servizi interni al database
(contenute in DBMGR_Loader) per caricare l'oggetto, ed invocare su di esso
il metodo getLeft(). È compito del database creare un proxy virtuale: in
generale questa operazione è fatta all'atto della compilazione dello schema
delle classi rese persistenti.
Esistono anche database in grado di inserire
oggetti al "volo" creando gli opportuni proxy.
Scambiare un proxy con il vero oggetto
L'OODBMGR può decidere di sostituirci "sotto il naso" il proxy, caricando il
vero oggetto e rimpiazzandolo.
In alcuni linguaggi (come SmallTalk) questa
operazione è semplice da fare: c'è un metodo apposta (che si chiama
become:) che scambia i riferimenti a due oggetti, giocando con il
Garbage Collector.
In altri (come in Java e C++) è necessario giocare a bassissimo livello
con la gestione dei puntatori agli oggetti o introdurre uno strato di
indirettezza in più.
In alcuni database è possibile impostare anche la "profondità di prefetch"
degli oggetti. Con tale parametro è possibile decidere come il
database userà i proxy virtuali.
Per esempio supponiamo di avere l'oggetto A che ha un riferimento all'oggetto B
che ha un riferimento all'oggetto C. Supponiamo inoltre che C faccia riferimento
a D ed A.
Se impostiamo nello schema dell'OODMBR una profondità di prefetch pari a due,
e carichiamo A, il database caricherà in memoria solo A e B e NON C.
Se accederemo a C il DBMGR caricherà C e D, e imposterà il riferimento da
C ad A in modo corretto.
Security Proxy
La sicurezza è un aspetto importante dell'informatica, ma solo verso gli
anni 1994-95 si è iniziato a sentirla come una necessità anche presso le
applicazioni Web. La diffusione dell'e-commerce e del trading on line ha fatto
sì che fosse necessario
rafforzare il debole TCP/IP, non pensato per garantire privacy e sicurezza.
Può quindi succedere di trovarsi con dei sistemi legacy "ingenui" a cui sia
necessario aggiungere in fretta un modello forte di sicurezza.
Siccome il tempo è sempre poco, il proxy pattern può risultare rapido e
efficace per rispettare i requisiti funzionali.
Per esempio è possibile far si che un ACLManager si preoccupi di verificare le
credenziali del Client, con un codice del genere (esempio per codice Java):
String username="kevin";
String password="oppip";
Robot r;
r = ACLManager.getRobot(username, password);
r.moveForward();
Il Security Proxy può rafforzare i controlli, per esempio potrebbe impedire ad
un client certificato, ma senza diritti di supervisione,
di invocare il metodo stop().
Conclusioni
L'overhead dovuto all'introduzione del proxy è trascurabile, se si
tiene conto dei vantaggi che esso introduce. Inoltre un proxy può essere usato
al posto dell'oggetto reale, nel caso esso non sia ancora implementato in modo
completo o non sia disponibile: come vedremo un proxy può risultare utile anche
per effettuare test di unità e di non regressione (in tal caso si parla di mock
objects).
Informazioni sull'autore
Giovanni Giorgi, classe 1974. Dopo il diploma di liceo Classico, si è laureato in Informatica nel febbraio 2000, e attualmente lavora nel campo del software finanziario (trading on line, soluzioni web). Appassionato di linguaggi di programmazione, si interessa anche di politica e letteratura.
Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides,
"Design Patterns. Elements of Reusable Object-Oriented Software"
Ed. Addison Wesley 1994.
Spesso viene indicato come [GOF94] acronimo di "The Gang of Four".
http://www.amazon.com/exec/obidos/ISBN=0201633612
FastObject (conosciuto un tempo come Poet) è un database ad oggetti
per C++ e Java. Dalle buone performance, non fa rimpiangere i database
relazionali.
La ditta produttrice (POET) è europea, ed offre un buon supporto.
http://www.fastobjects.com/eu/