Design Patterns for Data Access Layer
Ich habe eine Anwendung, die eine Datenbank (MongoDB) zum speichern von Informationen. In der Vergangenheit ich habe eine Klasse voll von statischen Methoden zum speichern und abrufen von Daten, aber ich habe da erkannt, dass dies nicht sehr Objekt-orientiert-ish oder die Zukunft gerüstet ist.
Obwohl es sehr unwahrscheinlich ist werde ich die Datenbank ändern, ich würde lieber etwas, das nicht Binde mich zu stark an "Mongo". Ich möchte auch in der Lage sein, um cache-Ergebnisse mit der option zum aktualisieren der Cache-Objekt aus der Datenbank, dies ist aber nicht zwingend und kann getan werden, in einem anderen Ort.
Ich habe mir von data access objects aber Sie scheinen nicht sehr gut definiert und ich finde keine guten Beispiele für die Implementierung (In Java oder einer ähnlichen Sprache). Ich habe auch viele man aus Fällen wie die Suche nach Benutzernamen für tab completion, die scheinen nicht gut geeignet und würde das DAO groß und aufgebläht.
Gibt es irgendwelche design patterns erleichtern würde, dass das abrufen und speichern von Objekten, ohne dass auch eine bestimmte Datenbank? Gute Beispiele für die Umsetzung wäre hilfreich (vorzugsweise in Java).
Du musst angemeldet sein, um einen Kommentar abzugeben.
Sowie das gemeinsame Konzept der Datenspeicherung in java ist, wie Sie bemerkt haben, gar nicht sehr Objekt-orientiert. Dies ist an und für sich weder schlecht oder gut: "Objekt-orientedness" ist weder Vorteil noch Nachteil, es ist nur eines von vielen Paradigmen, die manchmal hilft, mit guter Architektur und design (und manchmal nicht).
Den Grund DAOs in java sind normalerweise nicht Objekt-orientiert ist genau das, was Sie erreichen wollen - entspannen Sie Ihre Abhängigkeit auf eine bestimmte Datenbank. Besser gestaltete Sprache, die das erlaubt Mehrfachvererbung, oder natürlich, können Sie sehr elegant in einer Objekt-orientierten Art und Weise, aber mit java scheint es gerade zu sein mehr ärger als es Wert ist.
In einem weiteren Sinn, die nicht-OO-Ansatz hilft entkoppeln Sie Ihre Daten auf Anwendungsebene aus der Art, wie es gespeichert ist. Dies ist mehr als (nicht -) Abhängigkeit von den Spezifika einer bestimmten Datenbank, sondern auch die Lager-schemas ist vor allem wichtig bei der Verwendung von relationalen Datenbanken (don ' T get me started on ORM): Sie haben eine gut konzipierte relationale schema nahtlos übersetzt in der Anwendung von OO-Modell durch die DAO.
So, was die meisten DAOs sind in java sind heute im wesentlichen, was Sie zu Beginn erwähnt - Klassen, voll von statischen Methoden. Ein Unterschied ist, dass, anstatt alle Methoden statisch sind, ist es besser, eine einzelne statische "factory-Methode" (wahrscheinlich in eine andere Klasse), das gibt ein (singleton -) Instanz von DAO, die implementiert eine bestimmte Schnittstelle, verwendet durch die Anwendung code, um den Zugriff auf die Datenbank:
Warum tun es auf diese Weise, im Gegensatz zu den statischen Methoden? Nun, was ist, wenn Sie sich entschließen zu wechseln, um eine andere Datenbank? Natürlich, Sie würde erstellen Sie eine neue DAO-Klasse implementiert die Logik für den neuen Speicher. Wenn Sie wurden mit Hilfe von statischen Methoden, Sie würde nun gehen Sie durch alle Ihre code, der Zugriff auf den DAO, und ändern Sie es auf Ihre neue Klasse, richtig? Dies wäre ein großer Schmerz. Und was wäre, wenn Sie Ihre Meinung ändern und wollen, wechseln Sie zurück zu der alten db?
Mit diesem Ansatz, alles, was Sie tun müssen, ist zu ändern Sie die
GreatDAOFactory.getDAO()
und machen Sie zum erstellen einer Instanz einer anderen Klasse, und alle Ihre Anwendungs-code wird mit der neuen Datenbank ohne änderungen.Im realen Leben ist das oft getan, ohne änderungen am code, an alle: die factory-Methode wird die Implementierung der Klasse Namen per eine Eigenschaft festlegen, und instanziiert es mit Reflexion, also, alles, was Sie tun müssen, um zu wechseln-Implementierungen ist, Bearbeiten Sie eine property-Datei. Gibt es eigentlich frameworks wie
spring
oderguice
- zu verwalten, dass diese "dependency injection" - Mechanismus für Sie, aber ich werde nicht in die details gehen, Erstens, weil es ist wirklich über den Rahmen Ihrer Frage, und auch, weil ich bin nicht unbedingt davon überzeugt, dass die Vorteile, die Sie bekommen mit diesen Rahmenbedingungen ist der Mühe Wert, zu integrieren, mit Ihnen für die meisten Anwendungen.Anderen (wahrscheinlich eher in Anspruch genommen werden) profitieren von dieser "factory-Ansatz" im Gegensatz zu den statischen ist die Testbarkeit. Vorstellen, dass Sie das schreiben eines unit-test, das sollten testen Sie die Logik Ihrer
App
Klasse, unabhängig von etwaigen zugrunde liegenden DAO. Sie nicht wollen, es zu verwenden, keine realen zugrunde liegenden Speicher aus mehreren Gründen (Geschwindigkeit, dass, um es einzurichten, und reinigen Sie anschließend, mögliche Kollisionen mit anderen Prüfungen, die Möglichkeit der umweltschädliche test-Ergebnisse mit Problemen in DAO, die unabhängig vonApp
hat, was eigentlich getestet wird, etc.).Um dies zu tun, möchten Sie eine test-framework, wie
Mockito
können "mock-out" - die Funktionalität eines Objekts oder einer Methode, ersetzen Sie es mit einem "dummy" - Objekt, mit vordefinierten Verhalten (ich werde zum überspringen der details, da diese wieder über den Rahmen). So, Sie können diese dummy-Objekt ersetzen Sie Ihre DAO, und stellen Sie dieGreatDAOFactory
Gegenzug Ihre dummy anstelle der realen Sache durch den AufrufGreatDAOFactory.setDAO(dao)
vor dem test (und wiederherstellen nach). Wenn Sie wurden mit Hilfe von statischen Methoden, anstelle der Instanz der Klasse, diese würde nicht möglich sein.Einen weiteren Vorteil, die irgendwie ähnlich wie Wechsel der Datenbanken die ich oben beschrieben habe ist "Zuhälterei Ihre dao mit zusätzlicher Funktionalität. Angenommen, Ihre Anwendung wird langsamer, als die Menge der Daten in die Datenbank wächst, und Sie entscheiden, dass Sie brauchen eine cache-Ebene. Implementieren Sie eine wrapper-Klasse verwendet, die das wirkliche dao-Instanz (sofern es als Konstruktor-Parameter) auf die Datenbank zugreifen und speichert die Objekte, die er liest im Speicher, so dass Sie zurückgegeben werden können, schneller. Sie können dann Ihre
GreatDAOFactory.getDAO
instanziieren dieser wrapper, für die Anwendung zu nutzen.(Das nennt man "delegation pattern" ... und scheint Schmerzen in den Hintern, vor allem, wenn Sie viele Methoden definiert, die in Ihrem DAO: Sie implementieren müssen, um alle von Ihnen in die Hülle, auch, das Verhalten zu ändern nur einer. Alternativ könnten Sie auch einfach eine Unterklasse der dao, und caching, um es auf diese Weise. Das wäre viel weniger langweilig Codierung im Voraus, kann aber problematisch werden, wenn Sie entscheiden, tun, um die Datenbank zu ändern, oder, noch schlimmer, die haben die Möglichkeit, switching-Implementierungen hin und her.)
Einer ebenso breit verwendet wird (aber meiner Meinung nach minderwertige) alternative zu den "factory" - Methode ist, dass die
dao
eine member-variable in alle Klassen, die es brauchen:Diese Weise, den code, der erzeugt diese Klassen instanziieren muss das dao-Objekt (könnte immer noch die Fabrik), und geben Sie als parameter des Konstruktors. Der dependency injection-frameworks, die ich oben erwähnt, in der Regel tun Sie etwas ähnlich wie diese.
Diese bietet alle Vorteile der "factory method" - Ansatz, dass ich beshrieben früher, aber, wie ich schon sagte, ist nicht so gut meiner Meinung nach. Die Nachteile hier sind, dass zum schreiben eines Konstruktor für jeden Ihrer app-Klassen, tun exakt das gleiche Sache über und über, und auch nicht in der Lage zu instanziieren die Klassen leicht, wenn nötig, und einige verloren Lesbarkeit: mit einem ausreichend großen code-Basis, ein Leser des Codes, die nicht vertraut mit ihm, haben harte Zeit zu verstehen, die tatsächliche Implementierung des dao ist, wie es instanziiert ist, ob es ein singleton ist, eine thread-sichere Implementierung, ob es hält Stand, oder caches nichts, wie die Entscheidungen über die Auswahl einer bestimmten Implementierung sind gemacht usw.
boolean
für save/delete-Operationen, um anzuzeigen, dass das bestehen auf der Festplatte bzw. in der Datenbank war erfolgreich abgeschlossen.setDAO
Gegenzug die älteren/früheren DAO anstatt einfach wieder nichts? Wie soll das verwendet werden?setDAO
um den vorherigen Wert wiederherzustellen ist, so dass Sie Sie später wiederherstellen:DAO oldDao = setDAO(mock); try { doTest(); } finally { setDAO(oldDao); }