Datenbankzugriffsschicht ohne mit Singletons um sich zu schmeißen?



  • bewertung schrieb:

    dot schrieb:

    bewertung schrieb:

    [...] oder zwei "Singletons" erstelle...

    Was genau meinst du damit!?

    // main
    Logger loggerDerEigentlichDerEinzigeSeinSoll.
    foo(loggerDerEigentlichDerEinzigeSeinSoll)
    
    //... viel Code 
    //... ganz anderer Klasse
    
    Logger loggerDerZweite.
    foo(loggerDerZweite)
    

    Ähm, ich hoffe dir ist klar dass die Tatsache dass genau das problemlos möglich ist doch gerade einer der wesentlichen Vorteile von DI ist!? Abgesehen davon seh ich da keine Singletons...



  • bewertung schrieb:

    CStoll schrieb:

    Also gut, sagen wir mal ich will deinen Singleton für einen Logger verwenden und unter anderem die Konstruktion meiner Objekte loggen lassen.

    a) wo muß ich den createInstance()-Aufruf unterbringen, damit ein globales Objekt erfolgreich (d.h. ohne assert/Crash) dieser Klasse angelegt werden kann.
    b) Wer ist für den createInstance()-Aufruf zuständig, wenn mehrere Klassen diesen Logger verwenden wollen?

    Relativ am Anfang von main, so ähnlich wie wenn du ihn mit DI überall hin weitergeben willst.

    Dir ist klar, daß globale Objekte angelegt werden, bevor du in die main() kommst? Und daß der Autor der main()-Funktion möglicherweise gar nicht weiß, daß ich tief in den Innereien meiner Library den Singleton-Logger verwende?

    Und was passiert, wenn der Aufruf mehrfach passiert?

    Assert, crash, nix, was man halt in die createInstance() rein macht.

    Also muß diese Funktion genauso geschützt werden wie die getInstance() bei einem "normalen" Singleton. Aber dadurch, daß sie explizit aufgerufen wird, vergrößerst du nur die Probleme des Singleton, ohne damit einen erkennbaren Nutzen zu bieten.

    @Zeus/dot: Kann schon sein, daß ich das bereits unbewußt angewendet habe. Darüber muß ich noch etwas meditieren.



  • SideWinder schrieb:

    Mit DI werden die Objekte im Normalfall von einem Framework erzeugt und nicht von dir selbst, dementsprechend wirst du nie unabsichtlich zwei erzeugen.

    MfG SideWinder

    dot schrieb:

    bewertung schrieb:

    dot schrieb:

    bewertung schrieb:

    [...] oder zwei "Singletons" erstelle...

    Was genau meinst du damit!?

    // main
    Logger loggerDerEigentlichDerEinzigeSeinSoll.
    foo(loggerDerEigentlichDerEinzigeSeinSoll)
    
    //... viel Code 
    //... ganz anderer Klasse
    
    Logger loggerDerZweite.
    foo(loggerDerZweite)
    

    Ähm, ich hoffe dir ist klar dass die Tatsache dass genau das problemlos möglich ist doch gerade einer der wesentlichen Vorteile von DI ist!? Abgesehen davon seh ich da keine Singletons...

    Sell dich nicht so...?!?!?!? 🙄

    CStoll schrieb:

    bewertung schrieb:

    CStoll schrieb:

    Also gut, sagen wir mal ich will deinen Singleton für einen Logger verwenden und unter anderem die Konstruktion meiner Objekte loggen lassen.

    a) wo muß ich den createInstance()-Aufruf unterbringen, damit ein globales Objekt erfolgreich (d.h. ohne assert/Crash) dieser Klasse angelegt werden kann.
    b) Wer ist für den createInstance()-Aufruf zuständig, wenn mehrere Klassen diesen Logger verwenden wollen?

    Relativ am Anfang von main, so ähnlich wie wenn du ihn mit DI überall hin weitergeben willst.

    Dir ist klar, daß globale Objekte angelegt werden, bevor du in die main() kommst? Und daß der Autor der main()-Funktion möglicherweise gar nicht weiß, daß ich tief in den Innereien meiner Library den Singleton-Logger verwende?

    Dann würde auch die von dot verwendete Constructor-Injection nicht gehen.

    Und was passiert, wenn der Aufruf mehrfach passiert?

    Assert, crash, nix, was man halt in die createInstance() rein macht.

    Also muß diese Funktion genauso geschützt werden wie die getInstance() bei einem "normalen" Singleton. Aber dadurch, daß sie explizit aufgerufen wird, vergrößerst du nur die Probleme des Singleton, ohne damit einen erkennbaren Nutzen zu bieten.

    Nutzen siehe oben.

    Ich hab jetzt keine Lust mehr alles dreimal zu erklären.



  • bewertung schrieb:

    Sell dich nicht so...?!?!?!? 🙄

    😕

    bewertung schrieb:

    Dann würde auch die von dot verwendete Constructor-Injection nicht gehen.

    Wo liegt das Problem mit der Injection?

    bewertung schrieb:

    Ich hab jetzt keine Lust mehr alles dreimal zu erklären.

    Geh doch einfach auf die genannten Punkte ein...



  • bewertung schrieb:

    Dir ist klar, daß globale Objekte angelegt werden, bevor du in die main() kommst? Und daß der Autor der main()-Funktion möglicherweise gar nicht weiß, daß ich tief in den Innereien meiner Library den Singleton-Logger verwende?

    Dann würde auch die von dot verwendete Constructor-Injection nicht gehen.

    Wieso das? In meiner Library kann ich machen, was ich will, also kann ich auch sowas schreiben:

    Logger myLog(...);
    MyClass Worker(myLog);
    

    Da muß ich mich nicht davon abhängig machen, daß irgendjemand anderes (der vielleicht gar nichts von der inneren Arbeitsweise meines Codes weiß) mir einen Logger zur Verfügung stellt bzw. initialisiert?

    Und was passiert, wenn der Aufruf mehrfach passiert?

    Assert, crash, nix, was man halt in die createInstance() rein macht.

    Also muß diese Funktion genauso geschützt werden wie die getInstance() bei einem "normalen" Singleton. Aber dadurch, daß sie explizit aufgerufen wird, vergrößerst du nur die Probleme des Singleton, ohne damit einen erkennbaren Nutzen zu bieten.

    Nutzen siehe oben.

    Der Nutzen besteht darin, daß die getInstance()-Methode sich verkürzt und keine Synchronisation etc benötigt, habe ich das richtig verstanden?
    Dafür benötige ich jetzt eine andere Methode, die ihre Synchronisation- und ähnlichen Probleme erbt - und muß auch noch dafür sorgen, daß jemand anderes diese Methode aufruft. Irgendwie habe ich das Gefühl, daß da die Nachteile überwiegen.
    (wobei ich nichtmal ein Verfechter von "normalen" Singletons bin - ich bin schon oft genug über global gültige Variablen in den unterschiedlichsten Verkleidungen gestolpert)



  • Zeus schrieb:

    CStoll schrieb:

    PS: Ich will hier nicht die Vorteile der DI verteidigen, dazu kenne ich das Konzept zu wenig.

    Doch kennst du, ich versteh blos nicht, dass es unter eigenen Begiff gehypt wird 😉

    Gehypt wird glaub ich mehr DI mit Frameworks und entsprechenden Konfigurationsmöglichkeiten und nicht die simple Konstruktor-Injection die dot hier so hypt.



  • Gibt es hier eigentlich irgendjemand, außer dot, der Logger und DB manuell per Konstruktor-Injection weiter gibt, oder verwendet ihr alle DI Frameworks dazu?



  • In den C#/ASPX Projekten (habe ich Anfang des Jahres von einem Ex-Kollegen geerbt) habe ich Framework-basierte DI (d.h. durch Austausch/Bearbeiten einer XML-Datei kann ich die verwendeten Klassen austuaschen und Parameter setzen).
    In den SAL-Projekten, die die überwiegende Mehrheit bei uns ausmachen, läuft es wohl auf eine Art Header-basierte DI hinaus (auch wenn es dort keine Trennung zwischen Header und Implementationsdatei gibt).
    C++ habe ich seit einer Weile nicht mehr produktiv und im größeren Stil verwendet.

    (Randfrage: Der Umgang der STL mit Allokatoren und Traits-Klassen dürfte doch auch in den Bereich DI fallen, oder?)



  • Ich verstehe gar nicht, wieso hier um createInstance gestritten wird, kann man nicht auch so die if-Abfrage sparen und Threadsicherheit erzeugen:

    class TestSingleton
    {
    private:
    	static TestSingleton st;
    
    	int z;
    
    	TestSingleton() {z = 100;}
    	TestSingleton(const TestSingleton&);
    
    public:
    	static TestSingleton& getInstance()
    	{
    		return st;
    	}
    
    	int getZ() const {return z;}
    };
    
    TestSingleton TestSingleton::st ;
    

    Das kompiliert bei mir problemlos. Nachteil ist, dass es immer instanziiert wird und nicht bei Verwendung. Aber je nach Anwendungszweck finde ich das nicht so dramatisch.



  • Eisflamme schrieb:

    Ich verstehe gar nicht, wieso hier um createInstance gestritten wird, kann man nicht auch so die if-Abfrage sparen und Threadsicherheit erzeugen:

    Thread-Sicherheit: Ja, das Problem bei diesem Ansatz ist die Initialisieurngs-Reihenfolge von globalen Objekten.



  • Hm, verstehe... Und so was?

    class TestSingleton
    {
    private:
    	static TestSingleton st;
    
    	int z;
    
    	TestSingleton() {z = 100; std::cout << "Singleton initialized.";}
    	TestSingleton(const TestSingleton&);
    
    	friend TestSingleton& getSingletonInstance();
    
    public:
    	int getZ() const {return z;}	
    };
    
    TestSingleton& getSingletonInstance()
    {
    	static TestSingleton ts;
    	return ts;
    }
    

    Hm und die Übergabe von Traits und Allokatoren passt für DI imo nicht ganz, weil man nur Klassen, aber keine Objekte übergibt. Großer Vorteil der DI ist aber, dass man die Initialisierung dem Aufrufer überlässt. Man löst dadurch eher die dynamische Polymorphie ab, finde ich.



  • Eisflamme schrieb:

    Hm, verstehe... Und so was?

    Das hat auch wieder ein verstecktes if (in der Umsetzung der static-Variablen) 😉
    Aber wir müssen jetzt nicht alle Variationen eines Singleton durchgehen, dafür gibt es schon genug Fachlitertur, die sich mit den Vor- und Nachteilen der einzelnen Varianten beschäftigen. Ich bin bis jetzt zumindest davon ausgegangen, daß es hier um Möglichkeiten zur kompletten Vermeidung von Singletons geht.

    Hm und die Übergabe von Traits und Allokatoren passt für DI imo nicht ganz, weil man nur Klassen, aber keine Objekte übergibt. Großer Vorteil der DI ist aber, dass man die Initialisierung dem Aufrufer überlässt. Man löst dadurch eher die dynamische Polymorphie ab, finde ich.

    Wenn du es auf die Spitze treiben willst, kannst du auch parametrisierte Allokatoren schreiben und als Konstruktor-Parameter an die STL-Container übergeben 😉
    Aber meine Randfrage sollte hauptsächlich ausloten, wo die Experten (dot/Zeus/...) anfangen, von DI zu sprechen.



  • CStoll schrieb:

    Ich bin bis jetzt zumindest davon ausgegangen, daß es hier um Möglichkeiten zur kompletten Vermeidung von Singletons geht.

    Ja ich auch...

    CStoll schrieb:

    Aber meine Randfrage sollte hauptsächlich ausloten, wo die Experten (dot/Zeus/...) anfangen, von DI zu sprechen.

    ich bin ganz sicher kein Experte was das Thema angeht. Abgesehen davon hast du recht, die Idee hinter den STL Allokatoren ist sicher sowas ähnliches wie DI. Nur passiert das dort auf Typebene und nicht auf Objektebene, ka inwiefern man da noch von DI sprechen würde.



  • dot schrieb:

    ich bin ganz sicher kein Experte was das Thema angeht. Abgesehen davon hast du recht, die Idee hinter den STL Allokatoren ist sicher sowas ähnliches wie DI. Nur passiert das dort auf Typebene und nicht auf Objektebene,

    Nee, genau genommen passiert es auf Objektebene. Nicht nur Alloaktoren, auch zum Beispiel der Vergleicher in std::sort.
    Das Objekt ist allerdings in der Praxis meistens leer, da muß ich Dir Recht geben, und nur sein Typ wird verwendet. Muß aber nicht sein. Objekte, die zum Beispiel zur Laufzeit erst wissen, nach welchem Attribut sortiert werden soll, habe ich schon gesehen. Da ist aber dann auch vom Typ her klar, daß es ein Vergleicher ist, denn er hat den passenden operator<. Oder Allokatoren, die Objekteigenen Speicher haben, das geht ein wenig in Richtung memory pools.
    Wir machen von den Objekten die Typen aber nie ganz weg. Wir benutzen keine Basisklasse Object für alles und kein

    struct IComparer{
       virtual bool less(Object const& a,Object const& b);
       friend bool operator<(...//nur nötig, weil std::-Zeugs so pingelig ist
    

    , obwohl das ginge.



  • Versteh ich nicht. Man übergibt doch nur den Typen. Und wenn der genutzt werden soll, wird der lokal instanziiert.



  • Eisflamme schrieb:

    Man übergibt doch nur den Typen.

    Nicht wirklich.
    http://www.cplusplus.com/reference/stl/map/map/



  • Stimmt, hab vergessen dass man den Allocator/Comparator ja tatsächlich als Instanz übergibt, hab mir für einen Moment eingebildet die wären mehr nur ne Art Policy. Aber so kann man imo natürlich von einer Art Dependency-Injection reden, ist ja einfach nur statischer statt dynamischer Polymorphismus...



  • Oh, ich Fisch, dann passt DI.


Anmelden zum Antworten