Introspezione:
l'interfaccia BeanInfo
Le
convenzioni di naming descritte nei paragrafi precedenti
permettono ai Tool grafici abilitati ai Beans di scoprire
i servizi di un Componente grazie alla reflection. Questo
processo automatico è certamente comodo, ma ha
il difetto di non offrire nessun tipo di controllo sul
numero e sul tipo di servizi da mostrare. In alcune
occasioni può essere necessario mascherare un
certo numero di servizi, specie quelli ereditati da
una superclasse.
I
Beans creati a partire dalla classe JComponent, ad esempio,
ereditano automaticamente più di dieci attributi
(dimensioni, colore, allineamento......) e ben dodici
tipi diversi di Evento (ComponentEvent, MouseEvent,
HierarcyEvent....). Un simile eccesso provoca di solito
disorientamento nell'utente; in questi casi è
preferibile fornire un elenco esplicito dei servizi
da associare al nostro Bean, in modo da ripulire gli
eccessi.
Per
raggiungere questo obiettivo, bisogna associare al Bean
una classe di supporto, che implementi l'interfaccia
BeanInfo. Una classe BeanInfo permette di fare un certo
numero di cose: esporre solamente i servizi che si desidera
rendere visibili, aggirare le convenzioni di naming
imposte dalle specifiche Java Beans, associare al Bean
un'icona e attribuire ai servizi nomi più descrittivi
di quelli rilevabili con il processo di analisi delle
firme dei metodi.
Creare
una classe BeanInfo
Per creare una classe BeanInfo bisogna anzitutto definire
una classe con lo stesso nome del Bean, a cui si deve
aggiungere il suffisso BeanInfo. Per semplificare il
lavoro si può estendere SimpleBeanInfo, una classe
che fornisce un'implementazione nulla di tutti i metodi
dell'interfaccia. In questo modo ci limiteremo a sovrascrivere
solamente i metodi che ci interessano, lasciando tutti
gli altri con l'impostazione di default.
Per
ridefinire il numero ed il tipo dei servizi Bean, dobbiamo
agire in modo appropriato a restituire le proprietà,
i metodi o gli Eventi che si desidera esporre. Opzionalmente,
possiamo associare un'icona al Bean, definendo il metodo
public java.awt.Image getIcon(int iconKind). Per finire,
possiamo specificare la classe del Bean e il suo Customizer,
qualora ne esista uno, con il metodo public BeanDescriptor
getBeanDescriptor().
La
classe BeanInfo così prodotta deve essere messa
nello stesso package che contiene il Bean. In assenza
di una classe BeanInfo, i servizi di un Bean vengono
trovati con la reflection.
Feature
Descriptors
Una
classe di tipo BeanInfo restituisce, tramite i seguenti
metodi, vettori di descriptors che contengono informazioni
relative ad ogni proprietà, metodo o Evento che
il progettista di un Bean desidera esporre:
PropertyDescriptor[]
getPropertyDescriptors();
MethodDescriptor[] getMethodDescriptors();
EventSetDescriptor[] getEventSetDescriptors();
Ogni
descriptor fornisce una precisa rappresentanzione di
una classe di servizi Bean. Il package java.bean implementa
le seguenti classi:
-
FeatureDescriptor: è la classe base per tutte
le altre classi descriptor, e definisce gli aspetti
comuni a tutta la famiglia.
- BeanDescriptor:
descrive il tipo e il nome della classe Bean associati,
oltre a fornire il Custimizer, se ne esiste uno.
- PropertyDescriptor:
descrive le proprietà del Bean.
- IndexedPropertyDescriptor:
è una sottoclasse di PropertyDescriptor, e
descrive le proprietà indicizzate.
- EventSetDescriptor:
descrive gli eventi che il Bean è in grado
di inviare.
- MethodDescriptor:
descrive i metodi del Bean.
- ParameterDescriptor:
descrive i parametri dei metodi.
Esempio
In
questo esempio vedremo un BeanInfo per il Bean PhotoAlbum,
che permette di nascondere una grossa quantità
di servizi Bean che per default vengono eredita dalla
superclasse JPanel:
package
com.mokabyte.mokabook.javaBeans.photoAlbum;
import
java.beans.*;
import com.mokabyte.mokabook.javaBeans.photoAlbum.*;
public
class PhotoAlbumBeanInfo extends SimpleBeanInfo {
private
static final Class beanClass = PhotoAlbum.class;
public PropertyDescriptor[]
getPropertyDescriptors() {
try
{
PropertyDescriptor
imageNumber =
new
PropertyDescriptor("imageNumber",
beanClass,
"getImageNumber",
null);
PropertyDescriptor
imageIndex =
new
PropertyDescriptor("imageIndex",
beanClass,
"getImageIndex",
null);
PropertyDescriptor
imageComment =
new
PropertyDescriptor("imageComment",
beanClass,
"getImageComment",
null);
imageIndex.setBound(true);
imageComment.setBound(true);
PropertyDescriptor
properties[] = {imageNumber, imageIndex,
imageComment};
return
properties;
}
catch
(IntrospectionException e) {
throw
new Error(e.toString());
}
}
public EventSetDescriptor[]
getEventSetDescriptors() {
try
{
EventSetDescriptor
changed =
new
EventSetDescriptor(beanClass,
"propertyChange",
PropertyChangeListener.class,
"propertyChange");
changed.setDisplayName("Property
Change");
EventSetDescriptor
events[] = {changed};
return
events;
}
catch
(IntrospectionException e) {
throw
new Error(e.toString());
}
}
public MethodDescriptor[] getMethodDescriptors()
{
try
{
MethodDescriptor
showNext = new MethodDescriptor(
beanClass.getMethod("showNext",null));
MethodDescriptor
methods[] = {showNext};
return
methods;
}
catch
(Exception e) {
throw
new Error(e.toString());
}
}
public java.awt.Image getIcon(int
iconKind){
if(iconKind==SimpleBeanInfo.ICON_COLOR_16x16)
return
loadImage("photoAlbumIcon16.gif");
else
return
loadImage("photoAlbumIcon32.gif");
}
}
La
classe viene definita come sottoclasse di SimpleBeanInfo,
in modo da rendere il processo di sviluppo più
rapido.
Il
primo metodo, getPropertyDescriptors, restituisce un
array con un tre PropertyDescriptor, uno per ciascuna
delle proprietà che si vuole rendere visibile.
Il costruttore di PropertyDescriptor richiede quattro
argomenti: il nome della proprietà, la classe
del Bean, il nome del metodo getter e quello del metodo
setter: quest'ultimo è posto a null, a significare
che le proprietà sono di tipo Read Only. Si noti,
in questo metodo e nei successivi, che la creazione
dei Descriptors deve essere definita all'interno di
un blocco try - catch, dal momento che può generare
IntrospectionException.
Il
secondo metodo, getEventSetDescriptors, restituisce
un vettore con un unico EventSetDescriptor. Quest'ultimo
viene inizializzato con quattro parametri: la classe
del Bean, il nome della proprietà, la classe
dell'ascoltatore e la firma del metodo che riceve l'evento.
Si noti la chiamata al metodo setDisplayName, che permette
di impostare un nome più leggibile di quello
che viene normalmente ottenuto dalle firme dei metodi.
Il
terzo metodo, getMethodDescriptors, restituisce un vettore
contenente un unico MethodDescriptor, che descrive il
metodo showNext. Il costruttore di MethodDescriptor
richiede come unico parametro un oggetto di classe Method,
che in questo esempio viene richiesto alla classe PhotoAlbum
ricorrendo alla reflection.
Infine
il metodo getIcon restituisce un'icona, che normalmente
viene associata al Bean all'interno di strumenti visuali.
Per
impacchettare il Bean PhotoAlbum con le icone e il BeanInfo,
si può seguire la procedura già descritta,
modificando la riga di comando dell'utility jar in modo
da includere le icone nell'archivio:
jar
cfm photoAlbum.jar photoAlbumManifest.tmp com\mokabyte\mokabook\javaBeans\photoAlbum\*.class
com\mokabyte\mokabook\javaBeans\photoAlbum\*.gif com\mokabyte\mokabook\javaBeans\photoAlbum\images\*.*
Conclusioni
Questo
mese abbiamo studiato come sia possibile ridurre l'interfaccia
di programmazione di un Bean, in modo da semplificarne
l'uso all'interno di strumenti di sviluppo visuali.
Chi dispone di strumenti di sviluppo come JBuilder,
un ambiente di sviluppo abilitato, potrà provare
ad integrare i bean di esempio (o altri di propria invenzione)
nella palette del tool. Il mese prossimo concluderemo
il panorama sui Java Beans, attraverso una panoramica
sui meccanismi più avanzati per la personalizzazione.
|