L'implementazione di un "sistema di eventi lanciabili" potrebbe essere offensivo?

3

Ho riflettuto per un paio d'anni sull'utilizzo degli eventi Throwable e sull'implementazione di una sorta di sistema di eventi che utilizza throw per inviare un evento, o lasciare che un metodo diverso lo gestisca con throws .

L'unico problema è che Throwable è riservato per errori ed eccezioni. Una ricerca su google mostra risultati zero (rilevanti) su qualcosa di simile. Presumo che questo sia completamente originale o in minoranza dei sistemi di eventi là fuori.

So che il sistema di eventi C # è simile alla mia idea (anche se non l'ho mai usato, ho visto solo frammenti di codice).

La domanda qui è che sarebbe abusivo creare un sistema di eventi che utilizzi le dichiarazioni try-catch e i throwables, che sono fatti apposta per la gestione degli errori? E ci sarebbero problemi di prestazioni rispetto a un'interfaccia "vanilla": listener con sistema di eventi event-event?

In ogni caso, probabilmente creerò un sistema di eventi usando questa idea solo per provarlo (un piccolo esercizio).

Ho fatto due esempi. Uno dei quali utilizza il sistema di eventi "vanilla", l'altro utilizza Throwables.

Vanilla :

public class Entity implements EntityListener {
  @Override
  public void entityEvent(EntityEvent e) {
    //...
  }
}

public interface EntityListener {
  public void entityEvent(EntityEvent e);
}

public static void main(String... args) {
  new Entity().entityEvent(new EntityEvent());
}

Throwable :

public class Class1 {

  public void logicalThrow() throws ThrowableEvent, HelloEvent, LogicEvent {
    if(something)
      throw new ThrowableEvent();
    else if (sayHello)
      throw new HelloEvent();
    else
      throw new LogicEvent();
  }
}

public class Class2 {
  private Class1 class1 = new Class1();

  public static void main(String... args) {
    try {
      class1.logicalThrow();
    } catch(ThrowableEvent e) {
      System.out.println("Event thrown: " + e.getClass().getName());
    } catch(HelloEvent e1) {
      System.out.println("Hello!");
    } catch(LogicEvent e2) {
      System.out.println("10 + 11 = 101");
    }
    // the other catches won't run since they inherit ThrowableEvent, 
    // but I put in the verbose catch statements for an example
  }
}
    
posta AMDG 24.05.2015 - 01:16
fonte

4 risposte

2

Would it be abusive to create an event system that uses try-catch statements and throwables, which are really made for error handling?

Abusivo è un termine caricato emotivamente. Ed è palesemente soggettivo ... a meno che tu non stia misurando uno standard che tutti gli interessati possano essere d'accordo.

Che cosa potrebbe essere quello standard?

  • Potrebbe essere una guida di stile particolare che le parti interessate hanno accettato di utilizzare.
  • "La migliore pratica del settore" NON è uno standard adatto perché: 1) non è codificato, e 2) cambia, e 3) non permetti a tutti di concordare cosa sia in realtà.
  • Potrebbe essere un insieme di regole di "buone pratiche" concordate dalle parti interessate.

E chi sono le parti interessate? È facile:

  • Se questo è un progetto personale, sei solo tu.
  • Se si tratta di un progetto che viene pagato da qualcuno per implementarlo: il team di progetto, il team di manutenzione e (forse) il cliente.
  • Se questo è un progetto open source - gli sviluppatori. (Le persone a valle possono voler dire la loro, ma la realtà è che hanno solo "diritti" se stanno contribuendo).
  • E così via.

Per la cronologia, né le vecchie Convenzioni di Sun Java Code o Guida in stile Java di Google dice qualsiasi cosa sull'opportunità o meno di ciò che stai proponendo.

Esiste un dogma comune secondo cui "le eccezioni non dovrebbero essere utilizzate per il normale controllo del flusso" ... ma si basa su una definizione circolare e (IMO) non è utile.

Questo dogma si basa (in parte) sui consigli dei designer Java. (Questo consiglio è stato influenzato dal fatto che le prime JVM non erano ottimizzate tenendo conto delle "eccezioni come controllo del flusso normale". Anche qui c'è una circolarità nella logica.) Tuttavia, non sono ancora riuscito a trovare questo consiglio in nessuna attuale normativa documentazione Java. (Le esercitazioni non sono normative ...)

And would there be performance issues compared to a "vanilla" interface-Listener-with-Event-object event system?

Potenzialmente, sì.

Un problema è che la creazione di un'istanza Throwable determina l'acquisizione dello stack di chiamate. Questo è costoso Puoi fare cose per ottimizzare questo:

  • Sostituire il metodo fillInStackTrace per non fare nulla elimina la maggior parte del sovraccarico.
  • In Java 7 e versioni successive, il compilatore JIT può ottimizzare alcuni casi in cui non è necessario acquisire stack di eccezioni.

L'altro pensa che ti sia sfuggito che puoi utilizzare solo eccezioni per la segnalazione di eventi all'interno di un singolo stack di chiamate e per schemi di gestione degli eventi in cui gli eventi vengono gestiti più in alto nello stack di chiamate. Questi sono limiti significativi e renderanno questo approccio poco pratico per molti casi d'uso.

In breve, questo non funzionerà come sistema di gestione di eventi generici.

    
risposta data 24.05.2015 - 03:39
fonte
3

Non capisco come funzionerebbe qualcosa del genere. Dopo che un metodo genera un'eccezione, interrompe l'esecuzione e conferisce definitivamente il controllo a qualche gestore di eccezioni. Ma dopo che un metodo ha generato un evento, ha temporaneamente il controllo sul gestore eventi, quindi continua ad essere eseguito.

Non vedo come potresti emulare temporaneamente abbandonando il controllo usando qualcosa che lascia definitivamente il controllo molto bene. Le eccezioni non sono mai state pensate per qualcosa di simile, e non penso che tu possa farle fare loro.

    
risposta data 24.05.2015 - 03:12
fonte
1

Oltre agli altri inconvenienti che altre risposte hanno posto, un altro è che il meccanismo Throwable è progettato per essere prepotente, cioè cortocircuita l'elaborazione normale ovunque sia presente. Immagina il seguente

 public void doSomething(){
    Foo it = new Foo();

    Bar something = it.doesSomething(); //anything thrown here will cause a hard stop here

    it.continuesDoingStuff(); //doesn't get executed

 }

Nel frammento sopra, lanciando un "evento" in doesSomething si modifica il flusso di doSomething in quello che è discutibilmente un modo sgradevole; all'utente incauto della tua API, il programma potrebbe ignorare del tutto continuesDoingStuff . L'evento si registrerà più come un effetto collaterale, che come un risultato atteso, che probabilmente sconfigge lo scopo della programmazione basata sugli eventi.

    
risposta data 24.05.2015 - 06:06
fonte
0

Questa potrebbe essere una buona strategia per un algoritmo di ricerca.

Supera hai un algoritmo ricorsivo che esegue ricerche molto profonde.

Quando hai trovato l'elemento che soddisfa i tuoi criteri di ricerca puoi semplicemente lanciare un "OnFoundEvent".

Se non hai trovato nulla, puoi lanciare un "OnNotFoundEvent".

Con questa tecnica si salta tutto il meccanismo di backtracking causato dall'istruzione return.

Esempio di pseudo codice:

public Object search(Criteria criteria) {
    try {
        performDeepSearch(criteria);
        return null; // should never reach this line
    } catch (OnFoundEvent found) {
        return found.object;
    } catch (OnNotFoundEvent notFound) {
        return null;
    }
}
    
risposta data 30.06.2016 - 15:09
fonte

Leggi altre domande sui tag