Proxy

Pattern descritto in Design Pattern.

Lo scopo del pattern Proxy (aka Surrogate) é quello di fornire un segnaposto per un altro oggetto per controllarne l'accesso.

Ci sono svariati utilizzi per il proxy:
  • Il proxy remoto offre una rappresentazione locale di un oggetto che in realtà risiede altrove. In Java un proxy di questo tipo lo si trova nell'implementazione del modello sottostante alla RMI (Remote Method Invocation) dove il proxy é chiamato stub e l'oggetto remoto a cui si riferisce skeleton.
  • Il proxy virtuale permette di ritardare la creazione di un oggetto costoso in termini di risorse.
  • Il proxy protettivo permette di diversificare l'accesso ad un oggetto a seconda delle diverse situazioni.
  • La smart reference sostituisce il semplice puntatore con una struttura che permette operazioni aggiuntive. Tipico esempio é lo smart pointer comunemente usato in C++.
Esempio

Nel nostro mondo fantasy é prevista l'esistenza di un oracolo a cui é possibile sottoporre delle domande che otterranno, dopo adeguata meditazione, una risposta.

L'interfaccia che descrive l'oracolo é molto semplice:

package gof.proxy;

public interface Oracle {
public String getAnswer(String question);
}

L'implementazione che diamo per l'oracolo reale é decisamente semplicistica, ma l'idea che c'é dietro é che si tratti di codice molto complesso che richiede grandi risorse e un notevole tempo per essere eseguito:

package gof.proxy;

public class RealOracle implements Oracle {
private String answer = "I don't know";

public RealOracle() {
try{Thread.sleep(500);} catch(InterruptedException ie) {}
System.out.println("[Oracle] I'm ready.");
}

public String getAnswer(String question) {
System.out.println("[Oracle] You asked me: " + question);
try{Thread.sleep(2000);} catch(InterruptedException ie) {}

return answer;
}
}

Per verificare il funzionamento del nostro oracolo scriviamo questa funzione:

private String direct(String question) {
System.out.println("My question is: " + question);

Oracle oracle = new RealOracle();
return oracle.getAnswer(question);
}

Che dovrebbe dare come output:

My question is: What is the sense of life?
[Oracle] I'm ready.
[Oracle] You asked me: What is the sense of life?
The oracle's answer is: I don't know

A parte la delusione per la risposta che otteniamo dall'oracolo, il fatto fastidioso é che restiamo appesi in attesa del risultato senza poter far altro.
Questo ci fa pensare di usare un proxy al nostro oracolo, che permetta al cliente di fare altro mentre l'oracolo pensa:

package gof.proxy;

public class OracleProxy implements Oracle {
private OracleThread oracle = null;

public String getAnswer(String question) {
if(oracle == null) {
oracle = new OracleThread(question);
oracle.start();
}

String answer = oracle.getAnswer();

if(answer == null) {
System.out.println("Oracle needs more time to answer.");
}
else {
try { oracle.join(); } catch (InterruptedException ex) {}
}
return answer;
}
}

Il nostro oracolo é stato messo in un altro thread, il proxy lo interroga e, se ha elaborato una risposta, la passa al chiamante, altrimenti ritorna un null dopo aver stampato un messaggio che avverte che l'oracolo é ancora perso nelle sue elucubrazioni.

Questo é il codice della classe d'appoggio per l'oracolo multithreading:

package gof.proxy;

public class OracleThread extends Thread {
private RealOracle oracle = new RealOracle();
private String answer = null;
private String question;

public OracleThread(String question) {
this.question = question;
}

public String getAnswer() {
return answer;
}

@Override
public void run() {
answer = oracle.getAnswer(question);
}
}

La funzione che ci permette di testare il proxy é questa:

private String proxy(String question) {
System.out.println("My question is: " + question);

Oracle oracle = new OracleProxy();
String answer = null;
do {
try { Thread.sleep(500); } catch(InterruptedException ie) {}
System.out.println("I have time to do other stuff...");
} while((answer = oracle.getAnswer(question)) == null );

return answer;
}

Che dà questo risultato:

My question is: What is the sense of life?
I have time to do other stuff...
[Oracle] I'm ready.
Oracle needs more time to answer.
[Oracle] You asked me: What is the sense of life?
I have time to do other stuff...
Oracle needs more time to answer.
I have time to do other stuff...
Oracle needs more time to answer.
I have time to do other stuff...
Oracle needs more time to answer.
I have time to do other stuff...
The oracle's answer is: I don't know

OK. Il risultato finale é lo stesso, però almeno il cliente dell'oracolo ha avuto la possibilità di impiegare più utilmente il suo tempo nell'attesa della risposta.

Nessun commento:

Posta un commento