Modelli per un albero di dati persistenti con più opzioni di archiviazione?

4

Ho un problema del mondo reale che proverò ad astrarre in un esempio illustrativo.

Quindi immagina di avere oggetti dati in un albero, in cui gli oggetti genitore possono accedere ai bambini e i bambini possono accedere ai genitori:

// Interfaces
interface IParent<TChild> { List<TChild> Children; }
interface IChild<TParent> { TParent Parent; }

// Classes
class Top : IParent<Middle> {}
class Middle : IParent<Bottom>, IChild<Top> {}
class Bottom : IChild<Middle> {}

// Usage
var top = new Top();
var middles = top.Children; // List<Middle>
foreach (var middle in middles) {
    var bottoms = middle.Children; // List<Bottom>
    foreach (var bottom in bottoms) {
        var middle = bottom.Parent; // Access the parent
        var top = middle.Parent; // Access the grandparent
    }
}

Tutti e tre gli oggetti dati hanno proprietà che sono conservate in due archivi di dati (ad esempio un database e un servizio Web) e devono riflettere e sincronizzarsi con i negozi. Alcuni oggetti richiedono solo dal servizio web, alcuni solo scrivono su di esso.

Data Mapper

Il mio pattern preferito per l'accesso ai dati è Data Mapper , perché separa completamente il gli oggetti dati stessi dalla comunicazione con l'archivio dati:

class TopMapper {
    public Top FetchById(int id) {
        var top = new Top(DataStore.TopDataById(id));
        top.Children = MiddleMapper.FetchForTop(Top);
        return Top;
    }
}

class MiddleMapper {
    public Middle FetchById(int id) {
         var middle = new Middle(DataStore.MiddleDataById(id));
         middle.Parent = TopMapper.FetchForMiddle(middle);
         middle.Children = BottomMapper.FetchForMiddle(bottom);
         return middle;
    }
}

In questo modo posso avere un mapper per archivio dati, e costruire l'oggetto dal mapper che voglio, e quindi salvarlo usando il mapper che voglio.

C'è un riferimento circolare qui, ma immagino che non sia un problema perché la maggior parte delle lingue può semplicemente memorizzare i riferimenti di memoria agli oggetti, quindi non ci saranno in realtà dati infiniti.

Il problema con questo è che ogni volta che voglio costruire un nuovo Top , Middle o Bottom , ha bisogno di costruire l'intero albero degli oggetti all'interno della proprietà Parent o Children , con tutte le richieste dell'archivio dati e l'utilizzo della memoria che ciò comporta. E nella vita reale il mio albero è molto più grande di quello qui rappresentato, quindi questo è un problema.

Richieste nell'oggetto

In questo gli oggetti richiedono il loro Parent se Children se stessi:

class Middle {
    private List<Bottom> _children = null; // cache
    public List<Bottom> Children {
        get {
            _children = _children ?? BottomMapper.FetchForMiddle(this);
            return _children;
        }
        set {
            BottomMapper.UpdateForMiddle(this, value);
            _children = value;
        }
    }
}

Penso che questo sia un esempio del schema del repository . È corretto?

Questa soluzione sembra accurata: i dati vengono richiesti dall'archivio dati solo quando ne hai bisogno, e successivamente vengono archiviati nell'oggetto se desideri richiederlo nuovamente, evitando un'ulteriore richiesta.

Tuttavia, ho due diverse origini dati. C'è un database, ma c'è anche un servizio web, e devo essere in grado di creare un oggetto dal servizio web e salvarlo sul database e poi richiederlo di nuovo dal database e aggiornare il servizio web.

Questo mi rende anche un po 'a disagio perché gli oggetti dati stessi non ignorano più l'origine dei dati. Abbiamo introdotto una nuova dipendenza, per non parlare di una dipendenza circolare, rendendo più difficile il test. E gli oggetti ora mascherano la loro comunicazione con il database.

Altre soluzioni

Ci sono altre soluzioni che potrebbero occuparsi del problema dei negozi multipli ma significa anche che non ho bisogno di creare / richiedere tutti i dati ogni volta?

    
posta Robin Winslow 30.10.2012 - 13:05
fonte

2 risposte

0

Ho finito per attaccare al pattern DataMapper, ma poi ho cambiato la mia applicazione:

Per molto tempo stavo usando un builder per costruire l'albero degli oggetti complesso. Ma col tempo, ho scoperto che l'inflessibilità di questo sistema rendeva il codice non mantenibile.

Ora penso che il problema di costruire oggetti in cui gli oggetti genitoriali possano accedere a bambini e bambini possano accedere ai genitori è difficile per una buona ragione: perché sta cercando di creare oggetti che sono troppo strettamente accoppiati l'uno con l'altro. A causa dell'accoppiamento stretto, è impossibile creare oggetti senza costruire l'intero albero.

Ciò che ho finito per fare è stato di ristrutturare il resto della mia applicazione per rimuovere eventuali dipendenze, includendo invece i metodi FetchForChild e FetchForParent sul mio oggetto DataMapper , allentando l'accoppiamento.

Questo ora funziona molto bene.

    
risposta data 08.01.2014 - 13:28
fonte
3

La parte indesiderabile del tuo approccio deriva dal fatto che gli stessi oggetti dati si sbloccano (caricando, deserializzando) essi stessi.

Questo non è un problema semplice!

Idealmente vuoi che i tuoi oggetti dati siano abbastanza semplici e che alcuni agenti esterni siano responsabili della deserializzazione e serializzazione usando dati che vivono su disco, o sulla rete, ecc.

Un approccio a questo potrebbe usare la riflessione e l'introspezione di oggetti e cose fantasiose del genere.

Un altro approccio potrebbe usare per usare "oggetti generici". Un oggetto generico può memorizzare una quantità arbitraria di proprietà (cioè campi / variabili). Ogni campo può essere uno dei pochi tipi di set: int, string, array di byte, riferimento a un altro oggetto generico ( quello è importante ), ecc. Quindi puoi modellare i tuoi oggetti di dati come oggetti generici .

Il vantaggio dell'utilizzo di "oggetti generici" è che si può quindi avere un meccanismo esterno (o super) alla classe di oggetti generica attraverso la quale si accede alle sue proprietà. Questo meccanismo riconosce quando un campo non è ancora stato caricato e può recuperarlo dall'origine appropriata. Inoltre gestisce la scrittura della cache, ad es. recupero dei dati dall'origine remota, scrittura nella cache locale.

Quanto sopra è una descrizione dal suono molto generale, ma non è una cosa da poco da implementare, quindi potrebbe essere difficile ottenere qualcosa di più specifico senza diventare molto prolisso.

Vedi anche questa domanda SO: link

Nota inoltre che Dati principali in iOS è un oggetto grapher che gestisce il caricamento lazy (faulting) - l'area problematica è molto simile a ciò che i dati di base indirizzi.

Core Data is an object graph and persistence framework provided by Apple [...] It allows data organised by the relational entity–attribute model to be serialised into XML, binary, or SQLite stores. The data can be manipulated using higher level objects representing entities and their relationships. Core Data manages the serialised version, providing object lifecycle and object graph management, including persistence.

    
risposta data 03.12.2012 - 18:44
fonte