Suche geeignetes Design Pattern



  • Hi zusammen,

    ich bin nicht sicher ob das hier das richtige Unterforum ist, da es sich um eine allgemein gehaltene Frage handelt. Da ich in C++ programmiere, stelle ich meine Frage einfach mal hier.

    Ich beschäftige mich erst seit kurzem mit OO und habe Probleme für folgende Aufgabe das richtige Pattern zu finden:

    Es existieren zwei Klassen ClassA und ClassB mit unterschiedlichen Attributen und Operationen.

    Ich möchte jetzt ein übergeordneten Klasse erstellen, der man als Client die Parameter A, B oder AB übergibt und die je nach Buchstabenkombination das Ergebnis von A, B oder A+B zurück gibt. Darüber hinaus muss die Klasse den Parametersatz vom Client annehmen und sinnvoll an A und B verteilen.

    Welches Pattern empfiehlt sich da? Derzeit tendiere ich zum Decorator bin mir jedoch nicht sicher ob ich dann in die falsche Richtung laufe.



  • Ich versteh nicht, was du willst und wegen der vielen Antworten vermute ich, dass es anderen ähnlich geht.



  • Ja, ist wirklich nicht so einfach. In welche Richtung möchtest Du denn Flexibilität erzeugen? Möchtest Du beliebig viele Operationen hinzufügen können, wobei nach Kriterien von A und B eben jeweils ein anderer zum Einsatz kommt? Dann musst Du aber von der Verwaltungsklasse natürlich trotzdem ständig den Code ändern. Außerdem wäre interessant, wie die Parameter aussehen, anhand deren entschieden wird, welche Operationen ausgeführt werden.

    Ich habe Mal angefangen Code hier reinzutippen, dann ist mir aufgefallen, dass mein Code einfach exakt gar nichts aussagen würde. Es gibt zu viele Möglichkeiten es zu gestalten, die beste hängt von den Details ab.



  • Ich bastel mal Code um das Problem besser zu beschreiben 🙂

    Dauert jedoch etwas 😃

    Ich werde das ganze mal so programmieren, dass es funktioniert, aber sehr sehr unelegant und ineffizient ist.



  • Okay, hat etwas gedauert:

    #include "stdafx.h"
    #include <iostream>
    
    using namespace std;
    
    struct ParameterA {
      bool a;
      bool b;
      bool c;
    };
    
    class ClassA {
    private:
      struct ParameterA parameter_;
    public:
      void setParameter(struct ParameterA &parameter) {
        parameter_ = parameter;
      }
    
      int operate(int &input) {
        int output = 0;
    
        if(parameter_.a == true) output = output + 2*input;
        if(parameter_.b == true) output = output + 3*input;
        if(parameter_.c == true) output = output + 4*input;
    
        return output;
      }
    };
    
    struct ParameterB {
      int x;
      int y;
      int z;
    };
    
    class ClassB {
    private:
      struct ParameterB parameter_;
    public:
      void setParameter(struct ParameterB &parameter) {
        parameter_ = parameter;
      }
    
      int operate(int &input) {
        int output = 0;
        output = input * (parameter_.x + parameter_.y + parameter_.z);
        return output;
      }
    };
    
    enum Algorithmus { AlgoA, AlgoB, AlgoAB };
    
    struct ParameterVerwalter {
      bool a;
      bool b;
      bool c;
      int x;
      int y;
      int z;
      Algorithmus algorithmus;
      int limit;
    };
    
    class Verwalter {
    public:
      ~Verwalter() {
        delete classA;
        delete classB
      }
    
      void initialize(struct ParameterVerwalter &parameter) {
        algorithmus_ = parameter.algorithmus;
        limit_ = parameter.limit;
    
        struct ParameterA parameterA;
        struct ParameterB parameterB;
    
        switch( algorithmus_ ) {
        case AlgoA:
          classA = new ClassA();
          parameterA.a = parameter.a;
          parameterA.b = parameter.b;
          parameterA.c = parameter.c;
          classA->setParameter(parameterA);
          break;
        case AlgoB:
          classB = new ClassB();
          parameterB.x = parameter.x;
          parameterB.y = parameter.y;
          parameterB.z = parameter.z;
          classB->setParameter(parameterB);
          break;
        case AlgoAB:
          classA = new ClassA();
          parameterA.a = parameter.a;
          parameterA.b = parameter.b;
          parameterA.c = parameter.c;
          classA->setParameter(parameterA);
          classB = new ClassB();
          parameterB.x = parameter.x;
          parameterB.y = parameter.y;
          parameterB.z = parameter.z;
          classB->setParameter(parameterB);
          break;
        }
      }
    
      int operate(int &input) {
        int output = 0;
    
        switch(algorithmus_) {
          case AlgoA:
          output = classA->operate(input);
          break;
        case AlgoB:
          output = classB->operate(input);
          break;
        case AlgoAB:
          output = classA->operate(input) + classB->operate(input);
          break;
        }
        if(output > limit_) output = limit_;
        if(output < -limit_) output = - limit_;
    
        return output;
      }
    
    private:
      Algorithmus algorithmus_;
      int limit_;
    
      ClassA *classA;
      ClassB *classB;
    };
    

    Der dazugehörige Aufrufer sieht so aus:

    #include "stdafx.h"
    #include <iostream>
    #include "Verwalter.h"
    
    using namespace std;
    
    int main() {
      Verwalter verwalter;
      ParameterVerwalter parameter;
    
      parameter.a = true;
      parameter.b = true;
      parameter.c = true;
      parameter.x = 2;
      parameter.y = 3;
      parameter.z = 4;
      parameter.limit = 100;
      parameter.algorithmus = AlgoAB;
      verwalter.initialize(parameter);
    
      int input = 1;
      int output = 0;
    
      output = verwalter.operate(input);
    
      cout << "output: " << output << endl;
    	B
      return 0;
    }
    

    Ich hoffe der Code macht es etwas klarer wie es aussehen soll. Das switch/case Gedönse ist einfach sehr sehr unschön und ich würde es gerne eleganter lösen. Zumal es sein könnte, dass in Zukunft nicht nur A, B und AB existieren, sondern C, AC, BC, ABC... dann das D... 😉





  • Nachtrag: Halte die Strategies in einer Liste.

    Und noch wichtiger: Du vermischst hier C (das struct vor dem Typ kann man weglassen) mit Java (new ohne Grund und ohne delete) und nennst das C++. Lies am besten Effective C++ durch und programmiere erst dann weiter.



  • Nein, nicht new ohne delete, sondern delete ohne new. Entschuldigung für den Vertauscher.



  • Mit

    void setParameter(struct ParameterA &parameter);
    

    und

    int operate(int &input);
    

    signalisierst du dem Benutzer, dass du vorhast, den inhalt der übergebenen Argumente innerhalb der Funktion zu ändern, tust du ja aber gar nicht.

    void setParameter(const ParameterA& parameter);
    

    wäre also schöner.
    operate kannst du auch so machen:

    int operate(int input);
    

    da es bei ints keinen Sinn hat, sie per ref to const zu übergeben.
    Achja, ist das jetzt schon der dritte Thread, indem du durch deinen Memleakerei die Leute dabei behinderst, dir bei deiner eigentlichen Frage zu helfen? 😉
    Ok, der gute Wille mit dem delete im dtor ist da, passt aber nicht. Die Zeiger könnten nicht initialisiert sein. Mach aus void initialize einen ctor, benutze initializer lists, usw. Mit anderen Worten: Lern C++. Du wirst wenig Spaß am Rumdesignpattern haben, wenn die Basics noch nicht sitzen.



  • Ich würde auch sagen, dass es grob nach dem Strategy Pattern ausschaut, aber so wirklich tief hab ich mich da jetzt nicht reingedacht.



  • Erstmal danke an alle, die mir den Ratschlag geben erstmal C++ zu lernen. In manchen Situationen hat man jedoch nicht die Möglichkeit sich gemütlich und in Ruhe in eine Materie einzulesen und fängt in der Mitte an, schaut dann ans Ende um anschließend am Anfang zu gucken was man alles falsch macht. Nennt man Softskilltraining by the job 😉

    Die deletes waren mehr oder weniger eine Verlegenheitstat. Da ich nicht wirklich weiß ob ich ClassA und ClassB benötige, habe ich sie nicht im Konstruktor initialisiert. Eigentlich sollen nur Objekte einer Klasse erzeugt werden, die auch benötigt werden.

    Ich müsste einfach mal gucken welche Objekte auch wirklich vorhanden sind und diese zerstören.



  • Davon abgesehen, dass Du Smartpointer für so was verwenden solltest, kannst Du es so gestalten:

    class Class
    {
    private:
        A* a;
        B* b;
    
        // Bei verwendetem destructor operator= und copy-ctor privatisieren
        // oder überschreiben
        Class& operator=(const Class&);
        Class(const Class&);
    
    public:
        Class();
        void methThatNeedsA();
        void methThatNeedsB();
        ~Class();
    };
    
    Class::Class() : a(nullptr), b(nullptr) {}
    
    void Class::methThatNeedsA()
    {
        if(!a)
            a = new A;
        // ...
    }
    
    void Class::methThatNeedsB()
    {
        if(!b)
            b = new B;
        // ...
    }
    
    void Class::~Class()
    {
        delete a; // delete auf nullptr hat keinen Effekt, ist also ok
        delete b;
    }
    


  • Template Method könnte auch eine Möglichkeit sein. Der Unterschied zu Strategy ist fein, aber Designmässig könnte eine Unterscheidung Sinn machen.



  • Zisko schrieb:

    Erstmal danke an alle, die mir den Ratschlag geben erstmal C++ zu lernen. In manchen Situationen hat man jedoch nicht die Möglichkeit sich gemütlich und in Ruhe in eine Materie einzulesen und fängt in der Mitte an, schaut dann ans Ende um anschließend am Anfang zu gucken was man alles falsch macht. Nennt man Softskilltraining by the job 😉

    Soft Skills kenn ich nur als soziale Kompetenz, aber gut, das ist ja nebensächlich.
    Du programmierst noch nicht lange (auch nicht Java), oder? Weil spätestens nach einigen Jahren merkt man normalerweise, dass man mit dem Ansatz eigentlich nur mehr Zeit benötigt, und dafür schlechtere Ergebnisse bekommt, auch wenn es anfangs den Anschein hat, man würde schneller voran kommen. 🙂



  • Dobi schrieb:

    Zisko schrieb:

    Erstmal danke an alle, die mir den Ratschlag geben erstmal C++ zu lernen. In manchen Situationen hat man jedoch nicht die Möglichkeit sich gemütlich und in Ruhe in eine Materie einzulesen und fängt in der Mitte an, schaut dann ans Ende um anschließend am Anfang zu gucken was man alles falsch macht. Nennt man Softskilltraining by the job 😉

    Soft Skills kenn ich nur als soziale Kompetenz, aber gut, das ist ja nebensächlich.
    Du programmierst noch nicht lange (auch nicht Java), oder? Weil spätestens nach einigen Jahren merkt man normalerweise, dass man mit dem Ansatz eigentlich nur mehr Zeit benötigt, und dafür schlechtere Ergebnisse bekommt, auch wenn es anfangs den Anschein hat, man würde schneller voran kommen. 🙂

    Bedingt. Natürlich fällt man mit C++ die ganze Zeit auf die Nase, wenn man das nicht kann. Aber stell dir einfach mal vor, du bist Vollzeitjavaentwickler mit 10 Jahren Berufserfahrung und hast noch nie was mit C++ gemacht. Dann kommt dein Chef und sagt, unser C++ Entwickler hat gekündigt, und jetzt musst du SOFORT sein Projekt übernehmen und 20 Sachen fixen und 5 neue Features einbauen. Dann wirst du schlecht sagen können, ok, aber erstmal brauch ich ein halbes Jahr, um mich richtig in C++ einzulesen...



  • Der Chef muss halt dringend einen neuen C++-Programmierer besorgen, für so was gibt es Kündigungsfristen. Wenn der Javaentwickler plötzlich die ganzen C++-Projekte übernimmt, baut er überall Speicherfehler ein und das Teil bricht dann ständig zusammen... ziemlich heftig, wenn der Chef das toleriert bzw. das so bestimmt. Imo sollte der Chefchef sich Gedanken machen, ob der Chef die richtige Stelle besetzt...



  • Das war nur ein Beispiel als Antwort auf Dobis Aussage. Wollte damit nur sagen, falls der TE ein erfahrener Entwickler ist, könnte es trotzdem passieren, dass er in eine Situation kommt, wo er schnell was machen muss, ohne sich auszukennen.

    Im übrigens ist es überhaupt nicht unrealistisch, dass sowas passiert. Ich hab z.B. Sachen in Python anpassen müssen, obwohl ich kein Python konnte. Hab auch nicht gesagt, erstmal paar Monate einlesen... Python ist zwar nicht C++, aber die Wahrscheinlichkeit, dass man in so eine Situation kommt ist schon gegeben.

    So, genug offtopic. Das Threadthema an sich ist nicht so uninteressant, hier ständig irgendwas anderes auszudiskutieren 🙂


Anmelden zum Antworten