Riprendiamo
l'esempio della puntata precedente (Observer/Observable) aggiungendo il
meccanismo del factory per creare automaticamente le istanze dei visualizzatori.
Il sorgente completo dell'esempio lo trovate qui.
Cosa realizziamo?
Cominciamo con
mostrare una schermata ottenuta compilando e lanciando l'esempio allegato:
Forse non noterete
molte differenze con l'esempio della puntata precedente... in realtà
sono molte e sostanziali.
L'altra volta
avevamo una situazione con un'istanza di Counter che veniva rappresentata
da alcune istanze di visualizzatori vari, il collegamento fra Observer
e Observable andava fatto A MANO (infatti era nella classe di inizializzazione
della GUI iniziale.
In questo esempio
aggiungiamo un concetto forte: ogni classe "visualizzabile" deve implementare
una interfaccia ben precisa (GuiGeneratorI) e quindi fornire un factory
method che istanzia un visualizzatore opportuno (un Frame) da usare
direttamente.
Il problema
importante da risolvere quando si creano interfacce utente è infatti
quello della associazione fra classe da rappresentare e classe di visualizzazione,
la domanda è: "data un'istanza di un oggetto da visualizzare, cosa
istanzio per rappresentarlo?"
Nell'esempio
dell'altra volta la conoscenza necessaria per la scelta era tutta nell'interfaccia
utente (la classe Gui), di solito non è bellissimo farlo così,
è molto meglio incapsulare questa conoscenza nella classe stessa
da rappresentare (o in una classe intermedia se volete essere puristi ;-)
che fornirà un metodo standard per creare istanze di visualizzazione.
Vediamo i
dettagli.
Ogni classe da
rappresentare deve implemetare GuiGeneratorI, che pubblica il metodo newVisualizer()
con parametro. Tale metodo verrà implementato opportunamente in
modo che ritorni un Frame (potrebbe essere un Panel) da usare direttamente,
GIA' COLLEGATO ALL'ISTANZA CHE DEVE RAPPRESENTARE, chi usa quel metodo
NON deve sapere niente di come i due (rappresentato e rappresentante) si
parlano.
import java.awt.*;
/**
Chi implementa questa
interfaccia saprà generare
un visualizzatore
di se stesso...
*/
public interface GuiGeneratorI
{
/**
le varie modalità di visualizzazione
*/
public final static
int
BARE=0,
ICON=1,
BIGICON=2,
GRAPHICAL=5,
FANCY=10,
DETAIL=100;
/**
Ritorna un visualizzatore già collegato come Observer
*/
public Frame newVisualizer(int
type);
}
L'unica scelta da
fare è la modalità di visualizzazione (fra BARE, ICON, BIGICON,
GRAPHICAL, FANCY, DETAIL) che ogni classe rappresentabile tratterà
a suo modo.
BTW: questa
scelta è simile a quella che trovate in una finestra di Windoze
(icone, icone piccole, dettagli, lista, etc.).
Per testare
il tutto basta istanziare un paio di rappresentabili (ne ho creato un altro:
CreatorBis) e provare a vedere come vengono rappresentati nelle varie modalità...
import java.awt.*;
public class Start
{
public static Frame[]
activateAll(GuiGeneratorI g)
{
Frame[] tmp=new Frame[6];
tmp[0]=g.newVisualizer(GuiGeneratorI.BARE);
tmp[0].setVisible(true);
System.out.println("bare");
tmp[1]=g.newVisualizer(GuiGeneratorI.ICON);
tmp[1].setVisible(true);
System.out.println("icon");
tmp[2]=g.newVisualizer(GuiGeneratorI.BIGICON);
tmp[2].setVisible(true);
System.out.println("bigicon");
tmp[3]=g.newVisualizer(GuiGeneratorI.GRAPHICAL);
tmp[3].setVisible(true);
System.out.println("graph");
tmp[4]=g.newVisualizer(GuiGeneratorI.FANCY);
tmp[4].setVisible(true);
System.out.println("fancy");
tmp[5]=g.newVisualizer(GuiGeneratorI.DETAIL);
tmp[5].setVisible(true);
System.out.println("detail");
return tmp;
}
public static void
main(String[] arg)
{
Frame[] f1b=activateAll(new CounterBis(400));
Frame[] f2b=activateAll(new CounterBis(600));
Frame[] f1=activateAll(new Counter(300));
Frame[] f2=activateAll(new Counter(500));
}
}
Come vedete vengono
istanziati due Counter e due CounterBis (la differenza tra i due è
solo nel fatto che uno è un Observable mentre l'altro no,
e quindi va monitorato in polling). Per ognuno di loro provo a istanziare
un rappresentante di ogni tipo, infatti avrò VENTIQUATTRO Frame
a video.
Come fa il tutto
a funzionare? E' semplice: ogni classe che implementa GuiGeneratorI deve
pubblicare il metodo newVisualizer(int type), cioè deve saper istanziare
un corretto rappresentante di se stessa... vediamo tale metodo di una delle
due:
public Frame newVisualizer(int
type)
{
Frame tmp=new Frame("Counter: Not Initialized...");
switch(type)
{
case GuiGeneratorI.BARE:
tmp=new Bare();
break;
case GuiGeneratorI.ICON:
tmp=new Bare();
break;
case GuiGeneratorI.BIGICON:
tmp=new Analog();
break;
case GuiGeneratorI.GRAPHICAL:
tmp=new Scroll();
break;
case GuiGeneratorI.FANCY:
tmp=new Future();
break;
case GuiGeneratorI.DETAIL:
tmp=new Future();
}
addObserver((Observer)tmp);
System.out.println(tmp);
return tmp;
}
A seconda della
modalità richiesta viene fornita un'istanza diversa, circa. Dico
"circa" perchè se notate bene alcune tipologie diverse ritornano
oggetti uguali, ciò vuol dire che questa classe visualizzabile NON
supporta TUTTE le possibili modalità.
Infatti se guardate
l'altra classe (CounterBis) vedrete che supporta solo la modalità
BARE, se gli si richiede una modalità diversa risponde "picche",
cioè istanzia un visualizzatore dummy.
Note
-
"BARE" vuol dire
"nuda", non ha nessuna accezione "mortuaria" ;-)
-
dummy è
un termine tecnico, indica un qualcosa che sta lì per riempire un
buco, ma che non ha nessuna funzionalità
Per provarlo
Compilate tutto
il codice e lanciate "java Start". |