Salvare un oggetto tramite un metodo a sé stante o tramite un'altra classe?

36

Se voglio salvare e recuperare un oggetto, dovrei creare un'altra classe per gestirlo, o sarebbe meglio farlo nella classe stessa? O forse mescolando entrambi?

Quale è raccomandato secondo il paradigma OOD?

Ad esempio

Class Student
{
    public string Name {set; get;}
    ....
    public bool Save()
    {
        SqlConnection con = ...
        // Save the class in the db
    }
    public bool Retrieve()
    {
         // search the db for the student and fill the attributes
    }
    public List<Student> RetrieveAllStudents()
    {
         // this is such a method I have most problem with it 
         // that an object returns an array of objects of its own class!
    }
}

Versus. (So che è raccomandato quanto segue, tuttavia mi sembra un po 'contrario alla coesione di Student class)

Class Student { /* */ }
Class DB {
  public bool AddStudent(Student s)
  {

  }
  public Student RetrieveStudent(Criteria)
  {
  } 
  public List<Student> RetrieveAllStudents()
  {
  }
}

Che ne dici di mescolarli?

   Class Student
    {
        public string Name {set; get;}
        ....
        public bool Save()
        {
            /// do some business logic!
            db.AddStudent(this);
        }
        public bool Retrieve()
        {
             // build the criteria 
             db.RetrieveStudent(criteria);
             // fill the attributes
        }
    }
    
posta Ahmad 09.12.2014 - 17:21
fonte

5 risposte

32

Principio della singola responsabilità , Separazione delle preoccupazioni e Coesione funzionale . Se leggi questi concetti, la risposta che ottieni è: Separali .

Un semplice motivo per separare Student dalla classe "DB" (o StudentRepository , per seguire le convenzioni più diffuse) è quello di consentire di modificare le "regole aziendali", presenti nella classe Student , senza influire sul codice responsabile della persistenza e viceversa.

Questo tipo di separazione è molto importante, non solo tra le regole aziendali e la persistenza, ma tra le molte preoccupazioni del tuo sistema, per consentire di apportare modifiche con un impatto minimo in moduli non correlati (minimo perché a volte è inevitabile). Aiuta a costruire sistemi più robusti, più facili da mantenere e più affidabili in caso di modifiche costanti.

Mettendo insieme regole di business e persistenza, una singola classe come nel tuo primo esempio o con DB come dipendenza di Student , stai unendo due preoccupazioni molto diverse. Può sembrare che appartengano insieme; sembrano coesi perché usano gli stessi dati. Ma ecco la cosa: la coesione non può essere misurata solo dai dati che sono condivisi tra le procedure, bisogna anche considerare il livello di astrazione a cui essi esistono. In effetti, il tipo di coesione ideale è descritto come:

Functional cohesion is when parts of a module are grouped because they all contribute to a single well-defined task of the module.

E chiaramente, l'esecuzione di convalide su Student mentre anche la persistenza non costituisce "una singola attività ben definita". Quindi, ancora una volta, le regole aziendali ei meccanismi di persistenza sono due aspetti molto diversi del sistema, che secondo molti principi di buona progettazione orientata agli oggetti dovrebbero essere tenuti separati.

Raccomando di leggere su Pulire l'architettura , guardare < a href="https://www.youtube.com/watch?v=Gt0M_OHKhQE"> questo parla del principio di responsabilità singola (dove viene utilizzato un esempio molto simile) e guardando parla anche di Clean Architecture . Questi concetti descrivono le ragioni alla base di tali separazioni.

    
risposta data 09.12.2014 - 17:41
fonte
10

Entrambi gli approcci violano il principio di responsabilità unica. La prima versione fornisce la classe Student a molte responsabilità e la lega a una specifica tecnologia di accesso ai database. Il secondo porta a un'enorme classe DB che sarà responsabile non solo per gli studenti, ma per qualsiasi altro tipo di oggetto dati nel tuo programma. EDIT: il tuo terzo approccio è il peggiore, dal momento che crea una dipendenza ciclica tra la classe DB e la classe Student .

Quindi, se non hai intenzione di scrivere un programma giocattolo, non usarne nessuno. Invece, usa una classe diversa come StudentRepository per fornire un'API per il caricamento e il salvataggio, supponendo che implementerai il codice CRUD da solo. Potresti anche prendere in considerazione l'uso di un ORM framework, che può fare il duro lavoro per te (e il framework tipicamente imporre alcune decisioni in cui devono essere posizionate le operazioni di caricamento e salvataggio.

    
risposta data 09.12.2014 - 17:38
fonte
3

Ci sono alcuni modelli che possono essere usati per la persistenza dei dati. Esiste il modello Unità di lavoro , c'è Repository pattern, ci sono alcuni pattern aggiuntivi che possono essere usati come Remote Facade e così via.

Molti di loro hanno i loro fan e i loro critici. Spesso si tratta di scegliere quello che sembra adattarsi meglio all'applicazione e attenersi ad esso (con tutti i suoi lati positivi e negativi, cioè non usare entrambi i modelli contemporaneamente ... a meno che non si sia veramente sicuri di ciò).

Come nota a margine: nell'esempio RetrieveStudent, AddStudent dovrebbero essere metodi statici (perché non dipendono dall'istanza).

Un altro modo per avere metodi di salvataggio / caricamento in classe è:

class Animal{
    public string Name {get; set;}
    public void Save(){...}
    public static Animal Load(string name){....}
    public static string[] GetAllNames(){...} // if you just want to list them
    public static Animal[] GetAllAnimals(){...} // if you actually need to retrieve them all
}

Personalmente userò tale approccio solo in applicazioni abbastanza piccole, possibilmente strumenti per uso personale o dove posso prevedere in modo affidabile che non avrò un caso d'uso più complicato del semplice salvataggio o caricamento di oggetti.

Anche personalmente, vedi il modello dell'Unità di lavoro. Quando lo si conosce, è davvero buono in entrambi i casi piccoli e grandi. Ed è supportato da molti framework / apis, per esempio EntityFramework o RavenDB per esempio.

    
risposta data 09.12.2014 - 17:39
fonte
1

Se è un'app molto semplice in cui l'oggetto è più o meno legato al datastore e viceversa (cioè può essere pensato come una proprietà del datastore), quindi avere un metodo .save () per quella classe potrebbe ha senso.

Ma penso che sarebbe davvero eccezionale.

Piuttosto, di solito è meglio consentire alla classe di gestire i suoi dati e le sue funzionalità (come un buon cittadino OO) e esternalizzare il meccanismo di persistenza in un'altra classe o in un insieme di classi.

L'altra opzione è quella di utilizzare un framework di persistenza che definisce la persistenza in modo dichiarativo (come con le annotazioni), ma che sta ancora esternalizzando la persistenza.

    
risposta data 13.12.2014 - 03:06
fonte
-1
  • Definisci un metodo su quella classe che serializza l'oggetto e restituisce una sequenza di byte che puoi inserire in un database o file o qualsiasi altra cosa
  • Avere un costruttore per quell'oggetto che accetta una tale sequenza di byte come input

Riguardo al metodo RetrieveAllStudents() , il tuo sentimento è giusto, è probabilmente probabilmente mal riposto, perché potresti avere più elenchi distinti di studenti. Perché non mantenere semplicemente la lista (s) al di fuori della classe Student ?

    
risposta data 12.12.2014 - 22:56
fonte

Leggi altre domande sui tag