Construire une multitude de beans: créer des composants JavaBeans réutilisables

Dans cette courte série, nous examinons le développement de composants logiciels JavaBeans. En fin de compte, la plupart des beans seront manipulés dans un environnement de développement de beans; cependant, nous ne nous intéressons ici qu'aux aspects au niveau de la source du cadre. Les avantages de développer des JavaBeans - c'est-à-dire de développer selon la spécification JavaBeans - sont multiples, parmi lesquels:

  • Les beans peuvent être facilement manipulés dans des environnements de développement visuel par des utilisateurs qui n'ont pas besoin d'être techniquement qualifiés dans le développement Java au niveau source.

  • En raison de l'interface standard, les beans sont facilement distribuables, ce qui permet aux composants tiers d'être plus facilement intégrés dans les efforts de développement.

  • Les développeurs peuvent facilement transférer le code qui a été développé pour un projet dans une bibliothèque réutilisable de composants, accessible lors des efforts de développement futurs.

L'œil de la tempête

dans le

première partie de cette série

, nous avons développé deux haricots simples: un bean d'alarme non visuel et un bean graphique flèche gauche / flèche droite. Les deux ont été augmentés avec visuel

personnalisateur

et

informations sur les haricots

Des classes. Dans les beans que nous couvrons ce mois-ci, nous ne fournirons pas de personnalisateurs; à la place, nous allons nous concentrer sur l'utilisation de beans et de composants existants pour créer des beans plus gros et meilleurs.

Conditions préalables

En tant que continuation d'une série en deux parties, je suppose que je me familiariserai avec les questions abordées dans la tranche précédente, y compris les articles et ressources supplémentaires.

Les haricots

Du début à la fin de cette série, nous développons les beans suivants:

AlarmBean Un bean non graphique qui déclenche un événement après un délai spécifié.
FlècheBean

Un bean graphique flèche gauche / flèche droite.

ProgressBean

Un bean d'affichage de progression graphique.

NumberFieldBean

Un TextFieldbean numérique graphique avec des boutons de rouleau. Ce bean utilise le bean ArrowBean.

FontChooserBean

Un bean de sélection de police graphique. Ce bean utilise le bean NumberFieldBean.

FontSelectorBean

Un bean de sélection de police graphique qui affiche la police actuelle et fournit des boutons OK / Annuler. Ce bean utilise le bean FontChooserBean.

FontDialogBean

Un bean de sélection de police graphique qui fait apparaître le sélecteur de police dans une boîte de dialogue séparée. Ce bean utilise le bean FontSelectorBean.

Nous avons discuté des haricots AlarmBeanet ArrowBeanen détail le mois dernier; dans cet épisode, nous discuterons des haricots restants avec différents niveaux de détail.

Vous vous demandez peut-être pourquoi nous construisons trois beans de police. Le but ultime est simplement de produire un bean de sélection de police qui fait apparaître une boîte de dialogue de police lorsque l'utilisateur clique sur un bouton. Cette tâche se divise très naturellement en trois beans que nous allons produire: le premier est l'interface utilisateur pour la sélection de police, le second ajoute des commandes de dialogue et un échantillon de police, et le troisième introduit un bouton pour faire apparaître la boîte de dialogue et contient les éléments de base code de gestion des dialogues.

Sans beans, nous aurions à développer ces éléments en tant que composants AWT spécialisés ou en tant que classe monolithique unique; en utilisant des beans, nous pouvons développer les trois parties en tant que beans indépendants et réutilisables à part entière.

Notre portée

Comme pour le premier opus de cette série, nous nous intéressons uniquement aux beanismes de ces classes et non aux écrous et boulons réels qui les font tiquer. En conséquence, nous discuterons des haricots sous forme de squelette, en mettant en évidence en rouge les fragments qui sont particulièrement pertinents et en laissant les autres détails à votre disposition pendant votre temps libre. Nous ne nous intéresserons pas non plus aux personnalisateurs, que nous avons abordés de manière suffisamment détaillée dans notre discussion des deux premiers grains.

Pour voir le travail forcé derrière les haricots, consultez le code source complet.

Construire le haricot ProgressBean

ProgressBean

est un simple bean d'affichage de progression. Il s'agit d'un composant AWT personnalisé qui affiche une valeur en pourcentage et une représentation graphique à barres de cette valeur, comme illustré dans la figure ci-dessous. Il expose deux propriétés: les valeurs de barre actuelles et maximales.

La valeur actuelle est exposée en tant que propriété observable . Les propriétés observables sont des propriétés dont les changements peuvent être observés. Les observateurs sont enregistrés auprès du bean de la même manière que les écouteurs d'événements, et ils sont notifiés chaque fois qu'une propriété change. Les propriétés individuelles d'un bean doivent être rendues explicitement observables par le bean; il n'est pas possible d'observer les modifications de n'importe quelle propriété d'un bean.

Ce bean est implémenté avec les deux classes suivantes:

  • ProgressBean - La classe principale de haricots

  • ProgressBeanBeanInfo - La classe d'information du bean

Classe ProgressBean

le

ProgressBean class est la classe principale du bean, un simple composant AWT personnalisé et un bean Java.

public class ProgressBean étend le composant ... 

This bean is a lightweight component, so we extend Component instead of Canvas, and provide an appropriate paint() method. The lightweight component framework is more efficient than the traditional custom-component framework, requiring fewer resources of the local windowing system. As a component, we automatically inherit the serializability mandated by JavaBeans, and we provide the default no-arg constructor.

public void setBarground (Color c) ... public Color getBarground () ... public synchronized void setMaximum (int m) ... public int getMaximum () ... 

Here, we expose the Color property barground (the color of the displayed bar) and the int property maximum (the maximum bar value).

public synchronized void setValue (int v) { if (value != v) { value = v; repaint (); fireValueChange (); } } public int getValue () ... 

The int property value is observable, which means that we must inform all interested listeners whenever its value changes. To this end, we call our fireValueChange() method to inform the listeners whenever setValue() is called.

protected PropertyChangeSupport listeners = new PropertyChangeSupport (this); public void addPropertyChangeListener (PropertyChangeListener l) { listeners.addPropertyChangeListener (l); } public void removePropertyChangeListener (PropertyChangeListener l) { listeners.removePropertyChangeListener (l); } 

Here, we maintain a list of objects that are registered to be notified whenever an observable property changes. We use the class PropertyChangeSupport from the java.beans package to maintain this list. The constructor for this class requires us to specify the bean that will be the origin of property change events; in this case, it is this, and the methods that it provides allow us to maintain the list.

By exposing the methods addPropertyChangeListener() and removePropertyChangeListener(), we automatically indicate that this bean has observable properties. We do not, however, indicate which properties are observable. That information must be appropriately documented with the bean.

protected Integer oValue = new Integer (valeur); protected void fireValueChange () {listeners.firePropertyChange ("value", oValue, oValue = new Integer (value)); }

Nous appelons cette méthode pour informer les écouteurs d'un changement dans notre propriété value ; nous utilisons la firePropertyChange()méthode de notre liste pour propager cette notification. Le premier paramètre est le nom de la propriété, qui doit correspondre au nom d'une propriété exposée; le deuxième paramètre est l'ancienne valeur de la propriété; et la troisième propriété est la nouvelle valeur. La PropertyChangeSupportclasse retourne sans rien faire si les anciennes et les nouvelles valeurs sont identiques.

Classe ProgressBeanBeanInfo

le

ProgressBeanBeanInfoclass décrit simplement le ProgressBeanbean, masquant toutes les informations héritées que nous souhaitons masquer.

Construire le bean NumberFieldBean

This bean implements a common user-interface component, the rollable number entry field -- a numeric text field that provides increment and decrement arrows, as shown in the figure below. This bean brings up an important JavaBeans concept:

programmatic manipulation of beans

.

Programmatic manipulation of beans refers to the mechanisms that JavaBeans provides for programmatically creating and accessing beans. Although it is possible to access beans using the standard Java object creation (new X ()) and type-casting mechanisms ((Y) x), it is recommended that you use the provided JavaBeans mechanisms to allow for future extension of the JavaBeans framework.

This bean is implemented with the following two classes:

  • NumberFieldBean -- The main bean class

  • NumberFieldBeanBeanInfo -- The bean information class

Class NumberFieldBean

The NumberFieldBean class, the main bean class, is an AWT container that adds three components: two ArrowBean beans and a TextField. Programmatic access to the ArrowBean class requires that we make use of the bean manipulation mechanisms I mentioned a moment ago.

The current numeric value is exposed as an observable property. Although it is a normal property that can be accessed and manipulated through the usual beans accessor methods, it is also observable, so listeners can register to be notified whenever its value changes. We do not fire an event when the user presses Return, although that would be an obvious extension to this class.

public class NumberFieldBean extends Container implements ActionListener ... 

We extend Container and implement ActionListener in order to receive events from the beans and AWT components that we use. Extending Container instead of the more traditional Panel means that this bean, like the ProgressBean bean is a lightweight component.

public NumberFieldBean () ... 

As a bean, we must provide a public no-arg constructor. Note that we should not provide other constructors for programmatic use; doing so would go against the JavaBeans access mechanism.

try { down = (ArrowBean) Beans.instantiate (getClass ().getClassLoader (), "org.merlin.beans.arrow.ArrowBean"); } catch (Exception ex) { ex.printStackTrace (); } 

Here, we create an ArrowBean using the programmatic beans instantiation mechanism. We don't use the standard Java new operator; instead, we use the instantiate() method of class Beans. We specify the ClassLoader to use for loading the bean class; in this case, we use our own ClassLoader and the fully qualified name of the bean class ("org.merlin.beans.arrow.ArrowBean"), and cast the resulting Object to the appropriate class.

Note that the instantiate() method may throw a variety of exceptions (for example, if the specified bean could not be located). We simply catch and display any such exceptions, which, by the way, should not occur if the bean is appropriately installed.

add ("East", (Component) Beans.getInstanceOf (down, Component.class)); 

Here, we cast the ArrowBean to a Component and add it as a normal Component. We don't use the standard (Component) type-casting mechanism, and we don't use the fact that our AlarmBean is a subclass of Component; instead, we use the getInstanceOf() method of class Beans. We specify the bean that we wish to cast and the Class object to which we wish to cast it (in this case, Component.class).

Although this approach makes little sense right now, future versions of JavaBeans will support beans composed of multiple class files, as well as beans that can expose different aspects of themselves as the different classes. For example, a bean could appear to subclass both Component and RemoteObject by providing two coupled classes: a Component and a RemoteObject. Using the JavaBeans type-casting mechanism, the appropriate bean object can be returned automatically, so beans can have apparent multiple-inheritance, although Java does not natively support this. For details, see the "Glasgow" JavaBeans specification. (A link to this spec is provided in the Resources section of this article.)

It is necessary for us to use these beans access mechanisms now, so we can transition our beans to future JavaBeans technologies without any problems.

down.setDirection (ArrowBean.LEFT); down.addActionListener (this); 

Here, we configure the ArrowBean using the setDirection() property accessor and the addActionListener() registration method. We can use these property accessors and listener registration methods directly on the bean we just created; it is only necessary to use the JavaBeans type-casting feature when we are accessing an aspect of a bean that is inherited from another class.

public synchronized void setValue (int v) { field.setText (String.valueOf (v)); fireValueChange (getValue ()); } public synchronized int getValue () ... 

Here, we expose the int property value, which is the value of this field. This property is observable, so we must notify listeners whenever it is changed. We do this by calling our fireValueChange() method.

public void setColumns (int c) ... public int getColumns () ... public synchronized void setMinimum (int m) ... public int getMinimum () ... public synchronized void setMaximum (int m) ... public int getMaximum () ... public synchronized void setStep (int s) ... public int getStep () ... 

Ici, nous exposons les colonnes deint propriétés , minimum , maximum et step , qui sont, respectivement, le nombre de colonnes affichées dans , les valeurs minimum et maximum que ce champ doit contenir, et la quantité par laquelle les flèches doivent modifier le valeur. Ces propriétés ne sont pas observables.TextField

Notez que nous utilisons la synchronisation pour assurer la sécurité des threads le cas échéant.

public synchronized void actionPerformed (ActionEvent e) {int value = getValue (); if (e.getSource () == down) {if (value> minimum) {value = (value - step> value)? minimum: pince (valeur - pas); setValue (valeur); }} ...