Come evitare una folle quantità di interfacce nell'interfaccia utente con iniezione di dipendenza?


8

Problema Di
recente ho letto molto sul fatto che Singleton sia cattivo e su come sia meglio l'iniezione di dipendenza (che intendo come "utilizzo delle interfacce"). Quando ho implementato parte di questo con callback / interfacce / DI e aderendo al principio di segregazione dell'interfaccia, ho finito con un bel casino.

Le dipendenze di un genitore dell'interfaccia utente dove fondamentalmente quelle di tutti i suoi figli si univano, quindi più in alto nella gerarchia era un elemento dell'interfaccia utente, più il suo costruttore era gonfio.

In cima alla gerarchia dell'interfaccia utente c'era una classe Application, che conteneva le informazioni sulla selezione corrente e un riferimento a un modello 3d che deve riflettere le modifiche. La classe di applicazione stava implementando 8 interfacce, e questa era solo una rotatoria di un quinto dei prodotti (/ interfacce) a venire!

Attualmente lavoro con un singleton che contiene la selezione corrente e gli elementi dell'interfaccia utente che hanno una funzione per aggiornarsi. Questa funzione scorre la struttura dell'interfaccia utente e gli elementi dell'interfaccia utente, quindi accede al singleton di selezione corrente, se necessario. Il codice mi sembra più pulito in questo modo.

Domanda
Un singleton è forse appropriato per questo progetto?
Altrimenti, c'è un difetto fondamentale nel mio pensiero e / o implementazione di DI che lo rende così ingombrante?

Ulteriori informazioni sul progetto
Tipo: carrello della spesa per appartamenti, con campane e fischietti
Dimensione: 2 mesi-uomo per il codice e l'interfaccia utente
Manutenzione: nessun aggiornamento in esecuzione, ma forse "versione 2.0" successiva
Ambiente: utilizzo di C # in Unity, che utilizza un'entità Sistema di componenti

In quasi tutti i casi, l'interazione dell'utente attiva diverse azioni. Ad esempio, quando l'utente seleziona un elemento

  • la parte dell'interfaccia utente che mostra quell'elemento e la sua descrizione devono essere aggiornati. Per questo, ha anche bisogno di ottenere alcune informazioni da un modello 3d per calcolare il prezzo.
  • più avanti nell'interfaccia utente, il prezzo totale complessivo deve essere aggiornato
  • una funzione corrispondente in una classe su un modello 3d deve essere chiamata per visualizzare le modifiche lì

DI non si tratta solo di usare le interfacce, è la capacità di scambiare concrezioni che è particolarmente utile nella sfera dei test unitari ...
Robbie Dee,

1
Inoltre, devo ancora vedere un problema in cui un singleton è la soluzione preferita. L'esempio canonico che le persone sembrano sempre affermare è uno scrittore di registri, ma anche qui potresti voler scrivere su un numero di registri diversi (errore, debug ecc.).
Robbie Dee,

@RobbieDee Sì, DI è di più, ma non riesco a vedere una situazione in cui l'utilizzo di un'interfaccia non è DI. E se un singleton non è la soluzione preferita - cosa che presumo anche io - allora perché la soluzione preferita è così disordinata? Questa è la mia domanda principale, ma volevo tenerlo aperto, poiché a volte la regola generale non si applica.
R. Schmitz,

La disposizione interfaccia-concrezione è anche una caratteristica dei quadri beffardi. Sono stati anche in lingue OO per alcuni decenni ...
Robbie Dee,

Non capisco il tuo punto.
R. Schmitz,

Risposte:


4

Penso che la domanda sia un sintomo, non una soluzione.

Di recente ho letto molto sul fatto che Singleton sia cattivo e su come sia meglio l'iniezione di dipendenza (che intendo come "utilizzo delle interfacce"). Quando ho implementato parte di questo con callback / interfacce / DI e aderendo al principio di segregazione dell'interfaccia, ho finito con un bel casino.

Una soluzione alla ricerca di un problema; questo e il suo malinteso probabilmente sta corrompendo il tuo design. Hai letto questa domanda SO su DI vs Singleton, concetti diversi o no? Ho letto che semplicemente avvolgendo un singleton in modo che il cliente non abbia a che fare con un singleton. It.is.just.good.old.encapsulation. Penso che sia perfetto.


più in alto nella gerarchia era un elemento dell'interfaccia utente, più era gonfio il suo costruttore.

Costruisci prima i pezzi più piccoli, poi passali nel costruttore della cosa a cui appartengono e passa quella cosa più grande nel costruttore della prossima cosa più grande ...

Se hai problemi di costruzione complessi, usa il modello di fabbrica o di costruzione. In conclusione c'è quella complessa costruzione inserita nella propria classe per mantenere le altre classi ordinate, pulite, comprensibili, ecc.


La classe di applicazione stava implementando 8 interfacce, e questa era solo una rotatoria di un quinto dei prodotti (/ interfacce) a venire!

Sembra l'assenza di capacità di estensione. Manca il design centrale e tutto viene riempito dall'alto. Dovrebbero esserci più costruzioni, composizione ed eredità dal basso verso l'alto.

Mi chiedo se il tuo design sia "molto pesante". Sembra che stiamo provando a fare in modo che una classe sia tutto o qualunque cosa possa essere. E il fatto che sia una classe UI, non una classe di dominio aziendale, mi fa davvero meravigliare della separazione delle preoccupazioni.

Rivedi il tuo progetto fin dall'inizio e assicurati che ci sia un'astrazione di prodotto solida e di base che può essere costruita per realizzare produzioni più complesse o di categorie diverse. Quindi è meglio avere raccolte personalizzate di queste cose in modo da avere un posto dove mettere la funzionalità di "livello di raccolta" - come quel "3 ° modello" di cui parli.


... modello 3d che deve riflettere i cambiamenti.

Gran parte di questo può rientrare nelle classi di raccolta personalizzate. Può anche essere una struttura di classe indipendente a se stessa a causa della profondità e della complessità. Queste due cose non si escludono a vicenda.

Leggi lo schema dei visitatori. È l'idea di avere interi mandrini di funzionalità collegati in modo astratto a diversi tipi.


Design e DI

Il 90% di tutta l'iniezione di dipendenze che farai sarà il passaggio di parametri del costruttore. Così dice il quy che ha scritto il libro . Progetta bene le tue classi ed evita di inquinare quel processo di pensiero con una vaga nozione sulla necessità di usare un contenitore-DI. Se ne hai bisogno, il tuo design te lo suggerirà per così dire.


Concentrati sulla modellazione del dominio commerciale degli appartamenti.

Evita l'approccio alla progettazione di Jessica Simpson : "Non so assolutamente cosa significhi, ma lo voglio".

I seguenti sono sbagliati:

  • Dovrei usare le interfacce
  • Non dovrei usare un singleton
  • Ho bisogno di DI (qualunque cosa sia)
  • Dovrei usare la composizione, non l'eredità
  • Dovrei evitare l'eredità
  • Ho bisogno di usare modelli

Ho provato a sbarazzarmi di tutti i singoli che ho usato e alcune delle cose che hai detto sono avvenute più o meno automaticamente. Anche il resto ha molto senso e prenderò il tuo consiglio e leggerò sul modello del visitatore ecc. Tutto sommato una risposta ben scritta, grazie!
R. Schmitz,

Solo un punto, e non sto cercando di essere un vampiro di aiuto qui, ma era in qualche modo parte della domanda originale: il consenso generale (e si basa su validi motivi) sembra essere che non dovresti davvero usa un singleton. Scrivi "'Non dovrei usare un singleton' è sbagliato" - in quale situazione sarebbe appropriato usarne uno allora?
R. Schmitz,

Tutto quello che sto dicendo è "dipende". Troppo spesso vedo massime prese alla lettera.
radarbob,

3

"Classe eredità" è un po 'una bandiera rossa. Ipotetico: una pagina web con 5 widget. La pagina non è un antenato di nessuno dei widget. Può contenere un riferimento a tali widget. Ma non è un antenato. Invece dell'erarchia di classe, considera l'utilizzo della composizione. Ognuno dei 5 widget può essere costruito da solo, senza riferimento agli altri widget o alla pagina principale. La pagina principale viene quindi costruita con informazioni sufficienti per creare una pagina di base e impaginare gli oggetti widget (Collection) che le vengono passati. La pagina è responsabile del layout, ecc., Ma non della costruzione e della logica dei widget.

Una volta che usi la composizione, DI è il tuo migliore amico. DI ti consente di scambiare ogni widget con qualsiasi versione o tipo di widget definito in DI. La costruzione dei widget è catturata nella DI ed è separata dalla pagina principale. Forse anche la composizione della Collezione può essere definita nel tuo DI. La pagina principale esegue il layout in base ai widget passati, come dovrebbe. Non sono necessarie modifiche al costruttore della pagina principale. La prima pagina richiede solo la logica per eseguire il layout dei widget e passare le informazioni al / dal widget in base alle interfacce definite.

Invece di passare gli ascoltatori su e giù per la catena di composizione, iniettare una raccolta di ascoltatori per quei widget che ne hanno bisogno. Iniettare la raccolta in un editore in modo che possano pubblicare nella raccolta. Iniettare la raccolta nei listener in modo che possano aggiungersi alla raccolta. La collezione attraversa tutti gli oggetti nella catena di composizione.


Siamo spiacenti, "gerarchia di classi" era una formulazione errata qui; in realtà non era la gerarchia di classi, ma piuttosto la gerarchia dell'interfaccia utente. I "widget" non sono sottoclassi della classe di applicazione, ma la "pagina" contiene riferimenti ad essi al fine di pubblicare gli aggiornamenti dell'interfaccia utente. È stato molto testabile con questa struttura, ma c'erano anche molte spese generali soprattutto nei costruttori, che trasmettevano molti ascoltatori per i loro figli (UI).
R. Schmitz,

@ R.Schmitz: le spese generali erano importanti? L'interfaccia utente è stata lenta e il design che hai descritto è da biasimare?
Robert Harvey,

@ R.Schmitz Puoi approfondire il "passaggio di molti ascoltatori"? Forse la mia esperienza con DI in C # è bassa, ma qualcosa mi dice che non sarebbe necessario (almeno nel costruttore) con una progettazione adeguata.
Katana314,

@RobertHarvey Nessuna perdita di prestazioni, riguarda solo la leggibilità.
R. Schmitz,

@ Katana314 Ad esempio, quando viene aggiunto un articolo, il modello 3d deve essere aggiornato e non appartiene a nessuna delle categorie di prodotti, quindi viene fatto riferimento nella classe dell'applicazione, che ascolta gli articoli aggiunti / rimossi / modificati. Alla fine sarebbe praticamente lo stesso per ogni categoria di prodotto. Anche altre parti dell'interfaccia utente reagiscono (e ascoltano), ma il messaggio deve spostarsi verso l'alto perché è qui che si trova il riferimento del modello 3d e i dati effettivi (che vengono poi aggiornati).
R. Schmitz,
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.