MokaByte Numero 27  -  Febbraio 1999
Pattern Factory
L'applicazione
  di 
Andrea Trentini
Un case study bell'e pronto in Java.
Vediamo cosa offre Java in tema di Observers...


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".


 

MokaByte Web  1999 - www.mokabyte.it

MokaByte ricerca nuovi collaboratori. 
Chi volesse mettersi in contatto con noi può farlo scrivendo a mokainfo@mokabyte.it