È meglio mettere un evento sull'oggetto in alto o più vicino a dove è locale?

5

Ad esempio, prendi il seguente codice strutturato di base:

class Player
{
   public QuestLog QuestLog { get; set; }
}

Consideriamo che "QuestLog" ha la capacità di sparare alcuni eventi quando le cose vengono aggiunte o rimosse da esso. Normalmente, vorrei iscrivermi direttamente a QuestLog, ad esempio facendo qualcosa del tipo:

player.QuestLog.AcceptedQuest += SomeHandler

Tuttavia, voglio passare in un contesto extra (l'oggetto giocatore). Ho visto un paio di opzioni.

  1. Creazione di una funzione di gestione lambda che può risolvere un parametro aggiuntivo per il gestore. Funziona ma ha alcune stranezze che non mi piacciono. Attualmente sto usando questo approccio.
  2. Potrei accoppiare il QuestLog a un giocatore, passare un riferimento del giocatore a cui appartiene ed estinguere gli eventi di conseguenza con esso. Sembra un accoppiamento inutile.
  3. Esporre l'evento sull'oggetto Player stesso e su QuestLog e "bolla" l'evento fino a dove il contesto è disponibile, consentendo così le iscrizioni direttamente sul giocatore.

Ricorda che un giocatore può avere alcune raccolte e alcuni eventi, quindi spostarli tutti potrebbe causare una dozzina di eventi da tenere in una classe.

Come viene normalmente gestito?

    
posta Vaughan Hilts 27.08.2014 - 01:13
fonte

1 risposta

2

Creating a lambda handler function which can resolve an extra parameter to the handler. This works but has some quirks I don't like. I'm currently using this approach.

Cosa non ti piace? Mantenendo la firma piccola e permettendo al gestore di essere a conoscenza delle cose che ha (ma l'oggetto che lancia eventi ha bisogno di non sapere) si separa molto efficacemente la conoscenza da dove deve andare.

I could couple the QuestLog to a Player, passing a reference of the player it belongs to in and fire events off accordingly with it. Feels like needless coupling.

Dato che i giocatori hanno già QuestLogs, allora direi che l'accoppiamento esiste già in una certa misura. Inoltre, se il tuo gestore vuole saperlo, questo non fa altro che rafforzare questa nozione.

L'accoppiamento non è sempre cattivo. Alcune cose dovrebbero davvero dipendere l'una dall'altra e un design pulito accoppierà queste cose insieme in modo coerente, in modo che sia semplice, pulito e robusto.

Expose the event on the Player object itself as well as the QuestLog and 'bubble' the event up into where the context is available, thus allowing subscriptions directly onto player.

A volte buono, ma spesso un segno che hai l'evento nel posto sbagliato. Questo fa trapelare dettagli di implementazione (o almeno lo sarebbe se non avessi QuestLog sul lettore pubblicamente, incoraggiando gli utenti a violare la legge di Demeter ...).

Ma tornando al mio commento, gli eventi si sono in gran parte disinnescati dal fare logica non UI. Gli eventi, per loro natura, causano un accoppiamento temporale (devi fare le cose nel momento giusto, nell'ordine giusto perché funzionino correttamente). Gli eventi proteggono gli invarianti in modo scadente e tendono a filtrare attraverso le astrazioni. Sono fragili, sono abbastanza difficili da testare, richiedono un mediatore per aggiungere / rimuovere l'evento, sono la causa principale delle perdite di memoria ...

Più spesso, i delegati (o le dipendenze stesse) vengono passati direttamente al costruttore in modo che il gestore sia conosciuto in fase di costruzione. Non si può abusare della classe se è costruita per funzionare sin dall'inizio.

Questo o cose funzionano su un meccanismo di pull piuttosto che di push. Qualcosa controlla lo stato ogni frame o qualche altro intervallo temporale (opzionalmente controllando un flag sporco che viene premuto). Ciò rende più facile ignorare i tempi delle cose in molti casi, rendendo il parallelismo molto più praticabile.

    
risposta data 27.08.2014 - 03:09
fonte

Leggi altre domande sui tag