È una cattiva pratica passare le istanze attraverso diversi livelli?

59

Nella progettazione del mio programma, mi capita spesso di passare le istanze di oggetti attraverso diverse classi. Per esempio, se ho un controller che carica un file audio, e poi lo passa a un giocatore, e il giocatore lo passa al playerRunnable, che lo passa di nuovo da qualche altra parte ecc. Sembra un po 'brutto, ma non lo faccio sapere come evitarlo. O è OK farlo?

EDIT: Forse l'esempio del giocatore non è il migliore perché potrei caricare il file più tardi, ma in altri casi non funziona.

    
posta Puckl 27.09.2012 - 14:32
fonte

12 risposte

54

Come altri hanno già detto, questa non è necessariamente una cattiva pratica, ma dovresti prestare attenzione a non interrompere la separazione dei problemi tra i livelli e passare le istanze specifiche dei livelli tra i livelli. Ad esempio:

  • Gli oggetti del database non dovrebbero mai essere passati a livelli superiori. Ho visto programmi che utilizzano la classe DataAdapter di .NET, un DB- accedere alla classe e passarla al livello dell'interfaccia utente, piuttosto che utilizzare DataAdapter nel DAL, creando un DTO o un set di dati e facendolo passare. L'accesso al database è il dominio del DAL.
  • Gli oggetti dell'interfaccia utente dovrebbero, ovviamente, essere limitati al livello dell'interfaccia utente. Ancora una volta, ho visto questa violazione, sia con i ListBox popolati con dati utente passati al livello BL, che su un array / DTO del suo contenuto, e (uno dei miei preferiti in particolare), una classe DAL che recuperava i dati gerarchici da il DB, e piuttosto che restituire una struttura gerarchica dei dati, ha appena creato e popolato un oggetto TreeView e lo ha passato nuovamente all'interfaccia utente per essere aggiunto dinamicamente a un modulo.

Tuttavia, se le istanze che stai passando sono DTO o entità, probabilmente è ok.

    
risposta data 27.09.2012 - 15:10
fonte
15

Interessante che nessuno abbia ancora parlato di Oggetti immutabili . Direi che passare un oggetto immutabile attraverso tutti i vari livelli è in realtà una buona cosa piuttosto che creare molti oggetti di breve durata per ogni livello.

Ci sono alcune grandi discussioni sull'immutabilità di Eric Lippert su il suo blog

D'altra parte, direi che passare oggetti mutabili tra i livelli è un design cattivo . Stai essenzialmente costruendo un livello con la promessa che i livelli circostanti non lo altereranno in un modo per violare il codice.

    
risposta data 27.09.2012 - 18:12
fonte
13

Il passaggio di istanze di oggetti attorno è una cosa normale da fare. Riduce la necessità di mantenere lo stato (cioè le variabili di istanza) e disaccoppia il codice dal suo contesto di esecuzione.

Un problema che si può affrontare è il refactoring, quando è necessario modificare le firme di più metodi lungo la catena di chiamata in risposta alla modifica dei requisiti dei parametri di un metodo vicino alla parte inferiore di tale catena. Tuttavia, può essere mitigato con l'uso di moderni strumenti di sviluppo software che aiutano nel refactoring.

    
risposta data 27.09.2012 - 14:49
fonte
8

Forse minore, ma c'è il rischio di assegnare questo riferimento da qualche parte in uno dei livelli, causando potenzialmente un riferimento ciondolante o perdita di memoria in seguito ..

    
risposta data 27.09.2012 - 14:59
fonte
7

Se stai passando oggetti semplicemente perché è necessario in un'area remota del tuo codice, utilizza l' inversione del controllo e modelli di progettazione di dipendenza da dipendenza insieme a un contenitore IoC appropriato possono risolvere i problemi relativi al trasporto di istanze di oggetti. L'ho usato su un progetto di medie dimensioni e non avrei mai più pensato di scrivere un grande pezzo di codice server senza usarlo.

    
risposta data 27.09.2012 - 19:28
fonte
4

Il passaggio di dati attraverso una serie di livelli non è una cosa negativa, è davvero l'unico modo in cui un sistema a livelli può funzionare senza violare la struttura a livelli. Il segno ci sono problemi quando passi i tuoi dati a diversi oggetti nello stesso livello per raggiungere il tuo obiettivo.

    
risposta data 27.09.2012 - 14:48
fonte
3

Risposta rapida: c'è niente di sbagliato in istanze di passaggio di oggetti. Come accennato, il punto è saltare l'assegnazione di questo riferimento in tutti i livelli che potenzialmente causano un riferimento ciondolante o perdite di memoria.

Nei nostri progetti, usiamo questa pratica per passare DTO (oggetto di trasferimento dati) tra gli strati ed è una pratica molto utile. Inoltre, riusciamo a riutilizzare i nostri oggetti dto per creare più complesse una volta, come per le informazioni di riepilogo.

    
risposta data 27.09.2012 - 15:27
fonte
3

Sono principalmente uno sviluppatore dell'interfaccia utente Web, ma a me sembra che il tuo disagio intuitivo potrebbe essere meno relativo al pass-through dell'istanza e più al fatto che stai procedendo in modo un po 'procedurale con quel controller. Il tuo controller dovrebbe sudare tutti questi dettagli? Perché fa riferimento anche a più del nome di un altro oggetto per ottenere l'audio riprodotto?

Nel design OOP, tendo a pensare in termini di ciò che è sempreverde e che è più probabile che sia soggetto a cambiamenti. L'argomento per cambiare le cose è quello che vorrete inserire nelle caselle degli oggetti più grandi in modo da poter mantenere interfacce coerenti anche quando i giocatori cambiano o vengono aggiunte nuove opzioni. Oppure ti ritrovi a voler scambiare oggetti audio o componenti al loro interno all'ingrosso.

In questo caso, il controller deve identificare che è necessario riprodurre un file audio e quindi avere un modo coerente / sempre verde di riprodurlo. Le cose del lettore audio, d'altro canto, potrebbero facilmente cambiare via via che la tecnologia e le piattaforme vengono alterate o vengono aggiunte nuove opzioni. Tutti questi dettagli dovrebbero essere posti sotto un'interfaccia di un oggetto composito più grande, IMO, e non dovresti dover riscrivere il controller quando i dettagli di come viene riprodotto l'audio cambiano. Quindi, quando passi un'istanza di un oggetto con i dettagli come la posizione del file nell'oggetto più grande, tutto ciò che si scambia è fatto all'interno di un contesto appropriato in cui qualcuno ha meno probabilità di fare qualcosa di stupido con esso.

Quindi, in questo caso, non penso che sia l'istanza di oggetto che viene gettata in giro che potrebbe infastidirti. È che il Capitano Picard sta correndo alla sala macchine per accendere il nucleo di curvatura, correre di nuovo sul ponte per tracciare le coordinate, e poi colpire il pulsante "punch-it" dopo aver acceso gli scudi piuttosto che semplicemente dire "Prendi noi al pianeta X a Warp 9. Fallo così. " e lasciando che il suo equipaggio risolva i dettagli. Perché quando lo gestisce in questo modo, può comandare qualsiasi nave nella flotta senza conoscere la disposizione di ogni nave e come tutto funzioni. E questo è in definitiva il più grande successo di progettazione OOP, IMO.

    
risposta data 28.09.2012 - 03:00
fonte
2

È un progetto abbastanza comune che finisce per accadere anche se potresti avere problemi di latenza se la tua app è sensibile a quel genere di cose.

    
risposta data 27.09.2012 - 14:53
fonte
2

Questo problema può essere risolto con variabili con scope dinamiche, se la lingua li ha, o archiviazione thread-local. Questi meccanismi ci consentono di associare alcune variabili personalizzate a una catena di attivazione oa un thread di controllo, in modo che non dobbiamo passare questi valori in codice che non ha nulla a che fare con essi solo in modo che possano essere comunicati a un altro codice che ha bisogno di loro.

    
risposta data 27.09.2012 - 19:48
fonte
2

Come hanno sottolineato le altre risposte, questo non è un design intrinsecamente povero. Può creare un accoppiamento stretto tra le classi nidificate e quelle che le annidano, ma l'allentamento dell'accoppiamento potrebbe non essere un'opzione valida se l'annidamento dei riferimenti fornisce un valore alla progettazione.

Una possibile soluzione è "appiattire" i riferimenti annidati nella classe controller.

Invece di passare un parametro più volte attraverso oggetti nidificati, puoi mantenere i riferimenti di classe del controllore a tutti gli oggetti nidificati.

Come esattamente questo è implementato (o se è anche una soluzione valida) dipende dal design attuale del sistema, come ad esempio:

  • Sei in grado di mantenere una sorta di mappa degli oggetti nidificati nel controller senza che questo si complichi troppo?
  • Quando si passa il parametro all'oggetto nidificato appropriato, l'oggetto nidificato può riconoscere immediatamente il parametro oppure esisteva una funzionalità aggiuntiva durante il passaggio attraverso gli oggetti nidificati?
  • ecc.

Questo è un problema che ho riscontrato in un modello di progettazione MVC per un client GXT. I nostri componenti della GUI contenevano componenti della GUI annidati per diversi livelli. Quando i dati del modello sono stati aggiornati, abbiamo finito per passarlo attraverso i vari livelli fino a raggiungere il componente appropriato. Creava un accoppiamento indesiderato tra i componenti della GUI perché se volevamo che una nuova classe di componenti della GUI accettasse i dati del modello, dovevamo creare metodi per aggiornare i dati del modello in tutti i componenti della GUI che contenevano la nuova classe.

Per risolverlo, abbiamo mantenuto nella classe View una mappa di riferimenti a tutti i componenti della GUI nidificati in modo che ogni volta che i dati del modello venivano aggiornati, la View poteva inviare i dati del modello aggiornato direttamente ai componenti della GUI che ne avevano bisogno, fine della storia. Questo ha funzionato bene perché c'erano solo singole istanze di ciascun componente della GUI. Potrei vederlo non funzionare così bene se ci fossero più istanze di alcuni componenti della GUI, rendendo difficile identificare quale copia doveva essere aggiornata.

    
risposta data 08.10.2012 - 03:10
fonte
0

Quello che stai descrivendo è un modello di design denominato Chain Of Responsibility .  Apple usa questo modello per il loro sistema di gestione degli eventi, per quello che vale.

    
risposta data 27.09.2012 - 23:16
fonte

Leggi altre domande sui tag