Devo usare gli eventi in questo caso?

1

Sto creando un video player, come un player YouTube personalizzato.

Tutti gli elementi della GUI (barra di avanzamento, lettore video, pulsante di riproduzione, ...) sono classi diverse, ma ovviamente ho bisogno che comunichino. Quando si fa clic sulla barra di avanzamento o si sposta lo slider, è necessario inviare un comando "seek (x)" al lettore video. Allo stesso modo, il video player deve aggiornare la barra di avanzamento ogni fotogramma.

Attualmente lo sto facendo avendo quasi tutti gli elementi collegati tra loro. Quindi quando creo la barra di avanzamento, ti dirò dove si trova il video player.

Ma dopo un po 'questo diventa sempre più complicato e mi chiedo se gli eventi sarebbero un modo migliore per farlo. O una classe di controller principale che ha tutte le connessioni. Cosa dovrei fare?

    
posta joon 10.08.2012 - 12:45
fonte

5 risposte

4

Dovresti combinare eventi, stati e metodi.

Gli eventi dovrebbero solo informare che l'evento è successo, non di più. Gli stati dell'applicazione dovrebbero farti decidere cosa può e cosa non può essere fatto in quel momento. I metodi dovrebbero funzionare come l'avvio / l'arresto della riproduzione del video, ecc.

Gli eventi non dovrebbero essere usati al posto dei metodi, perché diventa più difficile leggere e comprendere il codice. Basta cambiare stato e chiamare alcuni metodi dal tuo evento.

    
risposta data 10.08.2012 - 12:57
fonte
3

Idealmente, le tue classi GUI non dovrebbero avere la minima idea di far parte di un video player. Loro interagiscono con l'utente e basta. Questo è chiamato Principio di responsabilità singola .

Nel tuo caso, creerei una classe TimeBase per tenere traccia del frame attualmente in riproduzione. Avrebbe seek() e nextFrame() metodi per cambiare il frame corrente. Gli oggetti chiamerebbero addSubscriber(subscriber, interval) per ricevere eventi quando cambia il frame corrente. Il parametro intervallo è per i widget che non necessitano di una risoluzione di un fotogramma, ad esempio un display che cambia ogni secondo, ad esempio.

Il prossimo livello in basso sarebbero classi di adattatori per ogni tipo di widget, che implementano sia l'interfaccia TimeBaseListener che le interfacce clickener / diapositiva / qualsiasi listener dalla classe GUI e tradurranno tra loro. Ad esempio, un ProgressBarAdapter otterrebbe i puntatori a entrambi gli oggetti TimeBase e ProgressBar durante la costruzione. Ascolta gli eventi spostati dal cursore, traduce il nuovo valore del cursore in un numero di frame e chiama timeBase.seek() . Quando ha ricevuto un evento FrameChanged , traduce il numero di fotogramma nel valore della barra di avanzamento e modifica la GUI di conseguenza.

Molte persone si bloccano al secondo livello, preferendo trasferire tale funzionalità in una classe derivata dalla classe del widget. Un sacco di tutorial sui siti web ufficiali delle lingue usano anche quel metodo, ma non dovrebbero. Viola sia il principio della singola responsabilità che preferisce la composizione all'eredità.

In termini meno buzz-wordy, il problema con l'ereditare dalla classe widget è che ora sei bloccato nella sua gerarchia di ereditarietà e non puoi mai sceglierne uno più adatto alla tua applicazione. Non puoi dividere la classe anche quando diventa troppo grande. Ciò che funziona bene su tutorial di siti Web di piccole dimensioni che illustrano un piccolo concetto non si adatta bene a un'applicazione di vita reale.

    
risposta data 10.08.2012 - 17:47
fonte
2

Le mie regole pratiche per l'utilizzo degli eventi:

  • puoi immaginare questi componenti esistenti separatamente, usati non solo in combinazione tra loro? Se sì, molto probabilmente andrà con gli eventi.
  • hai bisogno di questo componente di alcune informazioni in risposta all'evento inviato? Se non lo fai, allora, ancora una volta, molto probabilmente, hai preso la decisione giusta sull'invio di eventi.

Questo non ti libera da una buona e ben ponderata architettura OOP, dal momento che anche i gestori di eventi non dovrebbero essere codice spagetti.

Questo non ti libera dalle difficoltà di eseguire il debug di questo codice, che per sua natura tende ad essere asincrono.

Tuttavia, è possibile riassumerlo come segue: Se pensi al tuo sistema come a un insieme di componenti autonomi che dovrebbero essere facilmente intercambiabili / usati con qualsiasi altro (sotto) insieme di componenti, usa gli eventi.

    
risposta data 10.08.2012 - 12:57
fonte
1

Ogni framework GUI utilizza qualche tipo di pubblicazione / gestione degli eventi, quindi sei costretto a usarli.

Durante la progettazione di un'applicazione GUI, dovresti utilizzare il MVP design pattern (o una delle varianti, come Presenter prima o model-view ). In questo modo, puoi facilmente testare il codice e la logica dell'unità. Quando il codice viene testato unitamente, di solito è più semplice capire (fuori rotta, ci sono eccezioni).

Inoltre, non mettere tutto in una triade MVP, ma cerca di interrompere la tua applicazione il più possibile. Piccole porzioni di codice sono più facili da capire.

    
risposta data 10.08.2012 - 12:56
fonte
0

Sembra che quello che hai è la migliore scommessa per ora. Vuoi mantenere i tuoi collegamenti al minimo, ma deve esserci una connessione tra la barra di avanzamento e il video player, quindi almeno uno ha bisogno di un riferimento all'altro. Avere tutte le connessioni in un controller significa lavorare di più quando un elemento vuole parlare con un altro.

Gli eventi non ti salveranno. Se una dozzina (o anche due) elementi diversi, sconosciuti al giocatore, necessitavano di aggiornamenti regolari sul progresso, un evento sarebbe la cosa giusta. Tieni presente che il player ora sposterà l'evento anziché chiamare un metodo della barra di avanzamento, quindi ti troverai praticamente in difficoltà. (Ma ora è più difficile capire chi viene informato.) Ecco, penso che una semplice chiamata di metodo sia migliore.

L'elegante soluzione - quella che probabilmente si muove nella parte posteriore della tua testa che ti ha portato a fare questa domanda - sarebbe quella di invertire la responsabilità e fare in modo che la barra di progresso chieda al giocatore cosa il progresso è Ciò elimina il giocatore - non deve preoccuparsi di quando effettuare le chiamate - anche se deve avere le informazioni di avanzamento sempre pronte. Non ci sono eventi di fantasia, solo un metodo semplice che qualcuno chiama else . E al giocatore non importa quanti altri elementi chiamano il metodo, o quando, o perché.

Il lato negativo è che la barra di avanzamento deve eseguire un timer per mantenere le cose in movimento. Ma il giocatore avrebbe dovuto fare qualcosa di simile: il lavoro e la responsabilità sono ora concentrati a cui appartengono.

Non sono sicuro che tu abbia ancora bisogno di questa soluzione "elegante", ma penso che sia la direzione in cui vorresti muoverti più tardi.

(Se lo usi, assicurati di eseguire tutto su EventQueue, o sulla coda UI, o qualunque sia la tua lingua lo chiami - in ogni caso, mantieni tutto su un thread. difficile, e nella maggior parte dei casi l'interfaccia utente funziona solo sul proprio thread comunque.)

    
risposta data 10.08.2012 - 15:38
fonte

Leggi altre domande sui tag