Facade 73
10. Facade
(GoF pag. 185)
10.1.
Descrizione
Fornisce una interfaccia unificata per un insieme di interfacce di un
sottosistema, rendendo più facile l’uso di quest’ultimo.
10.2.
Esempio
Un applicativo Java abilitato ad accettare dati forniti tramite l’interfaccia
di consola, deve gestire l’interazione di un insieme di classi di
Input/Output e di conversione di tipi di dati. Per esempio, se si vuole
leggere un numero si richiedono le seguenti istruzioni:
//Crea una istanza di un oggetto lettore
BufferedReader br = new BufferedReader( new
InputStreamReader( System.in ) );
String aString ="";
// La lettura può generare una eccezione
try {
aString = br.readLine();
} catch( IOException e ) { }
Il numero così letto è, in realtà, una stringa. Se dopo si vuole utilizzare
come dato numerico, ad esempio, dentro un oggetto Integer, la
stringa letta dovrà interagire con questa classe:
// Si converte il valore letto a int
Integer anInteger = new Integer( aString );
Queste operazioni sono abbastanza comuni nelle applicazioni. Si deve
notare, però, che ogni volta che si vuole inserire dati di tipo numerico il
programmatore deve interagire con le seguenti classi:
-
BufferedReader
InputStreamReader
System
IOException
String, Integer, Double, Float, ecc.
Sarebbe auspicabile una forma più facile di interagire con loro.
10.3.
Descrizione della soluzione offerta dal pattern
Il “Facade” pattern suggerisce la creazione di un oggetto che presentia
un’interfaccia semplificata al cliente, ma in grado di gestire tutta la
complessità delle interazioni tra gli oggetti delle diverse classi per
compiere l’obbiettivo desiderato.
Facade 74
10.4.
Struttura del pattern
<<stereotype>>
Facade
10.5.
Applicazione del pattern
Schema del modello
ConsoleReader
Integer
BufferedReader
InputStreamReader
Double
Partecipanti
Œ
Facade: classe ConsoleReader.
-
Œ
Ha conoscenza delle funzionalità di ogni classe del
sottosistema.
Delega agli appropriati oggetti del sottosistema ogni richiesta
pervenuta dall’esterno.
Subsystem classes: classi BufferedReader, InputStreamReader, Doble e
Integer, tra altre.
-
Implementano le funzionalità del sottosistema
Gestiscono le attività assegnate dal Facade.
Non hanno riferimenti verso il Facade.
Facade 75
Descrizione del codice
Si implementa la classe ConsoleReader, che offre i metodi per
l’inserimento dati tramite consola. I metodi restituiscono oggetti String,
Integer o Double (tutti e tre appartenenti al pakage java.lang),
secondo la necessità del chiamante.
Nel caso che il dato letto non sia di un formato valido, ad esempio, se
l’utente fornisce una lettera quando si aspetta come input un valore
intero, si ha deciso di fornire due diversi tipi di comportamento: lanciare
un’eccezione ConsoleReaderException (modo sicuro) o generare
l’oggetto con valore nullo, vale dire, zero (modo no sicuro). L’utente
dovrà decidere quale strategia adoperare, al momento d’istanziare il
ConsolerReader, secondo le proprie esigenze.
L’utilizzo di questo oggetto di lettura a interfaccia di lettura sarà ad
esempio:
//La variabile statica SECURE_MODE_OFF indica che l’oggetto lettore
//trascurerà gli errori.
ConsoleReader rdr = new ConsoleReader( ConsoleReader.SECURE_MODE_OFF );
// Legge una stringa
String text = reader.readString();
// Legge un numero e restituisce un oggetto di tipo Integer:
Integer numberInt = reader.readInteger();
// Legge un numero e restituisce un oggetto di tipo Double:
Double numberDouble = reader.readDouble();
Il codice del ConsoleReader, è il seguente:
import java.io.*;
public class ConsoleReader {
public static final boolean SECURE_MODE_ON = true;
public static final boolean SECURE_MODE_OFF = false;
BufferedReader br;
boolean secureMode;
public ConsoleReader() {
this( SECURE_MODE_ON );
}
public ConsoleReader(boolean secMod ) {
br = new BufferedReader( new InputStreamReader( System.in ) );
secureMode = secMod;
}
public String readString() {
try {
return br.readLine();
} catch (Exception e) {
if ( secureMode )
throw new ConsoleReaderException( e );
return "";
}
}
public Integer readInteger() {
Integer theInteger = null ;
try {
theInteger = new Integer( br.readLine() );
} catch (Exception e) {
if ( secureMode )
Facade 76
throw new ConsoleReaderException( e );
theInteger = new Integer( 0 );
}
return theInteger;
}
public Double readDouble() {
Double theDouble = null ;
try {
theDouble = new Double( br.readLine() );
} catch (Exception e) {
if ( secureMode )
throw new ConsoleReaderException( e );
theDouble = new Double( 0 );
}
return theDouble;
}
}
La classe ConsoleReaderException estende RuntimeException,
e si presenta di seguito:
public class ConsoleReaderException extends RuntimeException {
public ConsoleReaderException( Exception e ) {
super( e.toString() );
}
}
Adesso ripresenta il codice del programma di prova, che nella prima
parte lavora col lettore in modo insicuro (errori non noti), e poi con lo
stesso in modo sicuro. Si osservi quanto si sia semplificato l’inserimento
di dati, in relazione con i tradizionali meccanismi di input:
public class FacadeExample {
public static void main( String[] arg ) {
ConsoleReader reader = new ConsoleReader(
ConsoleReader.SECURE_MODE_OFF );
System.out.println( "This is the ’Insecure mode’: " +
"the system can’t detect invalid inputs." );
System.out.println( "Enter a string : ");
String text = reader.readString();
System.out.println( "You wrote: " + text );
System.out.println( "Enter an Integer : ");
Integer oInteger = reader.readInteger();
System.out.println( "You wrote: " + oInteger );
System.out.println( "Enter a Double : ");
Double oDouble = reader.readDouble();
System.out.println( "You wrote: " + oDouble );
System.out.println( "This is the ’Secure mode’: " +
"the system raises an exception when it " +
"detects invalid inputs." );
reader = new ConsoleReader( );
System.out.println( "Enter a string : ");
text = reader.readString();
System.out.println( "You wrote: " + text );
System.out.println( "Enter an Integer : ");
oInteger = reader.readInteger();
System.out.println( "You wrote: " + oInteger );
System.out.println( "Enter a Double : ");
oDouble = reader.readDouble();
System.out.println( "You wrote: " + oDouble );
}
}
Facade 77
Osservazioni sull’esempio
La gestione delle eccezioni si presenta solo per completezza della
progettazione della classe.
Si possono aggiungere metodi per restituire tipi primitivi (int, double,
ecc.) e altri oggetti (Byte, Float, ecc.)
Esecuzione dell’esempio
Si trascrive un dialogo possibile nell’esecuzione del codice dell’esempio.
Tanto nel modo insicuro come nel sicuro, si fornisce un dato non valido.
C:\ Design Patterns\Structural\Facade>java FacadeExample
This is the ’Insecure mode’: the system can’t detect invalid inputs.
Enter a string :
this is a test
You wrote: this is a test
Enter an Integer :
x
You wrote: 0
Enter a Double :
3
You wrote: 3.0
This is the ’Secure mode’: the system raises an exception when it
detects invalid inputs.
Enter a string :
another test
You wrote: another test
Enter an Integer :
3
You wrote: 3
Enter a Double :
x
Exception in thread "main" ConsoleReaderException:
java.lang.NumberFormatException: x
at ConsoleReader.readDouble(ConsoleReader.java:47)
at FacadeExample.main(FacadeExample.java:32)
10.6.
Osservazioni sull’implementazione in Java
Non ci sono considerazioni particolari nell’implementazione Java.