Si je veux sauvegarder et récupérer un objet, dois-je créer une autre classe pour le gérer, ou vaudrait-il mieux le faire dans la classe elle-même? Ou peut-être mélanger les deux?
Qu'est-ce qui est recommandé selon le paradigme OOD?
Par exemple
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!
}
}
Contre. (Je sais que ce qui suit est recommandé, mais cela me semble un peu contre la cohésion de Student
classe)
Class Student { /* */ }
Class DB {
public bool AddStudent(Student s)
{
}
public Student RetrieveStudent(Criteria)
{
}
public List<Student> RetrieveAllStudents()
{
}
}
Que diriez-vous de les mélanger?
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
}
}
design
object-oriented
Ahmad
la source
la source
Réponses:
Principe de responsabilité unique , séparation des préoccupations et cohésion fonctionnelle . Si vous lisez ces concepts, vous obtiendrez la réponse suivante: séparez-les .
Une raison simple de séparer la
Student
classe "DB" (ouStudentRepository
, pour suivre des conventions plus répandues) est de vous permettre de modifier vos "règles métier", présentes dans laStudent
classe, sans affecter le code responsable de la persistance, et vice-versa. vice versa.Ce type de séparation est très important, non seulement entre les règles métier et la persistance, mais aussi entre les nombreuses préoccupations de votre système, pour vous permettre d'apporter des modifications avec un impact minimal sur les modules indépendants (minime car il est parfois inévitable). Cela permet de construire des systèmes plus robustes, faciles à entretenir et plus fiables en cas de changements constants.
En associant règles de persistance et règles métier, soit une classe unique, comme dans votre premier exemple, soit
DB
une dépendance deStudent
, vous associez deux préoccupations très différentes. Il peut sembler qu'ils vont bien ensemble. ils semblent cohésifs car ils utilisent les mêmes données. Mais voici la chose: la cohésion ne peut pas être mesurée uniquement par les données partagées entre les procédures, vous devez également prendre en compte le niveau d'abstraction auquel elles existent. En fait, le type de cohésion idéal est décrit comme suit:Et évidemment, effectuer des validations
Student
pendant un certain temps, même si cela persiste, ne constitue pas "une tâche unique bien définie". Encore une fois, les règles métier et les mécanismes de persistance sont deux aspects très différents du système, qui, selon de nombreux principes de bonne conception orientée objet, doivent être séparés.Je vous recommande de lire sur l' architecture propre , de regarder ce qui suit sur le principe de responsabilité unique (dans lequel un exemple très similaire est utilisé) et de regarder également cette présentation sur l'architecture propre . Ces concepts décrivent les raisons de telles séparations.
la source
Student
classe devrait être correctement encapsulée, oui? Comment une classe externe peut-elle alors gérer la persistance d'un état privé? L'encapsulation doit être comparée au SoC et au SRP, mais choisir simplement l'un ou l'autre plutôt que l'autre sans peser avec soin les compromis est probablement une erreur. Une solution possible à cette énigme consiste à utiliser des accesseurs privés de paquets pour le code de persistance à utiliser.Les deux approches violent le principe de responsabilité unique. Votre première version donne à la
Student
classe de nombreuses responsabilités et la lie à une technologie d’accès à la base de données spécifique. La seconde mène à uneDB
classe énorme qui sera responsable non seulement des étudiants, mais de tout autre type d’objet de données de votre programme. EDIT: votre troisième approche est la pire car elle crée une dépendance cyclique entre la classe DB et laStudent
classe.Donc, si vous n'écrivez pas un programme de jouets, n'en utilisez aucun. Au lieu de cela, utilisez une classe différente, telle que celle
StudentRepository
pour fournir une API pour le chargement et la sauvegarde, en supposant que vous allez implémenter le code CRUD vous-même. Vous pouvez également envisager d'utiliser un cadre ORM , qui peut effectuer le travail difficile pour vous (et le cadre appliquera généralement certaines décisions lorsque les opérations de chargement et d'enregistrement doivent être placées).la source
Il existe de nombreux modèles pouvant être utilisés pour la persistance des données. Il existe un modèle d' unité de travail, un modèle de référentiel , d'autres modèles pouvant être utilisés, tels que la façade distante, et ainsi de suite.
La plupart de ceux-ci ont leurs fans et leurs critiques. Souvent, il s'agit de choisir ce qui semble être le mieux adapté à l'application et de s'y tenir (avec tous ses avantages et inconvénients, c'est-à-dire de ne pas utiliser les deux modèles en même temps ... sauf si vous en êtes vraiment sûr).
Remarque: dans votre exemple, RetrieveStudent, AddStudent devrait être une méthode statique (car elle ne dépend pas d'une instance).
Une autre façon d'avoir des méthodes de sauvegarde / chargement en classe est la suivante:
Personnellement, je n'utiliserais cette approche que dans des applications relativement petites, éventuellement des outils à usage personnel ou pour lesquels je peux prédire de manière fiable que les cas d'utilisation ne seront pas plus compliqués que la simple sauvegarde ou le chargement d'objets.
Aussi personnellement, voir le modèle d'unité de travail. Lorsque vous apprenez à le connaître, il est vraiment bon dans les petites et les grandes affaires. Et il est supporté par beaucoup de frameworks / apis, nommer EntityFramework ou RavenDB par exemple.
la source
S'il s'agit d'une application très simple dans laquelle l'objet est plus ou moins lié au magasin de données et inversement (c'est-à-dire qu'il peut être considéré comme une propriété du magasin de données), il peut être judicieux d'utiliser une méthode .save () pour cette classe.
Mais je pense que ce serait assez exceptionnel.
Il est généralement préférable de laisser la classe gérer ses données et ses fonctionnalités (comme un bon citoyen OO) et d'externaliser le mécanisme de persistance vers une autre classe, ou un ensemble de classes.
L'autre option consiste à utiliser un cadre de persistance qui définit la persistance de manière déclarative (comme avec les annotations), mais en externalisant la persistance.
la source
En ce qui
RetrieveAllStudents()
concerne la méthode, vous avez raison, elle est probablement mal placée, car vous pourriez avoir plusieurs listes d’élèves distinctes. Pourquoi ne pas simplement garder les listes en dehors de laStudent
classe?la source