Quali precondizioni dovresti controllare mentre aggiungi / rimuovi un listener?

2

Immagina la seguente interfaccia:

 interface Service {
   addListener(Listener l)
   removeListener(Listener l)
 }

Devo controllare i valori nulli mentre aggiungo / rimuovi? È una buona idea rimuovere per verificare se il listener è stato registrato prima (ad esempio, l'ascoltatore B non è mai stato registrato e dovrebbe essere rimosso)?

Quale comportamento aiuta gli sviluppatori qui a identificare i problemi senza crearne di nuovi come IllegalArgumentExceptions?

    
posta Chriss 01.12.2014 - 15:03
fonte

2 risposte

2

Innanzi tutto, dovresti essere coerente con la convenzione quadro. Java è un grande mondo per essere sicuro. Io uso Java per Android, e qui la convenzione è piuttosto permissiva.

Ad esempio, setOnClickListener ti permette di passare in null - questo è un modo rozzo ma non ambiguo per rimuovere il listener.

Dal codice sorgente Android:

public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
// [snip]

/**
 * Register a callback to be invoked when this view is clicked. If this view is not
 * clickable, it becomes clickable.
 *
 * @param l The callback that will run
 */
  public void setOnClickListener(OnClickListener l) {
      if (!isClickable()) {
          setClickable(true);
      }
      getListenerInfo().mOnClickListener = l;
  }

Null va bene:

  /**
   * Return whether this view has an attached OnClickListener.  Returns
   * true if there is a listener, false if there is none.
   */
  public boolean hasOnClickListeners() {
      ListenerInfo li = mListenerInfo;
      return (li != null && li.mOnClickListener != null);
  }

Dove sono consentiti più listener, anche la rimozione di un listener è un'operazione tollerante:

/**
 * Remove a listener for layout changes.
 *
 * @param listener The listener for layout bounds change.
 */
public void removeOnLayoutChangeListener(OnLayoutChangeListener listener) {
    ListenerInfo li = mListenerInfo;
    if (li == null || li.mOnLayoutChangeListeners == null) {
        return;
    }
    li.mOnLayoutChangeListeners.remove(listener);
}

mOnLayoutChangeListeners è un ArrayList e ArrayList.remove(Object object) non si blocca su un elemento inesistente - restituisce semplicemente false .

La linea di fondo è il principio di robustezza - sii prudente in ciò che fai, sii liberale in ciò che accetti agli altri .

In caso di dubbio, andrei con il liberale, a meno che tu non abbia una solida ragione per non farlo. È più semplice e meno sorprendente. La coerenza è il re e il principio di stupore si basa sulla coerenza.

Eventuali precondizioni dovrebbero essere chiaramente documentate. La tua definizione di interface Service di per sé non promette di generare eccezioni quando viene passato nulla.

Le annotazioni

@NonNull e @Nullable possono aiutare a rimuovere l'ambiguità e sono supportate da IDE validi come IntelliJ Idea o Android Studio.

    
risposta data 01.12.2014 - 17:19
fonte
1

Buona domanda. Credo che tali metodi non richiedano il proprio controllo semplicemente perché:

  1. NullPointerException viene generato nel caso in cui si verifichi l'evento, quindi supponendo che tu abbia verificato quel caso almeno una volta, sapresti se è stato passato null .
  2. Se inizi ad aggiungere controlli per addListener o removeListener , allora dovresti davvero eseguire controlli di base su ogni metodo per rimanere coerente nel codice, il che avrà un impatto negativo sulle prestazioni e sulla leggibilità.

Penso che il punto critico alla base di controlli come questi sia quello di garantire che i parametri siano quello che dovrebbero essere entro margini ragionevoli. Se sei l'unico a chiamare il metodo e in solo un paio di posti, aggiungere questi controlli è superfluo. Se non altro, usa junit per testare metodi specifici che utilizzano questi eventi per assicurare che tutto vada come previsto.

Quando hai davvero bisogno di eseguire questi controlli?

  1. C'è una seria possibilità che il parametro che viene passato possa essere null , e per una ragione o l'altra, non puoi controllare prima di passarlo su Service.addListener . Una possibilità potrebbe essere una biblioteca che usa la riflessione come la primavera.
  2. * Il tuo * programma non chiama questi metodi. Se si sta scrivendo una libreria per essere utilizzata da altri, è buona norma controllare tutti i metodi pubblici che si intende richiamare dal programma utilizzando la libreria.
  3. Stai entrando in una parte piuttosto delicata del tuo programma in cui molte cose potrebbero andare storte. Devi essere in grado di formulare alcune ipotesi sul tuo input e hai bisogno che non vengano smentite, preferibilmente non su un computer di produzione durante la tua pausa pranzo. (Io chiamo questi controlli di sanità mentale)

Mi riferisco principalmente a addListener , sebbene per rispondere alla tua altra domanda su removeListener , di solito non trovo necessario sapere se un listener è stato rimosso o meno. Se tali informazioni sono importanti, puoi facoltativamente restituire il listener rimosso o null altrimenti, sebbene lo farei solo se tali informazioni fossero necessarie (e di solito non è nella mia esperienza).

Spero che ti aiuti!

    
risposta data 01.12.2014 - 16:52
fonte

Leggi altre domande sui tag