Sto refactoring il nostro attuale design per come scarichiamo i dati statici. È un casino di gerarchia di classi profonde e inferno di callback e voglio convertirlo in un design lineare più elegante.
Ecco due esempi dei passaggi che eseguiamo per scaricare i dati statici (file JSON):
Esempio 1, scaricando il manifest principale:
- Tentativo di caricare il file manifest dal disco locale
- In caso di errore, scarica da Internet
- Se non riesci a scaricare, salva con errore
- In caso di successo, restituisci dati stringa
- In caso di successo, deserializza il file manifest
- Per ogni file nel manifest, tenta di caricare dal disco locale
- In caso di successo, deserializza e restituisci
- In caso di errore, scarica da Internet
- Se il download ha esito positivo, restituisci i dati stringa
- Deserializza e ritorna
- Se il download non riesce, salva l'errore
- Quando tutti i file sono deserializzati, restituisci tutti i risultati in un elenco
Esempio due, scarica un file specifico:
- Chiedi al gestore manifest di ottenere una versione specifica del file
- Se il gestore manifest lo ha memorizzato, torna
- Se il gestore manifest non lo ha archiviato localmente, prova a scaricarlo
- Se il download ha esito positivo, deserializza e restituisce
- Se il download non riesce, salva l'errore
Attualmente, ogni passo è un metodo in ... qualche classe nella gerarchia, e le sottoclassi sovrascrivono alcuni passaggi per adattarsi ad un involucro speciale (ad esempio non utilizzare manifest manager, non tentare di scaricare, ecc.). / p>
Quasi ogni passaggio utilizza i callback a causa della natura del download di materiale da Internet. E così, abbiamo finito per dover ignorare i callback in ... alcune classi nella gerarchia, che hanno portato a un pasticcio completo e illeggibile.
Il mio primo tentativo di refactoring è stato quello di utilizzare il pattern di progettazione Decorator o Chain of Responsibility. Il fatto è che alcuni passaggi richiedono il CdR e altri no. Inoltre, alcuni passaggi (se non tutti) utilizzano i callback, che non sembrano i pattern in cui il design deve essere utilizzato. Un altro problema era che alcuni passaggi della catena trasformavano un tipo di dati (stringa) in un altro (oggetto deserializzato). E infine, non ho idea di come tradurrei "prendi il manifest scaricato e produci ogni file separatamente, e quando tutto finisce, continua la catena".
Questo è il più vicino che ho ottenuto con un mix di classi Decorator e CoR, ma non include l'ultima parte in cui abbiamo diviso e scaricato ogni file singolarmente.
var callbackReader = new CallbackGameDataReader<ManifestVO<ManifestFileVO>>();
var manifestDeserializer = new DeserializeGameDataReader<ManifestVO<ManifestFileVO>>(callbackReader);
var wwwReader = new WwwGameDataReader(remoteEnvironmentPath, manifestDeserializer);
var streamingReader = new StreamingAssetsGameDataReader(streamingAssetsManifestPath, manifestDeserializer, wwwReader);
streamingReader.Read(dataId, o =>
{
ManifestVO<ManifestFileVO> localManifest = (ManifestVO<ManifestFileVO>)o;
localParsedData = localManifest.files;
localKeyedData = CreateKeyedData(localParsedData);
parsedData = localManifest.files;
keyedData = CreateKeyedData();
ContinueInit();
});
Inoltre non sembra molto intuitivo.
Il mio obiettivo è finire con qualcosa di simile a questo (kinda):
List<ManifestFile> dataFiles = new DataReader()
.ReadLocal() // How do we fallback to WWW?
.Deserialize<Manifest>()
.Split(manifest => manifest.files)
.ReadLocal()
.Deserialize<ManifestFile>()
.Merge() // ???
.ToList();
Ho letto di Chain of Responsibility, Decorator, Future / Promises, Pipeline e ho cercato di abbracciarli. Parte del problema è che ho fatto solo manipolazioni di raccolte di base con Linq (dove, select, etc) quindi qualcosa di simile a map / reduce è ancora nuovo per me, e l'altra parte è che non sono abbastanza sicuro di come si adattano i callback con tutto ciò (eccetto forse Future / Promises).
E poi c'è la parte di fallback. Potrei avere un singolo GetFile che tenta di fare tutto localmente, ma poi, nei casi in cui il processo dovrebbe sempre utilizzare i dati locali, devo duplicare la logica.
Quindi questo è dove sono ora. Ho letto un sacco di schemi e ho cercato di capire come scegliere quello che meglio si adatta alle mie esigenze e incollarle insieme.
I miei obiettivi sono:
- Ha bisogno di usare le callback
- Idealmente, posso configurare i fallback (se appropriato)
- Dovrebbe essere sicuro per il tipo (probabilmente usi i generici)
- Tutti i passaggi devono essere (relativamente) intercambiabili (ad esempio wwwReader con localReader)
Mi piacerebbe sentire i tuoi pensieri. Qualsiasi materiale consigliato da leggere sarebbe fantastico.
Grazie!
P.S. Oltre a risolvere effettivamente il problema, la mia intenzione è di imparare come implementarlo da zero, quindi preferirei evitare l'uso di librerie esistenti
P.P.S. La soluzione deve essere eseguita in Unity, che utilizza un runtime mono personalizzato, quindi async / await non è un'opzione. E l'utilizzo di librerie potrebbe introdurre problemi durante la compilazione per dispositivi mobili.