Wenn ich ein Objekt speichern und abrufen möchte, sollte ich eine andere Klasse erstellen, um damit umzugehen, oder wäre es besser, dies in der Klasse selbst zu tun? Oder vielleicht beides mischen?
Was wird nach dem OOD-Paradigma empfohlen?
Beispielsweise
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!
}
}
Gegen. (Ich weiß, das Folgende wird empfohlen, aber es scheint mir ein bisschen gegen den Zusammenhalt der Student
Klasse)
Class Student { /* */ }
Class DB {
public bool AddStudent(Student s)
{
}
public Student RetrieveStudent(Criteria)
{
}
public List<Student> RetrieveAllStudents()
{
}
}
Wie wäre es, sie zu mischen?
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
quelle
quelle
Antworten:
Grundsatz der einheitlichen Verantwortung , Trennung von Anliegen und funktionaler Zusammenhalt . Wenn Sie sich über diese Konzepte informieren, lautet die Antwort: Trennen Sie sie .
Ein einfacher Grund, die
Student
Klasse "DB" zu trennen (oderStudentRepository
gängigeren Konventionen zu folgen), besteht darin, dass Sie Ihre in derStudent
Klasse vorhandenen "Geschäftsregeln" ändern können , ohne den für die Persistenz verantwortlichen Code zu beeinflussen. umgekehrt.Diese Art der Trennung ist sehr wichtig, nicht nur zwischen Geschäftsregeln und Persistenz, sondern auch zwischen den vielen Belangen Ihres Systems, damit Sie Änderungen in nicht verwandten Modulen mit minimaler Auswirkung vornehmen können (minimal, da dies manchmal unvermeidbar ist). Es hilft, robustere Systeme zu bauen, die einfacher zu warten sind und bei ständigen Änderungen zuverlässiger sind.
Indem Sie Geschäftsregeln und Persistenz miteinander vermischen, entweder eine einzelne Klasse wie im ersten Beispiel oder
DB
eine Abhängigkeit vonStudent
, verbinden Sie zwei sehr unterschiedliche Anliegen. Es mag so aussehen, als ob sie zusammengehören. Sie scheinen zusammenhängend zu sein, weil sie dieselben Daten verwenden. Aber hier ist die Sache: Kohäsion kann nicht nur an den Daten gemessen werden, die zwischen Prozeduren geteilt werden. Sie müssen auch die Abstraktionsebene berücksichtigen, auf der sie existieren. Tatsächlich wird die ideale Art der Kohäsion beschrieben als:Und es ist klar, dass das Ausführen von Überprüfungen über einen
Student
längeren Zeitraum hinweg keine "einzelne, genau definierte Aufgabe" darstellt. Auch hier sind Geschäftsregeln und Persistenzmechanismen zwei sehr unterschiedliche Aspekte des Systems, die nach vielen Prinzipien eines guten objektorientierten Designs voneinander getrennt werden sollten.Ich empfehle, über saubere Architektur zu lesen , diese Gespräche über das Prinzip der Einzelverantwortung zu lesen (wobei ein sehr ähnliches Beispiel verwendet wird) und diese Gespräche auch über saubere Architektur zu lesen . Diese Konzepte umreißen die Gründe für solche Trennungen.
quelle
Student
Klasse sollte richtig gekapselt sein, ja? Wie kann dann eine externe Klasse die Persistenz des Privatstaates handhaben? Die Verkapselung muss gegen SoC und SRP abgewogen werden, aber es ist wahrscheinlich falsch, eine der beiden Methoden zu wählen, ohne die Kompromisse sorgfältig abzuwägen. Eine mögliche Lösung für dieses Problem ist die Verwendung von paketprivaten Zugriffsmethoden für den zu verwendenden Persistenzcode.Beide Ansätze verstoßen gegen das Prinzip der einheitlichen Verantwortung. Ihre erste Version weist der
Student
Klasse viele Verantwortlichkeiten zu und bindet sie an eine bestimmte DB-Zugriffstechnologie. Die zweite führt zu einer riesigenDB
Klasse, die nicht nur für Schüler, sondern für alle anderen Arten von Datenobjekten in Ihrem Programm verantwortlich ist. BEARBEITEN: Ihr dritter Ansatz ist der schlechteste, da er eine zyklische Abhängigkeit zwischen der DB-Klasse und derStudent
Klasse erzeugt.Wenn Sie also kein Spielzeugprogramm schreiben möchten, verwenden Sie keines davon. Verwenden Sie stattdessen eine andere Klasse wie a,
StudentRepository
um eine API zum Laden und Speichern bereitzustellen, vorausgesetzt, Sie implementieren den CRUD-Code selbst. Sie können auch die Verwendung eines ORM- Frameworks in Betracht ziehen , das die harte Arbeit für Sie erledigen kann (und das Framework erzwingt normalerweise einige Entscheidungen, bei denen die Lade- und Speichervorgänge platziert werden müssen).quelle
Es gibt einige Muster, die für die Datenpersistenz verwendet werden können. Es gibt das Muster " Arbeitseinheit", das Muster " Repository" , einige zusätzliche Muster, die wie die Remote-Fassade verwendet werden können, und so weiter.
Die meisten davon haben ihre Fans und ihre Kritiker. Oft kommt es darauf an, herauszufinden, was am besten zur Anwendung passt, und dabei zu bleiben (mit all seinen Vor- und Nachteilen, dh nicht beide Muster gleichzeitig zu verwenden ... es sei denn, Sie sind sich wirklich sicher).
Als Randnotiz: In Ihrem Beispiel sollte RetrieveStudent AddStudent statische Methoden sein (da sie nicht instanzabhängig sind).
Eine andere Möglichkeit, in der Klasse gespeicherte / geladene Methoden zu verwenden, ist:
Persönlich würde ich einen solchen Ansatz nur in relativ kleinen Anwendungen verwenden, möglicherweise für den persönlichen Gebrauch, oder wenn ich verlässlich vorhersagen kann, dass ich keine komplizierteren Anwendungsfälle haben werde, als nur Objekte zu speichern oder zu laden.
Siehe auch persönlich das Muster der Arbeitseinheit. Wenn Sie es kennenlernen, ist es in kleinen und großen Fällen wirklich gut. Und es wird von vielen Frameworks / APIs unterstützt, zum Beispiel EntityFramework oder RavenDB.
quelle
Wenn es sich um eine sehr einfache App handelt, bei der das Objekt mehr oder weniger an den Datenspeicher gebunden ist und umgekehrt (dh als eine Eigenschaft des Datenspeichers betrachtet werden kann), ist eine .save () -Methode für diese Klasse möglicherweise sinnvoll.
Aber ich denke, das wäre ziemlich außergewöhnlich.
Es ist in der Regel besser, die Klasse ihre Daten und ihre Funktionalität verwalten zu lassen (wie ein guter OO-Bürger) und den Persistenzmechanismus für eine andere Klasse oder eine Gruppe von Klassen zu externalisieren.
Die andere Option ist die Verwendung eines Persistenz-Frameworks, das die Persistenz deklarativ definiert (wie bei Annotationen), die Persistenz jedoch weiterhin externalisiert.
quelle
In Bezug auf die
RetrieveAllStudents()
Methode ist Ihr Gefühl richtig, es ist in der Tat wahrscheinlich falsch platziert, weil Sie möglicherweise mehrere unterschiedliche Listen von Studenten haben. Warum nicht einfach die Liste (n) außerhalb derStudent
Klasse lassen?quelle