Sto sempre reimplementando l'osservatore / codice soggetto in Java. C'è un'opzione migliore?

3

Scrivo sempre le interfacce observer / subject in un particolare progetto Java, ad esempio:

/**
 * Registers the receiver to the dispatcher. 
 * When data arrives that the receiver can process, 
 * it will be passed to the receiver.
 * ...
 */
void addReceiver(IDataReceiver receiver);

/**
 * @return
 *      True, if the receiver has been registered, false otherwise.
 * ...
 */
boolean hasReceiver(IDataReceiver receiver);

...

Quando una classe implementa queste interfacce, devo implementare il codice, testarlo, eseguirne il debug, ecc. Sembra così inutile continuare a reimplementare una logica simile. C'è un'opzione qualsiasi migliore in Java?

    
posta Dylan Knowles 13.08.2014 - 00:45
fonte

1 risposta

3

Ci sono alcune astrazioni nell'API Java per Observer Pattern . Ad esempio java.beans.PropertyChangeListener . In combinazione con java.beans.PropertyChangeSupport è possibile astrarre un po 'il tuo problema.

Questo esempio è di Oracle Doc of Java:

public class MyBean {
 private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);

 public void addPropertyChangeListener(PropertyChangeListener listener) {
     this.pcs.addPropertyChangeListener(listener);
 }

 public void removePropertyChangeListener(PropertyChangeListener listener) {
     this.pcs.removePropertyChangeListener(listener);
 }

 private String value;

 public String getValue() {
     return this.value;
 }

 public void setValue(String newValue) {
     String oldValue = this.value;
     this.value = newValue;
     this.pcs.firePropertyChange("value", oldValue, newValue);
 }

 [...]
}

Con String in firePropertyChange puoi definire, quale proprietà è cambiata. Ovviamente, a causa dell'uso di String , il tipo di sicurezza viene perso.

Non ti libererai di questa parte prima di Java8:

 private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);

 public void addPropertyChangeListener(PropertyChangeListener listener) {
     this.pcs.addPropertyChangeListener(listener);
 }

 public void removePropertyChangeListener(PropertyChangeListener listener) {
     this.pcs.removePropertyChangeListener(listener);
 }

L'unica possibilità è di ereditarlo da una classe base astratta. Ma se lo fai, non puoi avere un'altra classe base.

Poiché Java8 è possibile definire i metodi predefiniti nelle interfacce. Puoi definire un'interfaccia del genere:

public interface Listenable {
    PropertyChangeSupport getPropertyChangeSupport ();

    default void addPropertyChangeListener(PropertyChangeListener listener) {
        getPropertyChangeSupport ().addPropertyChangeListener(listener);
    }

    default void removePropertyChangeListener(PropertyChangeListener listener) {
        getPropertyChangeSupport ().removePropertyChangeListener(listener);
    }
}

È necessario getter , perché le interfacce non possono avere membri direttamente.

Quindi dovrebbe essere possibile evitare un po 'più codice standard:

public class MyBean implements Listenable {
 private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
 @Override     
 public PropertyChangeSupport getPropertyChangeSupport () {
     return pcs;
 }

 private String value;

 public String getValue() {
     return this.value;
 }

 public void setValue(String newValue) {
     String oldValue = this.value;
     this.value = newValue;
     this.pcs.firePropertyChange("value", oldValue, newValue);
 }

 [...]
}

Bene, non vedo una possibilità in Java, di farlo completamente senza codice boilerplate.

    
risposta data 13.08.2014 - 17:28
fonte

Leggi altre domande sui tag