Design Problem mit Dependency Injection



  • Hallo,
    ich bräuchte mal eine Lösung für mein Design Problem. Es geht um folgendes

    class Service{
    public:
        Service(Broker & a,Broker &b) : brokerA(a),brokerB(b) { }
        ~Service(){
             brokerA.disconnect();
             brokerB.disconnect();  
        }
    private:
        Broker & brokerA;
        Broker & brokerB;
    };
    
    int main(){
          Broker a(...);
          Broker b(...);
          std::shared_ptr<Service> service = make_shared<Service>(a,b);
    }
    

    Hierbei ist jetzt alles gut Programm verhält sich wie erwartet. Wenn ich aber jetzt folgendes mache,

          std::shared_ptr<Service> service 
          Broker a(...);
          Broker b(...);
         service = make_shared<Service>(a,b);
    

    stürzt das Programm mit einem Speicherzugriffs Fehler ab. Es ist auch klar wieso... Die Destruktoren von a und b werden vor dem des Service aufgerufen. Für mich ist jetzt die Frage wie kann ich jetzt mein Design verbessern, dass die Reihenfolge der Deklarationen keine Rolle spielt.

    Grüße,



  • Broker a(...);
    Broker b(...);
    Service service(a,b);
    


  • Das Grundproblem ist, dass du Referenzen als Parameter nimmst und speicherst. Das ist immer etwas fehleranfällig, da man auch beim Erstellen des Service nicht sieht, dass die Parameter als Referenzen gespeichert werden.

    Frage: Sollte dein Service ggf. die beiden Broker besitzen? Du könntest anstelle von Broker &a einen std::unique_ptr<Broker> als Parameter & Membervariable nehmen. Nachteil: dann gehen die Broker nicht als Stack-Variablen. Aber da du Service eh mit make_shared anlegst, nehme ich an, dass dieser lange lebt?

    Noch ne andere Frage: warum ist der Service dafür überhaupt zuständig, die Broker-Connection zu schließen? Sollte das nicht das Broker-Objekt selbst machen?



  • @wob
    Ja eigentlich würde ich dir zustimmen, dass die Broker die connection selber schliessen sollten. Hätte in diesem Fall zur Folge, dass die andere Reihenfolge erforderlich ist. Nämlich zuerst müssten zwingend die Broker zerstört werden. Da der Service in einem Thread blockiert und wenn das Objekt nicht zerstört wird, wartet der Thread bis in alle Ewigkeit auf das beenden der Verbindung.
    Aber mit der Lösung des unique_ptr bin ich gerade dabei, das Interface zu ändern. Da mir auch nichts besseres eingefallen ist.

    Ich weiß nur nicht, ob ich nicht eher ein Design Problem habe und vielleicht habt ihr ja noch ein wenig Input das mich zu einem besseren Design führen könnte.



  • Wenn der Service die beiden Broker als unique_ptr hält könnte der vlt auch direkt die Broker als Member haben, oder? Dann könnten die auch beide ihre Verbindung selbst schließen, wenn sie zerstört würden und das würde automatisch dann passieren, wenn der Destruktor vom Service läuft.



  • @Schlangenmensch
    Hierbei liegt das Problem, dass ich ich die Abhängigkeit nicht explizit im Service halten möchte, um z.B. meine Service klasse testbar zu halten.
    Ich hatte auch daran gedacht, die Klasse als Template zu schreiben und so den Broker Typ hinein zu bringen. Also das Policy Design zu verwenden. Aber ich finde, sowas nicht wirklich schön. Da ich beim Testen, dann irgendwie an die Objekte kommen muss.



  • @manni66
    Ich weiß, dass das Problem hier ein wenig exotisch wirkt. Wen ich direkt auf dem Stack anlegen würde, wäre alles in Ordnung. Da ich keine andere Reihenfolge erzeugen könnte.
    Allerdings habe ich beim testen festgestellt, dass die Reihenfolge auf einmal wichtig wurde. Dies ist für mich immer ein Zeichen, dass das Design mist ist und ich irgendetwas anders machen sollte. Oder sehe ich das falsch?



  • @pmqtt Also stellt Broker nur das Interface bereit? Damit hatte ich aufgrund der Übergabe als Referenz nicht gerechnet.
    Ich würde das dann wahrscheinlich als Template realisieren, aber als unique_ptr Member wie von @manni66 vorgeschlagen geht natürlich auch, dann kann auch alles für sich aufräumen.



  • @pmqtt
    Das Vermischen von Stack- und Heapallocation ist hier meiner Meinung nach das grösste Designproblem. Entscheide dich für eines Variante.



  • Ähm.

    auto a = make_shared<Broker>(...);
    auto b = make_shared<Broker>(...);
    auto service = make_shared<Service>(a,b);
    

    ?

    Bzw. wenn das Service der einzige Benutzer der beiden Broker ist dann natürlich make_unique<Broker>.



  • @pmqtt sagte in Design Problem mit Dependency Injection:

    Nämlich zuerst müssten zwingend die Broker zerstört werden. Da der Service in einem Thread blockiert und wenn das Objekt nicht zerstört wird, wartet der Thread bis in alle Ewigkeit auf das beenden der Verbindung.

    Verwendest du etwa das Schliessen eines Sockets um einen blockierenden accept/recv Aufruf o.Ä. zu beenden? Das ist nämlich ganz grob falsch nur so nebenbei.

    Davon abgesehen: du kannst natürlich wenn du unique_ptr verwendest die Broker im Service zerstören. Oder, alternativ gäbe es noch die Möglichkeit der Broker Klasse eine "shutdown" Funktion zu spendieren. Würde ich aber nur machen wenns wirklich nötig ist, so "shutdown" Funktionen erzeugen immer einen greislichen Zombie-Zustand mit dem dann alle weiteren Memberfunktionen klar kommen müssen.


Log in to reply