Disegni di progettazione: spostamento di riferimenti a oggetti attorno a un'applicazione

4

Sono nuovo per i programmatori e sto cercando di aumentare la mia conoscenza della programmazione. Recentemente, un utente su Stack Overflow mi ha detto che usare i singleton è una cattiva idea, che incoraggiano l'accoppiamento stretto e che rendono i test più difficili.

Ciò che ha spinto questa discussione è stato che ogni classe che abbia mai scritto ha avuto accesso a qualsiasi tipo di dati attraverso un singleton. Ad esempio, una classe che gestisce i dati di stato per un gioco. Se qualsiasi altra classe volesse accedere a questi dati, tutto ciò che dovrebbero fare è ottenere l'istanza dei singleton e lì sarebbe. In particolare, creare un'istanza di una classe su AppDelegate, quindi accedervi tramite: [[[[UIApplication sharedApplication] delegate] gameObject] getScore].

L'utente SO mi ha detto che dovrei creare un'istanza del 'gameObject' ovunque sia necessario, quindi passare quell'oggetto ad ogni altra classe che ne ha bisogno tramite un metodo come argomento o tramite una proprietà.

Stavo discutendo di questi punti quando qualcun altro ha sottolineato che questa informazione è completamente sbagliata, che i singleton non sono correlati all'accoppiamento e che non influenzano affatto il test.

Ora sono completamente confuso su quale persona sia corretta. Qualcuno potrebbe per favore chiarire quale tecnica è la migliore per un oggetto a persistere per tutta la durata dell'app, ed essere accessibile ad altre classi, e anche mantenere un accoppiamento lento. Per me, l'oggetto che passa per metodo o proprietà per ogni classe sembra il più logico, ma non lo so!

Grazie in anticipo per qualsiasi aiuto!

    
posta ThisDarkTao 17.05.2012 - 20:56
fonte

7 risposte

6

Non lo farai mai sapere . La progettazione del programma riguarda i compromessi e l'analisi di particolari scenari per produrre una soluzione che sia buona (non la migliore) dato ciò che si sa su di loro. In questo particolare scenario, il sostenitore del singleton è decisamente scorretto ma ti manca il punto.

In scenari in cui la risposta è meno chiara, avrai bisogno di essere in grado di pensare le cose attraverso te stesso per giudicare quale sia l'approccio buono o cattivo per il problema in questione. Se sai cos'è l'accoppiamento e che cos'è un singleton, non dovresti davvero chiedercelo. È importante sviluppare questa abilità per quelle molte volte in cui i particolari dello scenario significano che non possiamo ottenere una risposta.

    
risposta data 17.05.2012 - 21:29
fonte
11

La parte che crea l'accoppiamento non è il fatto che la variabile vive in un singleton, ma che il tuo oggetto client va e lo recupera da lì. Ciò significa che non puoi mai cambiare il modo in cui il tuo oggetto viene archiviato senza introdurre alcun tipo di problema (supponendo, come dici tu, che molti oggetti si comportino in questo modo).

Pensa a cosa succederebbe, ad esempio, se decidessi di volere che il tuo programma sia MDI (interfaccia multi-documento) in modo che l'utente possa giocare più di una partita alla volta. Dal momento che l'intero programma accede a questo singleton, ti sei praticamente inculato.

D'altra parte, è possibile memorizzare il gioco nel singleton e quindi iniettarlo in oggetti che devono accedervi. Cerca l'iniezione di dipendenza. Non so perché avresti un singleton a quel punto, ma forse non sarebbe del tutto inutile.

Fondamentalmente, hanno entrambi ragione.

    
risposta data 18.05.2012 - 00:34
fonte
3

Riguardo al Singleton, considera un caso in cui desideri testare una classe che usa un Singleton, ma desideri prenderlo in giro. Puoi puoi eseguirlo estraendo un metodo chiamato getSingleton e quindi sovrascrivendolo in una sottoclasse verificabile, ma in questo modo diventa complicato.

La tua classe sarà più testabile se accetta la dipendenza dall'altra classe nel costruttore. È quindi possibile iniettare una dipendenza fittizia nei test.

Se lo applichi durante il tuo progetto, scoprirai che non hai più bisogno di Singletons, ma hai bisogno di "collegare" il tuo progetto sostanzialmente con il metodo principale.

Guice è un framework DI che aiuta in questo, ma prima consiglio di giocarci manualmente, quindi impari i concetti.

Se vuoi saperne di più, consiglio veramente cosa Miško Hevery ha da dire.

Video:

Blog:

risposta data 18.05.2012 - 07:21
fonte
2

Alla fine della giornata, devi fare ciò che ha più senso per te. I singleton stanno bene. Se hanno lavorato per te, usali.

Il problema con loro è che potresti aver bisogno di più di uno di loro un giorno. Il tuo programma di gioco potrebbe voler destreggiarsi tra due o più giochi contemporaneamente. Quindi devi rintracciare ogni riferimento al tuo GameObject e sostituirlo con qualcosa di più elaborato. Il costo di consentire più gameObject s dall'inizio è molto inferiore rispetto alla capacità di adattarsi in un secondo momento e solo leggermente più di un semplice GameObject per tutta la durata del programma.

Se sei sicuro di non avere più giochi, è più economico andare con il singleton. (Anche se evitarli potrebbe aiutare a migliorare le tue capacità di programmazione - potrebbe essere una buona pratica per le volte in cui non puoi usare i singleton.) Personalmente, ho passato abbastanza tempo a liberarmi dei singleton nel mio codice che ora li eviterò a tutti i costi.

Nel tuo caso, i singleton non sono correlati all'accoppiamento e non influenzano affatto il test (a parte il nuovo test che devi fare dopo averli rimossi).

Tuttavia, se il tuo codice rispondeva alle richieste HTTP web e quindi poteva essere eseguito in centinaia di thread simultaneamente, accedendo a singleton (e, per quella materia, qualsiasi dati visibili a più di un thread) diventa molto difficile. Se non si capisce bene la programmazione multi-threading, si causerà un grave disastro ferroviario. I valori nel singleton cambieranno in qualsiasi momento. I bug saranno irripetibili, perché dipendono dal tempo preciso di diversi thread. Test affidabili diventano impossibili.

Ci sono molte persone che fanno questo tipo di programmazione. Non capiscono il multi-threading. E le persone che devono ripulire dopo di loro sentono strongmente che non dovrebbero usare i singleton. Questo è ciò di cui parla l'utente di SO. (I pensa ti stava consigliando di creare un nuovo gameObject per ogni singola richiesta HTTP.) Dubito che questo sia rilevante per te, ma aiuta a sapere di cosa parlano tutti.

    
risposta data 17.05.2012 - 23:03
fonte
1

I singleton sono molto simili alle variabili globali con uno spazio dei nomi. Quasi tutte le persone dicono che le variabili globali sono cattive, ma in qualche modo, Singletons è ok.

Detto questo, Singletons ha alcuni usi specifici . Può essere usato quando vuoi decisamente una istanza di qualcosa. Ad esempio, si desidera solo 10 thread del database, altrimenti il server del database si arresta in modo anomalo durante il caricamento. La tua classe ThreadPool dovrebbe quindi essere un Singleton e gestire il threadpool DB.

Un altro uso potrebbe essere se si dispone di configurazioni di sola lettura (o che cambiano raramente) che sono essenzialmente variabili globali. In questo caso risparmierebbe memoria e file occasionali IO per averlo come Singleton.

Nel tuo esempio: vuoi avere un GameState come Singleton. Perché dovresti limitare il tuo programma a un gioco? Anche se volevi inizialmente una partita, ti stai limitando mettendo la logica singleton nella classe GameState.

Non sono sicuro della tua applicazione, ma avrei creato un oggetto di gioco separatamente:

void startGame() {
    Game gs = new Game();
    gs.play();
}

Ora come troviamo il punteggio? Dipende da chi ha bisogno del punteggio. Se presumo che la classe Game sia proprietaria del punteggio, allora può avere una funzione getScore() . A volte un Player potrebbe richiedere un punteggio. Questo può essere risolto lasciando che i giocatori abbiano un riferimento Game .

void startGame(Player[] players) {
    Game gs = new Game();
    for ( Player player : players ) {
        player.registerForGame(this);
    }
    gs.addPlayers(players);
    gs.play();
}
Player::registerForGame(Game game) {
    this.currentGame = game;
}

Il codice non è in una lingua particolare - solo un'illustrazione di come potrebbe essere progettato senza singleton. Inoltre, non ritengo che questo sia un "buon" design - per esempio la classe Game sarà probabilmente troppo qui, se il Gioco ha molte funzionalità.

Come qualcuno ha detto che il design è solo un sacco di compromessi e devi fare ciò che ha senso per un particolare problema.

    
risposta data 18.05.2012 - 09:36
fonte
0

Bene, iniziamo con singleton .

Sono un web developer JS / lato client che ha recentemente deciso di implementare singleton. Sto scrivendo oggetti UI che sono alquanto monolitici in quanto rappresentano tutti gli elementi dell'interfaccia utente di un determinato tipo in una pagina. L'oggetto dell'interfaccia utente è factory, warehouse e event / interface manager.

Il punto nella costruzione e nella gestione di tutte le istanze di questi elementi dell'interfaccia utente di un determinato tipo dallo stesso tipo che rappresenta l'oggetto è che a volte nell'interfaccia utente voglio trattare un elemento dell'interfaccia alla volta e talvolta voglio che tutti rispondere allo stesso evento. Inoltre, rende più facile la creazione di un'interfaccia più basata sugli eventi, che è qualcosa su cui mi è capitato di fare tardi.

Ma se dovessi consentire a più fabbriche dello stesso costruttore, avrei un tempo sgradevole o almeno goffo di cercare di colpire tutti gli elementi dell'interfaccia utente con un evento globale, ad esempio perché ogni oggetto di tipo UI generale l'istanza avrebbe solo i riferimenti agli elementi dell'interfaccia utente che ha effettivamente creato. Invece, i miei metodi di fabbrica di oggetti di tipo UI generale restituiranno semplicemente un riferimento agli oggetti originali quando sono già stati creati. Una classe / costruttore. Un'istanza.

La versione breve:

I singleton riguardano la prevenzione di più istanze quando avere istanze multiple potrebbe causare un problema. La fine.

Ora allontanati da Singletons, il soggetto di accoppiamento stretto :

L'accoppiamento stretto è tipicamente legato alle dipendenze eccessive. Un grosso oggetto globale che contiene troppe informazioni di stato senza alcun mezzo per verificare da dove arrivano le informazioni potrebbe essere problematico, ma non è strettamente vincolante. Quando le cose sono troppo strettamente accoppiate, tocco una funzione o un oggetto o un elemento casuale dell'ambiente (come dire una classe HTML sul web) e faccio esplodere tutte le interruzioni.

Passare un riferimento allo stesso oggetto in modo eccessivo e in modi bizzarri / casuali potrebbe portare a uno scenario strettamente accoppiato, ma soprattutto è una buona idea quando si conoscono i propri riferimenti dai propri oggetti / proprietà che vengono superati in base al valore e capitalizzare su tale conoscenza con saggezza.

    
risposta data 18.05.2012 - 07:56
fonte
0

Il disaccoppiamento ha valore se prevedi di riutilizzare il tuo codice (magari creando un server multi-game, ad esempio) o creando test unitari per lo sviluppo continuo o lo sviluppo del team.

Tuttavia, il tempo e gli sforzi per disaccoppiare il codice potrebbero essere completamente sprecati se non si riutilizza o si ha bisogno di testare ulteriormente il codice (ad esempio il gioco funziona, ma non si vende bene, quindi si passa ad un altro progetto) . Inoltre, un eccessivo disaccoppiamento negli anelli più interni di un motore di calcolo (simulazione fisica, ecc.) Può effettivamente sprecare la durata della batteria degli utenti (ad esempio, non essere "verde").

Il valore aggiunto del disaccoppiamento potrebbe entrare in gioco se il tuo gioco è un dormiente, inizia a vendere bene l'anno prossimo e devi tornare indietro e leggere il codice per aggiornare l'app. Quindi disaccoppiare per aggiungere leggibilità, il potenziale riutilizzo del codice e la testabilità finiranno per avere un valore, dopo tutto.

È un trade-off e un azzardo. Molti grandi progetti mission critical devono scommettere che il disaccoppiamento aggiunge valore (e quasi sempre vince quella scommessa). Per molte piccole app che vanno a morire in un app store, il gioco d'azzardo potrebbe essere una perdita completa.

    
risposta data 26.05.2012 - 19:19
fonte