Datenbankzugriffsschicht ohne mit Singletons um sich zu schmeißen?



  • aaaaaaaaaüö schrieb:

    Ob es eine oder 10 Datenbanken gibt legt man in Pflichtenheften oder ähnlichem fest.

    Es geht aber nicht um Datenbanken sondern um Verbindungen zur Datenbank. Und man kann immer mehr als eine Verbindung aufmachen wenn es denn notwendig ist.



  • SideWinder schrieb:

    Naja, das kann ja auch das DI für mich übernehmen. Ich kann ja Parameter mitgeben, bspw. will ich eine DBConn für eine bestimmte Tabelle. Das DI gibt mir dann die richtige DBConn zurück.

    Mehrere zu konfigurieren über das DI ist zumindest mit dem Spring Framework mal überhaupt kein Problem.

    Ja, aber nur wenn du vorher schon die Arbeit gemacht hast und überall den Tabellennamen angegeben hast, sonst musst du auch wieder alle Stellen entsprechend anpassen.

    Shade Of Mine schrieb:

    aaaaaaaaaüö schrieb:

    Ob es eine oder 10 Datenbanken gibt legt man in Pflichtenheften oder ähnlichem fest.

    Es geht aber nicht um Datenbanken sondern um Verbindungen zur Datenbank. Und man kann immer mehr als eine Verbindung aufmachen wenn es denn notwendig ist.

    Das man die Verbindungen nicht als Singleton macht hab ich hier schon geschreiben. http://www.c-plusplus.net/forum/p2084799#2084799 Und wenn die Anzahl begrenzt ist brauchst du ein Ding, das mitzählt. dot meinte dann, dass es mehrere Datenbanken geben kann. Warum er das dann wieder DatabaseConnection nannte, war mir nicht ganz klar, aber ich dachte, dass er Datenbanken meint, wahrscheinlich doch nicht.

    Im Prinzip läuft es aber doch eigentlich immer auf das gleiche raus, wenn etwas begrenzt ist. Irgendwo hast du ein static das mitzählt. Entweder heißt es
    DB.getInstance().getConnection() und die DB ist ein Singleton, hat einen static Zähler und gibt dir die Connection
    oder
    Framework.get("DB").getConnection(), dann hat das Framework irgendwo ein static Member, dass dir immer diese DB zurück gibt die die Connections zählt.

    Du hast aber immer einen globalen Zugriff auf die DB und damit auf die Connections und reichst nicht durch die ganze Software die DB oder die Connections rum.

    Übrigens ist Dependency Injection ganzschön doppeldeutig. Du injiziert dir damit auch ins ganze Programm Abhängigkeiten zum entsprechenden Framework.



  • SideWinder schrieb:

    Ich habe den Thread nicht verfolgt, aber generell, aus Sicht der derzeitigen SE-Theorie, ist ein Singleton hier falsch. Überall herumzureichen auch. Dafür verwendet man im Normalfall Dependency Injection.

    Ich hab eigentlich immer DI gemeint. Ich hab nur von "herumreichen" gesprochen weil der Ersteller des ursprünglichen Thread den Begriff verwendet hat. Unter DI versteh ich zumindest dass man Objekten die andere Objekte verwenden Referenzen auf diese übergibt (z.B. im Konstruktor)!? Also z.B. dass ein Objekt das irgendwas mit einer Datenbank tun soll eben im Konstruktor ne Referenz auf die entsprechende Datenbank übergeben bekommt. Genau das hab ich zumindest im andren Thread gepredigt 😉



  • Ja, aber nur wenn du vorher schon die Arbeit gemacht hast und überall den Tabellennamen angegeben hast, sonst musst du auch wieder alle Stellen entsprechend anpassen.

    Falls diese Art von Transparenz überhaupt je notwendig ist. Hat man schon den Fall, dass mehrere Datenbanken vorhanden sind weiß man auch meistens welche man haben will, also wohl soetwas:

    @Inject(PRODUCTION_DATABASE)
    DBConn conn;
    
    @Inject(BACKUP_DATABASE) // or whatever
    DBConn conn;
    

    Übrigens ist Dependency Injection ganzschön doppeldeutig. Du injiziert dir damit auch ins ganze Programm Abhängigkeiten zum entsprechenden Framework.

    Nein, siehe "non-invasive dependency injection", bspw. das Spring Framework.

    Du hast aber immer einen globalen Zugriff auf die DB und damit auf die Connections und reichst nicht durch die ganze Software die DB oder die Connections rum.

    Ja, natürlich gibt es irgendwo Zugriff auf die Datenbank. Aber in meinem letzten Projekt fand dieser nur "magically in the background" statt. Ich hatte nirgends diese Zeile von der du da redest 🙂

    MfG SideWinder



  • Ich kenne mich jetzt nicht so besonders damit aus. Aber wird DI hier jetzt als Lösung für DB dargestellt oder als allgemeine Lösung für alles Mögliche? Ein Framework nutzen zu müssen statt es selbst zu machen ist zwar irgendwo praktisch, bei mir läuten da aber immer die Alarmglocken.



  • Mal noch ne andere Frage. Was soll das eigentlich beim Testen bringen, wenn die DB-Klasse kein Singleton ist? Die DB ansich ist ja sowieso global, also alles was ich in die DB schreibe steht auch im nächsten Testdurchlauf wieder drin, egal ob Singleton oder nicht.



  • aaaaaaaaaüö schrieb:

    Die DB ansich ist ja sowieso global, also alles was ich in die DB schreibe steht auch im nächsten Testdurchlauf wieder drin, egal ob Singleton oder nicht.

    Ähm nein, du kannst z.B. Ein Mock Objekt statt einer richtigen Datenbank übergeben!?



  • @aaaaaaaaaüö:
    Wie willst du z.B. zwei Instanzen eines "Service" im gleichen Prozess laufen lassen, die unterschiedliche Datenbanken verwenden (weil es Instanzen für verschiedene Kunden sind, oder ein Testsystem + ein Stagingsystem etc.), wenn die DB-Connections von einem Singleton verwaltet werden?



  • Eisflamme schrieb:

    Ich kenne mich jetzt nicht so besonders damit aus. Aber wird DI hier jetzt als Lösung für DB dargestellt oder als allgemeine Lösung für alles Mögliche?

    DI ist eine Lösung für alles Mögliche, auch für deine Datenbank.



  • aaaaaaaaaüö schrieb:

    Mal noch ne andere Frage. Was soll das eigentlich beim Testen bringen, wenn die DB-Klasse kein Singleton ist? Die DB ansich ist ja sowieso global, also alles was ich in die DB schreibe steht auch im nächsten Testdurchlauf wieder drin, egal ob Singleton oder nicht.

    Abgesehen von Mocking kannst du natürlich die Test-Datenbank vor jedem Testdurchlauf (oder vor jedem Test) auch auf einen Ursprungszustand zurücksetzen.

    MfG SideWinder



  • hustbaer schrieb:

    @aaaaaaaaaüö:
    Wie willst du z.B. zwei Instanzen eines "Service" im gleichen Prozess laufen lassen, die unterschiedliche Datenbanken verwenden (weil es Instanzen für verschiedene Kunden sind, oder ein Testsystem + ein Stagingsystem etc.), wenn die DB-Connections von einem Singleton verwaltet werden?

    Ich weiß, dass ich mit einem Singleton nicht alles machen kann, aber ich seh überhaupt keinen Vorteil in diesem einmal Erstellen und dann herumreichen, wie es dot erklärt hat, vielleicht hat er es auch falsch erklärt und das hat nichts mit DI zu tun oder ich hab es falsch verstanden oder ich habs verstanden und finde DI schlecht.

    Nehmen wir mal an es gibt ein fertiges System das Userdaten (Name, Kontonr.) und alle möglichen anderen Daten in einer DB speichert. Jetzt soll das System aus Sicherheitsgründen so umgebaut werden, dass die Kontonr. in einer anderen Datenbanken gespeichert wird, der Rest kann in der alten DB bleiben. Wenn ich ein Singleton habe kann ich relativ einfach daraus eine Factory mit einem Parameter machen. Der Compiler sagt mir dann wo ich hinschauen muss und ich muss nur den Parameter bei der Singleton/Factroy Verwendung angeben. Wenn ich die DB jetzt immer als Parameter bei Konstruktoren angegeben hab, muss ich jetzt überall da, wo auf die Kontonr. zugegriffen wird, einen zweiten Parameter für die andere DB angeben. Ist das nicht viel hässlicher? Vorallem, gibt es beim herrumreichen, doch auch noch jede Menge "Durchschleifklassen", also Klassen die die DB eigentlich garnicht brauchen, sondern nur an eine andere weitergeben?



  • aaaaaaaaaüö schrieb:

    Wenn ich ein Singleton habe kann ich relativ einfach daraus eine Factory mit einem Parameter machen.

    D.h. anstatt eine Datenbank übergeben zu bekommen erzeugt sich einfach jeder der eine brauch eine?

    aaaaaaaaaüö schrieb:

    Wenn ich die DB jetzt immer als Parameter bei Konstruktoren angegeben hab, muss ich jetzt überall da, wo auf die Kontonr. zugegriffen wird, einen zweiten Parameter für die andere DB angeben.

    Oder noch besser: Du kapselst das alles in einem Objekt das sich darum kümmert was genau wie wohin gespeichert werden soll. Dann muss nur dieses Objekt die beiden Datenbanken kennen und der restliche Code ist davon unabhängig und bekommt nicht mit ob es jetzt 1, 2 oder 3 Datenbanken gibt oder ob vielleicht irgendwas dann auch noch wo anders gespeichert werden soll.

    aaaaaaaaaüö schrieb:

    Ist das nicht viel hässlicher?

    Nein. Singleton wäre hässlich weil es diese Abhängigkeit verschleiert. Aber nur weil du sie nicht siehst bedeutet das nicht dass sie nicht da ist.

    aaaaaaaaaüö schrieb:

    Vorallem, gibt es beim herrumreichen, doch auch noch jede Menge "Durchschleifklassen", also Klassen die die DB eigentlich garnicht brauchen, sondern nur an eine andere weitergeben?

    Nein gibt es nicht, wenn du solche Klassen hast dann machst du grad was falsch.



  • dot schrieb:

    aaaaaaaaaüö schrieb:

    Wenn ich ein Singleton habe kann ich relativ einfach daraus eine Factory mit einem Parameter machen.

    D.h. anstatt eine Datenbank übergeben zu bekommen erzeugt sich einfach jeder der eine brauch eine?

    Ne, er bekommt die die er anfordert.

    aaaaaaaaaüö schrieb:

    Wenn ich die DB jetzt immer als Parameter bei Konstruktoren angegeben hab, muss ich jetzt überall da, wo auf die Kontonr. zugegriffen wird, einen zweiten Parameter für die andere DB angeben.

    Oder noch besser: Du kapselst das alles in einem Objekt das sich darum kümmert was genau wie wohin gespeichert werden soll. Dann muss nur dieses Objekt die beiden Datenbanken kennen und der restliche Code ist davon unabhängig und bekommt nicht mit ob es jetzt 1, 2 oder 3 Datenbanken gibt oder ob vielleicht irgendwas dann auch noch wo anders gespeichert werden soll.

    Irgendwie hört sich das immer mehr nach einem ManagerManager an.

    aaaaaaaaaüö schrieb:

    Ist das nicht viel hässlicher?

    Nein. Singleton wäre hässlich weil es diese Abhängigkeit verschleiert. Aber nur weil du sie nicht siehst bedeutet das nicht dass sie nicht da ist.

    aaaaaaaaaüö schrieb:

    Vorallem, gibt es beim herrumreichen, doch auch noch jede Menge "Durchschleifklassen", also Klassen die die DB eigentlich garnicht brauchen, sondern nur an eine andere weitergeben?

    Nein gibt es nicht, wenn du solche Klassen hast dann machst du grad was falsch.

    Hast du mal ein großes Open Source Projekt zum anschauen das so aufgebaut ist, wie du das sagst?



  • aaaaaaaaaüö schrieb:

    Irgendwie hört sich das immer mehr nach einem ManagerManager an.

    Wie auch immer du jetzt darauf kommst...

    aaaaaaaaaüö schrieb:

    Hast du mal ein großes Open Source Projekt zum anschauen das so aufgebaut ist, wie du das sagst?

    Ich hab mir noch nie den Code von nem großen Open Source Projekt angetan. Also leider nicht...



  • aaaaaaaaaüö schrieb:

    hustbaer schrieb:

    @aaaaaaaaaüö:
    Wie willst du z.B. zwei Instanzen eines "Service" im gleichen Prozess laufen lassen, die unterschiedliche Datenbanken verwenden (weil es Instanzen für verschiedene Kunden sind, oder ein Testsystem + ein Stagingsystem etc.), wenn die DB-Connections von einem Singleton verwaltet werden?

    Ich weiß, dass ich mit einem Singleton nicht alles machen kann, aber ich seh überhaupt keinen Vorteil in diesem einmal Erstellen und dann herumreichen, wie es dot erklärt hat, vielleicht hat er es auch falsch erklärt und das hat nichts mit DI zu tun oder ich hab es falsch verstanden oder ich habs verstanden und finde DI schlecht.

    Nehmen wir mal an es gibt ein fertiges System das Userdaten (Name, Kontonr.) und alle möglichen anderen Daten in einer DB speichert. Jetzt soll das System aus Sicherheitsgründen so umgebaut werden, dass die Kontonr. in einer anderen Datenbanken gespeichert wird, der Rest kann in der alten DB bleiben. (...)

    Ich wollte mit dem Beispiel versuchen dir klarzumachen, von welchen "es könnte mehrere Datenbanken geben" Fällen wir sprechen (oder zumindest ich spreche).

    Was du beschreibst, wird durch DI/rumreichen/wie-auch-immer-man-es-nennen-will weder einfacher noch schwerer.

    Was ich meine dagegen schon.



  • Wie macht ihr das mit einem Logger? Macht ihr ein Singleton oder gebt ihr den an praktisch jede Klasse weiter?



  • Logger... schrieb:

    Wie macht ihr das mit einem Logger? Macht ihr ein Singleton oder gebt ihr den an praktisch jede Klasse weiter?

    Falls Du Debug-Logger meinst, nicht weitergeben.
    Singleton oder nicht? Die Frage stellt sich nicht wirklich, da per Makro geloggt wird, zum Beispiel

    #define LOG(...) do{std::ofstream("log.txt",ios::app)<<__VA_ARGS__;}while(false)
    

    Das kann jederzeit so leicht umgestellt werden von so auf globale Variable oder auf Singleton, daß man eigentlich keine Fehlentscheidung treffen kann.



  • Was bring do...while(false); ?



  • Und was machst du in Sprachen die keine Makros haben, z.b. Java?



  • Logger... schrieb:

    Was bring do...while(false); ?

    Schiebt das ganze in einen Block, verhindert bspw. das Loggen an "unüblichen" Bereichen wo das Makro schaden anrichten könnte.

    @Java: Dort würde man eine fertige Bibliothek nehmen. Da gibt es sogar zwischen den Bibliotheken eine Quasi-Standardisierung indem das SLF4J-Interface implementiert wird: http://www.slf4j.org/ Dort funktioniert das über eine LoggerFactory die afaik ein Singleton ist.

    MfG SideWinder


Anmelden zum Antworten