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.