Indica la fine di uno stream quando null è un ritorno valido

4

Ho due tipi di flussi di dati che implementano entrambi la stessa interfaccia:

public interface DataReceiver {
    public Data getData();
}

public class DeviceDataReceiver implements DataReceiver {
    // Receives data from an external device
    public Data getData();
}

public class PlaybackDataReceiver implements DataReceiver {
    // Receives data from textfile
    public Data getData();
}

Il DeviceDataReceiver può restituire null , indicando che non ci sono nuovi dati disponibili, o che il dispositivo non è stato in grado di rilevare alcun nuovo dato. Il PlaybackDataReceiver restituisce null se tutti i dati del file sono stati restituiti. Entrambi dovrebbero essere interrogati in un ciclo while.

Il problema che sto avendo è come indicare che non ci sono nuovi dati disponibili, quindi lavorare con l'interfaccia usando l'iniezione delle dipendenze ha più senso. Stavo pensando di aggiungere questo metodo alla mia interfaccia:

public boolean hasNext();

Dove DeviceDataReceiver restituisce sempre true (poiché possono sempre esserci nuovi dati) e PlaybackDataReceiver restituisce true, a meno che non abbia raggiunto EOF.

Le classi verrebbero interrogate tramite l'interfaccia in questo modo:

DataReceiver rec = getDataReceiverImplementation();
while(rec.hasNext()) {
    Data data = rec.getData();
    if(data != null) { /* perform some action */ }
}

In questo modo è possibile iniettare entrambe le classi e il ciclo uscire automaticamente per PlaybackDataReceiver . Per uscire dal ciclo per DeviceDataReceiver , l'utente dovrebbe inviare un tipo di segnale, ad esempio premere un pulsante per interrompere la ricezione dei dati.

È comprensibile? Dovrebbe diventare parte di una biblioteca che sto scrivendo.

    
posta Luca Fülbier 13.11.2016 - 17:08
fonte

4 risposte

4

La segnalazione di errori in banda in cui determinati valori hanno un significato speciale è sempre problematica. È facile dimenticare di controllare quei casi speciali.

Hai trovato una soluzione a questo problema: sposta il significato speciale fuori dal valore di ritorno. In questo caso, questa è una funzione separata. Questa è una soluzione valida, ma introduce delle dipendenze tra il tuo metodo: il getData() deve essere chiamato solo se hasNext() è restituito true. Questo complica l'API. Ma in questo caso, questo è molto simile alla ben nota java.lang.Iterator API ed è un tale linguaggio accettabile. Infatti, potresti avere DataReceiver estendere l'interfaccia Iterator , quindi puoi utilizzare tutto DataReceivers in un ciclo for come:

for (Data data : receiver) {
  if (data != null) { /* perform some action */ }
}

Una soluzione alternativa consiste nel racchiudere il valore restituito in un tipo diverso che indica il tipo di risultato: abbiamo ottenuto un valore o un indicatore speciale. Ad esempio:

final class DataOrEOF {
  private final boolean isEOF;
  private final Data data;

  private DataOrEOF(boolean isEOF, Data data) {
    this.isEOF = isEOF;
    this.data = data;
  }

  public static DataOrEOF of(Data d) { return new DataOrEOF(false, d); }
  public static DataOrEOF ofEOF() { return new DataOrEOF(true, null); }

  public boolean isEOF() { return isEOF; }

  public Data getDataOrThrow() {
    if (isEOF) throw InvalidStateException(...);
    return data;
  }
}

Ciò obbliga il consumatore del risultato a verificare il caso speciale oa rischiare un'eccezione. Questo è in qualche modo simile a java.util.Optional di Java8, eccetto che il nostro DataOrEOF consente null di dati.

    
risposta data 13.11.2016 - 17:46
fonte
3

Il nostro lavoro come programmatori è di fornire astrazioni utili, complete e facili da usare.

Se un'astrazione non è utile e completa, il client che consuma (programmatore) deve aggiungere un po 'di cose insieme, probabilmente deducendo troppe informazioni sulle implementazioni sottostanti delle cose che sta raggruppando insieme, risultando più stretto accoppiamento di quanto vorremmo tra un cliente e un'implementazione.

Le astrazioni che pubblichiamo (anche se solo per noi stessi) dovrebbero essere così facili da usare che il programmatore cade nel pozzo del successo .

Come sottolinea @amon, il paradigma hasNext / getData è ben compreso. Non dovresti solo usarlo, dovresti anche implementare iteratore in modo che la tua astrazione sia un iteratore.

Per confronto, valori speciali e valori nulli sono impoveriti da un punto di vista auto-documentante.

    
risposta data 13.11.2016 - 18:16
fonte
2

Potresti definire una speciale istanza unica di Dati per indicare che è stata eseguita, ad es.

public class Data {
   public static final Data.EOF = new Data();'// Singleton to mark EOF

   // rest of Data stuff here...
}

Restituiscilo dai metodi. Il client verifica con ==, non è uguale a ()!

while (true) {  // for illustration, we're probably in a loop
                // real code probably needs to test isInterrupted()
    Data data = rec.getData();
    if (data == Data.EOF)
        break;

    // it's "real", process it...
}

// clean up interrupt, close stuff, finally clause, etc. here...
    
risposta data 13.11.2016 - 19:26
fonte
0

Il modo più semplice per risolverlo è quello di sfuggire a un indicatore di fine flusso. Scegli un carattere arbitrario ma non usato spesso. Quando viene trasmesso, segui un secondo char che indica se si tratta del char o del char di fine flusso.

    
risposta data 13.11.2016 - 17:36
fonte

Leggi altre domande sui tag