Come gestire il recupero dei dati per i riferimenti a oggetti circolari nei modelli di dominio OOP

3

Sto cercando di mettere insieme alcuni modelli base in OOP (C #), e apparentemente ho sbagliato idea.

Se ho un caso Comp di Workman, è applicato a un paziente. Ha anche una o più PatientVisits applicate al caso. Ad ogni PatientVisit è associato anche un Paziente e, naturalmente, un Paziente può avere un qualsiasi numero di Visite associato.

Il mio pensiero originale era quello di creare riflessioni di base sul mondo reale dei modelli di business facendo qualcosa del tipo:

class Patient {
        int ID;
        string FirstName;
        string LastName;

        List<Visit> Visits;
    }

    class Visit {
        int ID;
        DateTime VisitDate;
        Patient Patient;

        List<DxCode> DxCodes;
    }

    class WCCase {
        int ID;
        Patient Patient;
        DateTime DateEntered;

        List<Visit> Visits;
    }

Ho ingenuamente pensato che sarebbe bello, caricherò il mio modello e avrò tutto a portata di mano, molto semantico e rappresentativo del dominio / mondo reale, giusto?

Quindi, posso andare sulla strada opposta e riempire l'oggetto genitore e memorizzare gli elenchi di ID per i modelli correlati:

class Patient {
    int ID;
    string FirstName;
    string LastName;

    List<int> VisitIDs;

    public static Patient GetPatient(int id) {}
}

class Visit {
    int ID;
    DateTime VisitDate;
    int PatientID;

    List<DxCode> DxCodes;

    public Static Visit GetVisit(int id) {}
}

class WCCase {
    int ID;
    int PatientID;
    DateTime DateEntered;

    List<int> VisitIDs;

    public static case GetCase(int id) {}
}

Questo ovviamente mi permette di caricare tutto su richiesta (ottenere un ID e afferrare l'oggetto attraverso il suo metodo Get * ()), ma sembra un po 'lontano dall'idea di un modello del mondo reale ben rappresentativo e più come un mirror di una rappresentazione della tabella del database.

Suppongo che ci sia una via di mezzo, ma di sicuro vorrei avere qualche consiglio su come trovarlo ... Qualcuno può dare alcuni esempi di base su come modellizzarlo senza diventare pazzo con riferimenti e circolari, ecc. ?

Grazie in anticipo.

    
posta jleach 22.11.2015 - 04:49
fonte

2 risposte

2

Indirizzamento alla domanda di base

if I load a business entity into an object, and it has child objects as well, should I "fill out" all of the child models as well, or leave them null until needed, or...? [reference]

Dipende. (grazie Bob?)

Qualunque cosa. Questo è un dettaglio di implementazione. Non dovrebbe guidare la progettazione del dominio. Non sto dicendo nessuna delle due (al momento in cui scrivo) due risposte sbagliate. Sto dicendo che questo aspetto dovrebbe essere meglio incapsulato e quindi esposto in termini di dominio.

Encapsulating Data Fetching

... but seems a bit of a step away from the idea of a nicely representative real-world model and more like a mirror of a database table representation.

I'm guessing there must be some middle ground ...

Analisi del dominio

Come con qualsiasi buona burocrazia Gummit sei solo un numero per loro. E così una cosa di lavoratori è chi sei, cosa fai, cosa fanno (per te), ecc. Questa cosa suona come una classe e questo cosa è un numero. Ma non è un int , è un "file di casi Comp Workers" diciamo.

Inoltre, abbiamo tutti questi riferimenti circolari. Niente di male in questi aspetti da una prospettiva di progettazione OO.

public class WorkersCompCaseFile {
    public int ID { get; protected set; }
    public Patient { get; protected set; }
    public Visits { get; protected set; }
    public WCCase { get; protected set; }

    // here we can load things as desired, and change that
    // implementation isolated from the domain entities involved.

    //Case-wide functionalities
    public bool SuspectFraud() { 
        // lots of object instantiation for complex analysis
    }
}

// CLIENT
WorkersCompCaseFile myHardCase = new WorkersCompCaseFile();
if( myHardCase.HasVisits ) 
    myHardCase.SuspectFraud();  // yeah, everyone is guilty of something.

Punti dati

  • Abbiamo creato il contesto appropriato per garantire uno stato globale corretto.
    • Carico pigro / Eager, se necessario
    • I componenti non devono costruire pezzi, né esaminare continuamente, lo stato del contesto più ampio.
  • Con questa semplice incapsulamento - framework - ci sono così tante possibilità di design.
  • Interfaccia IDataFetch implementata da tutte le classi. Potrebbero esserci delegati o gestori di eventi in modo che WorkersCompCaseFile lo faccia in modo trasparente per i suoi componenti.
  • protected vice public proprietà
    • il file del caso controlla totalmente la costruzione del componente
    • protected int ID - non esporre il periodo di ID. È un concetto unificatore interno.
  • Patient , Visit , tutti hanno un riferimento al loro contenente WorkersCompCaseFile
    • Patient.Visits è un riferimento pass-through all'oggetto file del caso.
    • I componenti potrebbero non avere una proprietà ID.
  • Principio della minima conoscenza: il file del caso espone componenti per loro conto. per esempio.
    • MyHardCase.Name vice MyHardCase.Patient.Name
    • Il cliente è beatamente all'oscuro dell'istanza e della nullità dell'oggetto interno.
    • I componenti interni possono essere ugualmente, reciprocamente ignoranti.
risposta data 22.11.2015 - 18:03
fonte
2

Quello che stai descrivendo è un dilemma molto comune: da una parte tu vuoi mantenere il tuo modello di dominio semplice e ignorare i dettagli tecnici di archiviazione dei dati e modelli di accesso comuni. D'altro canto questo approccio di solito non è pratico perché tende ad essere molto inefficiente. Accade spesso che l'accesso a un'entità richieda di carica l'intero grafico dell'oggetto.

Un esempio semplice più estremo: supponiamo che un social network si modellizzi gli amici di un utente come elenco di oggetti utente.

class User {
  List<User> friends;
}

Caricamento di un utente sarebbe necessario caricare l'intera rete! Sarebbe non è nemmeno facile da configurare perché hai riferimenti circolari.

Opzione A: usa id

Un modo per affrontare questo problema è usare id ovunque e recuperare gli utenti per gli ID quando ne hai bisogno. Sebbene questo risolva il problema di dover caricare l'intero grafico, non è molto bello dal punto di vista della modellazione.

Opzione B: utilizza riferimenti espliciti

Un'altra alternativa è usare gli oggetti di riferimento che sono responsabili per restituire gli oggetti referenziati su richiesta. Sia da una cache o il database.

interface UserRef {
   User get();
}

class User {
  List<UserRef> friends;
}

Se non si utilizza un framework di persistenza, sarà necessario scrivi il tuo. Per motivi di prestazioni, di solito lo vorrai specificare quali riferimenti sono risolti immediatamente quando l'utente è caricato e che verrà caricato pigramente.

Opzione C: rimani con l'approccio ingenuo se appropriato

Infine, ci sono alcuni casi in cui ha senso modale a relazione come hai provato tu. In caso di composizione, ad es l'oggetto consiste di altri oggetti e non ci sono riferimenti circolari, e una cardinalità bassa e limitata può avere senso. Diciamo te modella un'auto che consiste, tra le altre cose, di un elenco di posti a sedere. Anche se non vuoi sempre accedere ai posti, potresti farlo efficienza commerciale per semplicità qui.

    
risposta data 22.11.2015 - 10:22
fonte

Leggi altre domande sui tag