Propriétés
Propriétés ► Les propriétés peuvent être visibles dans les environnements de scripts ► Les propriétés peuvent être accédées par programmation par les autres composants en invoquant les accesseurs (getter and setter methods) ► Feuille de propriétés fait partie du processus de personnalisation (customization) d’un composant ► En général, les propriétés sont persistentes
Méthodes d’accès ► Les propriétés sont toujours accédées par invocation de méthodes sur les instances qui les contiennent. ► Noms les méthodes d’accès peuvent avoir des noms arbitraires. Par convention PropertyType getFoo(); // simple getter void setFoo(PropertyType value); // simple setter même si dans un éditeur de scripts permet d’écrire quelque chose du genre “b.Label = foo” il y aura invocation de méthode sur l’objet cible
La classe Livre public class Livre { // titre : propriété en lecture seulement // titre : propriété en lecture seulement private String titre_; private String titre_; public Livre(String titre) { public Livre(String titre) { this.setTitre(titre); this.setTitre(titre); } public String getTitre() { public String getTitre() { return this.titre_; return this.titre_; } protected void setTitre(String titre) { protected void setTitre(String titre) { this.titre_ = titre; this.titre_ = titre; }}
Propriétés indexées ► Pour spécifier un indice qui identifie quelle valeur obtenir ► Les indices de propriétés doivent être de type int (Java). PropertyType getter(int index); // indexed getter PropertyType[] getter(); // array getter void setter(int index, PropertyType value); // indexed setter void setter(PropertyType values[]); // array setter ► peut déclencher une exception java.lang.ArrayIndexOutOfBoundsException
Exceptions et méthodes d’accès ► Les méthodes d’accès simples et indexées peuvent déclencher des exceptions.
Propriétés liées (bound) ► S’il existe un service de notification des changement d’une propriété bound ► PropertyChangeListener event listener pour informer des mises-à-jour des propriétés liées simples. public void addPropertyChangeListener (PropertyChangeListener x); public void removePropertyChangeListener (PropertyChangeListener x); invocation de la méthode suivante pour chacun des aListener.propertyChange(PropertyChangeEvent evt)
Propriétés liées (bound) ► La source de l’événement devrait déclencher l’événement après la mise-à-jour de son état interne. ► Classe utilitaire PropertyChangeSupport pour gérer la liste des PropertyChangeListeners pour déclencher les événements PropertyChange.
Propriété liée public class Livre { private Membre emprunteur_; private Membre emprunteur_; private PropertyChangeSupport private PropertyChangeSupport propertyChangeSupport_ = new PropertyChangeSupport(this); propertyChangeSupport_ = new PropertyChangeSupport(this); public Livre(String titre) { public Livre(String titre) { this.setTitre(titre); this.setTitre(titre); this.addPropertyChangeListener(LogLivre.getInstance()); this.addPropertyChangeListener(LogLivre.getInstance()); } public Membre getEmprunteur() { return this.emprunteur_; public Membre getEmprunteur() { return this.emprunteur_; } public void setEmprunteur(Membre emprunteur) { public void setEmprunteur(Membre emprunteur) { Membre oldValue = emprunteur_; Membre oldValue = emprunteur_; this.emprunteur_ = emprunteur; this.emprunteur_ = emprunteur; propertyChangeSupport_.firePropertyChange("emprunteur", propertyChangeSupport_.firePropertyChange("emprunteur", oldValue, emprunteur_); oldValue, emprunteur_); } public void addPropertyChangeListener(PropertyChangeListener listener){ public void addPropertyChangeListener(PropertyChangeListener listener){ propertyChangeSupport_.addPropertyChangeListener(listener); propertyChangeSupport_.addPropertyChangeListener(listener); } public void removeVetoableChangeListener(PropertyChangeListener listener){ public void removeVetoableChangeListener(PropertyChangeListener listener){ propertyChangeSupport_.removePropertyChangeListener(listener); propertyChangeSupport_.removePropertyChangeListener(listener); }}
Singleton LogLivre public class LogLivre implements PropertyChangeListener { static private LogLivre instance__ = null; static private LogLivre instance__ = null; synchronized static public LogLivre getInstance(){ synchronized static public LogLivre getInstance(){ if (instance__ == null){ instance__ = new LogLivre(); if (instance__ == null){ instance__ = new LogLivre(); } return instance__; return instance__; } private LogLivre() { private LogLivre() { } public void log(String msg){ System.out.println( msg ); public void log(String msg){ System.out.println( msg ); } public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) { log("événement : " + ((Livre) evt.getSource()).getTitre() log("événement : " + ((Livre) evt.getSource()).getTitre() + "propriété : " + evt.getPropertyName() + "propriété : " + evt.getPropertyName() + "ancienne valeur : " + evt.getOldValue() + "ancienne valeur : " + evt.getOldValue() + "nouvelle valeur : " + evt.getNewValue()); + "nouvelle valeur : " + evt.getNewValue()); }}
Propriétés contraintes ► Les propriétés sont dites contraintes lorsque d’autres beans peuvent souhaiter valider le changement rejeter un changement s’il est inapproprié. public PropertyType getFoo(); public void setFoo(PropertyType value) throws PropertyVetoException;
Propriétés contraintes ► Si le récepteur de l’événement ne souhaite pas que la modification soit faite il déclenche une exception PropertyVetoException ► la responsabilité de la source de capturer cette exception réétablir l’ancienne valeur rapporte le retour l’ancienne valeur via un nouvel événement VetoableChangeListener.vetoableChange. ► La source devrait déclencher ce type d’événement avant de mettre à jour son état.
Propriétés contraintes ► Il existe une classe utilitaire VetoableChangeSupport pour gérer la liste des VetoableChangeListener s pour déclencher les événements VetoableChange pour capturer les exceptions PropertyVetoException s et émettre les événements de réversion nécessaires.
Ecouter les propriétés à la fois liées et contraintes Si un bean supporte une propriété qui est à la fois liée et contrainte alors elle devrait déclencher un événement VetoableChangeListener.vetoableChange avant la mise-à-jour de la propriété un événement PropertyChangeListener.propertyChange après la mise-à-jour de la propriété.
Propriété contraintes public class Livre { private VetoableChangeSupport private VetoableChangeSupport vetoableChangeSupport_ = new VetoableChangeSupport(this); vetoableChangeSupport_ = new VetoableChangeSupport(this); public Livre(String titre) { public Livre(String titre) { this.setTitre(titre); this.setTitre(titre); this.addVetoableChangeListener(Finance.getInstance()); this.addVetoableChangeListener(Finance.getInstance()); this.addPropertyChangeListener(LogLivre.getInstance()); this.addPropertyChangeListener(LogLivre.getInstance()); } public void setEmprunteur(Membre emprunteur) throws PropertyVetoException { public void setEmprunteur(Membre emprunteur) throws PropertyVetoException { Membre oldValue = this.emprunteur_; Membre oldValue = this.emprunteur_; try { try { vetoableChangeSupport_.fireVetoableChange("emprunteur", oldValue, vetoableChangeSupport_.fireVetoableChange("emprunteur", oldValue, emprunteur_); emprunteur_); this.emprunteur_ = emprunteur; this.emprunteur_ = emprunteur; propertyChangeSupport_.firePropertyChange("emprunteur", oldValue, propertyChangeSupport_.firePropertyChange("emprunteur", oldValue, emprunteur_); emprunteur_); } catch (PropertyVetoException e) { } catch (PropertyVetoException e) { LogLivre.getInstance().log("emprunt refusé"); LogLivre.getInstance().log("emprunt refusé"); throw e; throw e; } } public void addVetoableChangeListener(VetoableChangeListener listener){ public void addVetoableChangeListener(VetoableChangeListener listener){ vetoableChangeSupport_.addVetoableChangeListener(listener); vetoableChangeSupport_.addVetoableChangeListener(listener); } public void removeVetoableChangeListener(VetoableChangeListener listener){ public void removeVetoableChangeListener(VetoableChangeListener listener){ vetoableChangeSupport_.removeVetoableChangeListener(listener); vetoableChangeSupport_.removeVetoableChangeListener(listener); }
Vérification des événements sans effet Si une méthode “setter” est invoquée avec un argument égal à la valeur courante de la propriété alors il est recommandé que le bean ne déclenche pas d’événement de type VetoableChangeListener.vetoableChange ou VetoableChangeListener.vetoableChange ouPropertyChangeListener.propertyChange.
Le singleton Finance public class Finance implements VetoableChangeListener { static private Finance instance__ = null; static private Finance instance__ = null; synchronized static public Finance getInstance(){ synchronized static public Finance getInstance(){ if (instance__ == null){ instance__ = new Finance(); } if (instance__ == null){ instance__ = new Finance(); } return instance__; return instance__; } private Finance() { private Finance() { } protected boolean isSolvable(Membre m){ return this.getSolde(m)>= 0; protected boolean isSolvable(Membre m){ return this.getSolde(m)>= 0; } protected int getSolde(Membre m) { return 0; protected int getSolde(Membre m) { return 0; } public void vetoableChange(PropertyChangeEvent evt) public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException { throws PropertyVetoException { if (evt.getNewValue() == null) return; if (evt.getNewValue() == null) return; if ( !isSolvable((Membre)evt.getNewValue())) { if ( !isSolvable((Membre)evt.getNewValue())) { throw new PropertyVetoException("emprunteur non solvable", evt); throw new PropertyVetoException("emprunteur non solvable", evt); } }}
Support optionnel pour les propriétés liées void addPropertyChangeListener (String propertyName, PropertyChangeListener listener); void removePropertyChangeListener (String propertyName, PropertyChangeListener listener); ► le bean doit aussi supporter le design pattern standard pour l’enregistrement des event listeners, qui ne prend qu’un seul argument. alternative void add Listener(PropertyChangeListener p); void remove Listener(PropertyChangeListener p);
Support optionel support pour les propriétés contraintes nommées void addVetoableChangeListener (String propertyName, VetoableChangeListener listener); void removeVetoableChangeListener (String propertyName, VetoableChangeListener listener); ► le bean doit aussi support le design pattern standard pour l’enregistrement des event listeners, qui ne prend qu’un seul argument. alternative void add Listener(VetoableChangeListener p); void remove Listener(VetoableChangeListener p);
Les classes de support en Java ► class java.beans.PropertyChangeEvent null peut être utilisé comme ancienne et/ou nouvelle valeur si la valeur réelle n’est pas connue. null peut être utilisé comme nom de la propriété modifiée pour indiquer qu’un ensemble arbitraire de propriétés ont été modifiées. ► interface java.beans.PropertyChangeListener ► class java.beans.PropertyChangeSupport Aucun événement n’est déclenché si les anciennes valeurs sont égales et non-nulles. ► class java.beans.PropertyVetoException ► interface java.beans.VetoableChangeListener ► class java.beans.VetoableChangeSupport