Programmazione basata su eventi: quando ne vale la pena?


19

Ok, so che il titolo di questa domanda è quasi identico a Quando dovrei usare la programmazione basata su eventi? ma le risposte a tale domanda non mi hanno aiutato a decidere se usare eventi nel caso specifico che sto affrontando.

Sto sviluppando una piccola applicazione. È un'app semplice e per la maggior parte la sua funzionalità è CRUD di base.

In determinati eventi (durante la modifica di determinati dati) l'applicazione deve scrivere una copia locale di tali dati in un file. Non sono sicuro di quale sia il modo migliore per implementarlo. Io posso:

  • Attiva eventi quando i dati vengono modificati e associa una risposta (genera il file) a tali eventi. In alternativa, implementare il modello di osservatore. Sembra una complessità inutile.
  • Chiamare il codice generatore di file direttamente dal codice che modifica i dati. Molto più semplice, ma sembra sbagliato che la dipendenza dovrebbe essere in questo modo, cioè sembra sbagliato che la funzionalità principale dell'app (codice che modifica i dati) dovrebbe essere accoppiata a quel vantaggio extra (codice che genera un file di backup). So, tuttavia, che questa app non evolverà fino al punto in cui tale accoppiamento pone un problema.

Qual è l'approccio migliore in questo caso?


2
Direi di non implementare nulla da soli: basta usare un bus eventi esistente. Questo renderà la vita molto più semplice ...
Boris the Spider

La programmazione guidata dagli eventi è intrinsecamente asincrona. Gli eventi possono o non possono accadere, nell'ordine in cui intendevi o forse in un altro ordine o per niente. Se riesci a gestire quella complessità in più, provaci.
Pieter B,

Generalmente guidato dagli eventi, il codice viene fornito come richiamate e queste vengono invocate altrove in modi che non è possibile prevedere. La tua descrizione suona più simile quando accade qualcosa di specifico nel tuo codice, devi fare più di quanto richiederebbe un'implementazione ingenua. Basta codificare le chiamate extra.
Thorbjørn Ravn Andersen,

V'è una differenza tra event-driven e basato su eventi. Guarda ad esempio l' episodio 355 di podcast di .NET Rocks estremamente stimolante con Ted Faison, Ted Faison porta gli eventi al limite! ( URL per il download diretto ) e il libro Programmazione basata su eventi: portare gli eventi al limite .
Peter Mortensen,

L'intervista a Ted Faison inizia a 13 minuti e 10 secondi.
Peter Mortensen,

Risposte:


31

Segui il principio KISS: Keep It Simple, Stupid o il principio YAGNI: non ne avrai bisogno.

Puoi scrivere il codice come:

void updateSpecialData() {
    // do the update.
    backupData();
}

Oppure puoi scrivere codice come:

void updateSpecialData() {
     // do the update.
     emit SpecialDataUpdated();
}

void SpecialDataUpdatedHandler() {
     backupData();
}

void configureEventHandlers() {
     connect(SpecialDataUpdate, SpecialDataUpdatedHandler);
}

In assenza di un motivo convincente per fare diversamente, seguire il percorso più semplice. Tecniche come la gestione degli eventi sono potenti, ma aumentano la complessità del codice. Richiede più codice per funzionare e rende più difficile seguire ciò che accade nel tuo codice.

Gli eventi sono molto critici nella giusta situazione (immagina di provare a fare la programmazione dell'interfaccia utente senza eventi!) Ma non usarli quando puoi KISS o YAGNI.


Mi piace soprattutto il fatto che tu abbia menzionato che l'attivazione di eventi quando i dati vengono modificati non è banale.
NoChance,

13

L'esempio che descrivi di un dato semplice, in cui la modifica attiva alcuni effetti può essere perfettamente implementata con il modello di progettazione dell'osservatore :

  • questo è più semplice da implementare e mantenere rispetto al codice guidato dall'evento completo.
  • l'accoppiamento tra soggetto e osservatore può essere astratto, il che facilita la separazione delle preoccupazioni.
  • è ideale per una o più relazioni (i soggetti hanno uno o più osservatori).

L'approccio event driven vale il suo investimento per scenari più complessi, quando possono verificarsi molte interazioni diverse, in un contesto molti-a-molti, o se sono previste reazioni a catena (ad esempio un soggetto informa un osservatore, che in alcuni casi vuole modificare il soggetto o altri soggetti)


1
Sono confuso, l'osservatore non è solo un modo per implementare eventi?
svick

1
@svick Non penso proprio. Nella programmazione guidata dagli eventi hai un ciclo principale che elabora gli eventi in una relazione da molte a molte, con mittenti e osservatori disaccoppiati. Penso che l'osservatore possa contribuire elaborando un tipo di evento perticolare, ma non è possibile ottenere l'intero spettro di EDP solo con un osservatore. Penso che la confusione derivi dal fatto che nel software event driven, gli osservatori sono talvolta implementati in cima all'elaborazione degli eventi (tipicamente MVC con una GUI)
Christophe

5

Come dici tu, gli eventi sono un ottimo strumento per ridurre l'accoppiamento tra le classi; pertanto, sebbene possa comportare la scrittura di codice aggiuntivo in alcune lingue senza il supporto integrato per gli eventi, riduce la complessità del quadro generale.

Gli eventi sono probabilmente uno degli strumenti più importanti in OO (Secondo Alan Kay - Gli oggetti comunicano inviando e ricevendo messaggi ). Se usi una lingua che ha il supporto integrato per gli eventi o tratta le funzioni come cittadini di prima classe, utilizzarli è un gioco da ragazzi.

Anche in lingue senza supporto incorporato, la quantità di piastra di caldaia per qualcosa come il modello Observer è abbastanza minima. Potresti essere in grado di trovare una libreria di eventi generica decente da qualche parte che puoi utilizzare in tutte le tue applicazioni per ridurre al minimo la piastra di cottura. (Un aggregatore di eventi generico o un mediatore di eventi è utile in quasi ogni tipo di applicazione).

Vale la pena in una piccola applicazione? Direi decisamente di sì .

  • Mantenere le classi disaccoppiate le une dalle altre mantiene pulito il grafico delle dipendenze delle classi.
  • Le classi prive di dipendenze concrete possono essere testate isolatamente senza considerare altre classi nei test.
  • Le classi senza dipendenze concrete richiedono meno test unitari per una copertura completa.

Se stai pensando "Oh, ma è davvero solo un'applicazione molto piccola, non ha molta importanza" , considera:

  • Le piccole applicazioni a volte finiscono per essere combinate con applicazioni più grandi in seguito.
  • È probabile che le applicazioni di piccole dimensioni includano almeno una parte della logica o dei componenti che potrebbero essere successivamente riutilizzati in altre applicazioni.
  • I requisiti per le piccole applicazioni possono cambiare, suggerendo la necessità di refactoring, il che è più semplice quando il codice esistente viene disaccoppiato.
  • Ulteriori funzionalità possono essere aggiunte in seguito, suggerendo la necessità di estendere il codice esistente, che è anche molto più semplice quando il codice esistente è già disaccoppiato.
  • Il codice liberamente accoppiato generalmente non richiede molto più tempo per scrivere rispetto al codice strettamente accoppiato; ma il codice strettamente accoppiato richiede molto più tempo per il refactoring e il test rispetto al codice debolmente accoppiato.

Nel complesso, la dimensione di un'applicazione non dovrebbe essere un fattore decisivo per mantenere le classi liberamente accoppiate; I principi SOLID non sono solo per grandi applicazioni, ma sono applicabili a software e codebase su qualsiasi scala.

In effetti, il tempo risparmiato in unità per testare le classi allentate in modo isolato dovrebbe controbilanciare qualsiasi ulteriore tempo speso disaccoppiando quelle classi.


2

Il modello di osservatore può essere implementato in modo molto più piccolo dell'articolo Wikipedia (o nel libro GOF), presupponendo che i linguaggi di programmazione supportino qualcosa come "callbacks" o "delegati". Basta passare un metodo di callback nel codice CRUD (il metodo observer, che potrebbe essere un metodo generico di "scrittura su file" o vuoto). Invece di "evento evento" basta chiamare quel callback.

Il codice risultante sarà solo minimamente più complesso che chiamare direttamente il codice generatore di file, ma senza gli svantaggi di un accoppiamento stretto di componenti non correlati.

Questo ti porterà "il meglio di entrambi i mondi", senza sacrificare il disaccoppiamento per "YAGNI".


Funziona la prima volta in Doc, ma quando l'evento deve innescare una seconda cosa, è probabile che la classe debba essere modificata facendolo in questo modo. Se si utilizza un modello di osservatore, è possibile aggiungere tutti i nuovi comportamenti desiderati senza aprire il backup della classe originale per la modifica.
RubberDuck,

2
@RubberDuck: l'OP stava cercando una soluzione "evitando complessità inutili" - se ci fossero eventi / comportamenti diversi necessari, probabilmente non considererebbe il modello di osservatore troppo complesso. Quindi sono d'accordo con quello che hai detto, quando le cose si fanno più complesse, un osservatore completo lo servirebbe meglio, ma solo allora.
Doc Brown,

Una dichiarazione equa, ma mi sembra una finestra rotta.
RubberDuck,

2
@RubberDuck: l'aggiunta di un osservatore completo, con la meccanica di editore / abbonato "per ogni evenienza", quando non è realmente necessario, mi sembra un eccesso di ingegnerizzazione - non è meglio.
Doc Brown,

1
Non sono d'accordo sul fatto che possa essere oltre l'ingegneria. Probabilmente mi sento come faccio perché è banale da implementare nel mio stack di scelta. Ad ogni modo, accetteremo solo di non essere d'accordo?
RubberDuck,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.