Interfaces und Paramter



  • Hi,

    ich bin neu hier und hab auch gleich ein konkretes Problem, das mir schon öfters begegnet ist.

    Ich habe folgendes Interface:

    class Abstract
    {
    public:
      DoSomething();
    };
    

    und davon zwei konkrete Klassen

    class Konkret1
    {
    public:
      DoSomething();
      SetDoubleParam(double);
    };
    
    class Konkret2
    {
    public:
      DoSomething();
      SetStringParam(string);
    }
    

    Die beiden konkreten Klassen habe unterschiedliche Parameter, die von aussen änderbar sein sollen. Dadurch verlieren die beiden Klassen aber ihr einheitliches Interface was im weiteren Programmverlauf zu Komplikation führen kann.

    Habt ihr da irgendwelche Ideen oder Erfahrungen wie man dieses Problem umgehen kann?



  • inwiefern kommt es da zu komplikationen ?
    solltest dein problem schon etwas besser darstellen

    Meep Meep



  • Ok, war vielleicht etwas kurz.

    Ich will dann folgende Klasse erstellen

    class Application
    {
    protected:
      Abstract *a_x,*a_y;
    public:
      Application(int i)
      {
        switch(i){
          case 1: a_x=new Konkret1();a_y=new Konkret1(); break;
          case 2: a_x=new Konkret2();a_y=new Konkret2(); break;
        }
      }
      DoSomething(){a_x->DoSomething();a_y->DoSomething();};
    };
    

    Ich bin mir hier noch nicht sicher, ob man die Instanzierung der Member a_x und a_y noch besser lösen könnte. Das eigentliche Problem ist eher bei mir, dass ich von aussen nicht mehr die Parameter von a_x und a_y ändern kann. Vielleicht ist mein Ansatz auch nicht optimal.

    Trotzdem schonmal ein grossen Dankeschön für Eure Hilfe

    headmyshoulder



  • Naja, du kannst die "SetParam" Methode ins Interface mit aufnehmen. Als Parameter übergibst du dann z.B. einen string, und den konvertierst du in der Klasse die ein double braucht einfach in ein double. Übergeben tust du dann eben die String-Darstellung des doubles, also vielleicht "1.234" o.ä.

    Das könnte dein Problem lösen - kommt halt drauf an was das eigentliche Problem ist.
    Wenn es aber reicht das Parameter beim Erstellen zu setzen, dann brauchst du weiter garnix zu machen, dann setzt du es eben einfach beim erstellen:

    Application(int i)
      {
        switch(i){
          case 1:
            a_x=new Konkret1();
            static_cast<Konkret1*>(a_x)->SetParam(123);
            a_y=new Konkret1();
            static_cast<Konkret1*>(a_y)->SetParam(234);
            break;
        // ...
        }
      }
    

    Oder du gibst es gleich dem Konstruktor mit:

    Application(int i)
      {
        switch(i){
          case 1:
            a_x=new Konkret1(123);
            a_y=new Konkret1(234);
            break;
        // ...
        }
      }
    


  • Ja, an die beiden Varianten habe ich auch schon gedacht. Die Variante mit dem Strings und dem Casten find ich nicht so elegant und die Variante des Parametersetzen bei Erzeugen schränkt die Funktionalität ein wenig ein.

    Das Problem kommt mir recht allgemein vor. Vielleicht gibts es dafür ja irgendeine allgemeine Lösung oder ein Design Pattern was das vereinheitlicht.



  • Ahh, mir fällt gerade was ein. Mann könnte ja die beiden Objekte a_x und a_y draussen erzeugen, also

    class class Application
    {
    protected:
      Abstract *a_x,*a_y;
    public:
      Application(Abstract *_a_x,Abstract *_a_y){a_x=_a_x;a_y=_a_y;};
      DoSomething(){a_x->DoSomething();a_y->DoSomething();};
      SetAX(Abstract *_a_x) { a_x=_a_x; };
      // usw....
    };
    
    Konkret1 *a_x=new Konkret1(),*a_y=new Konkret1();
    
    Application app(a_x,a_y);
    

    Dann kann man die Objekte von aussen manipulieren.



  • hoi

    also wenn es keinen grund dafuer geben sollte, das man auf das object von aussen zugreifen koennen muss, waere folgendes auch eine moeglichkeit:

    class Abstract
    {
       public:
          virtual void DoSomething();
    };
    
    template<class T>
    class KonkretBase : public Abstract
    {
       public:
          typedef T value_type;
    
          KonkretBase(const T &val) : value(val) { }
          void setParam(const T &val) { value = val; }
    
       protected:
          T value;
    };
    
    class Konkret1 : public KonkretBase<double>
    {
       public:
          Konkret1(double val) : KonkretBase<double>(val) { }
          void DoSomething(void) { /* do something */ }
    };
    
    class Konkret2 : public KonkretBase<std::string>
    {
       public:
          Konkret2(const std::string &val) : KonkretBase<std::string>(val) { }
          void DoSomething(void) { /* do something */ }
    };
    
    class Application
    {
       public:
          Application(double x, double y)
          {
             val_x = new Konkret1(x);
             val_y = new Konkret1(y);
          }
    
          Application(const std::string &x, const std::string &y)
          {
             val_x = new Konkret2(x);
             val_y = new Konkret2(y);
          }
    
          void setNewParam(double x, double y)
          {
             static_cast<Konkret1*>(val_x)->setParam(x);
             static_cast<Konkret1*>(val_y)->setParam(y);
          }
    
          void setNewParam(const std::string &x, const std::string &y)
          { 
             static_cast<Konkret2*>(val_x)->setParam(x);
             static_cast<Konkret2*>(val_y)->setParam(y);
          }
    
          void DoSomething(void)
          {
             val_x->DoSomething();
             val_y->DoSomething();
          }
    
       protected:
          Abstract *val_x, *val_y;
    };
    

    Meep Meep



  • Meep Meep schrieb:

    hoi

    also wenn es keinen grund dafuer geben sollte, das man auf das object von aussen zugreifen koennen muss, waere folgendes auch eine moeglichkeit:

    class Abstract
    {
       public:
          virtual void DoSomething();
    };
    
    template<class T>
    class KonkretBase : public Abstract
    {
       public:
          typedef T value_type;
       
          KonkretBase(const T &val) : value(val) { }
          void setParam(const T &val) { value = val; }
    
       protected:
          T value;
    };
    
    class Konkret1 : public KonkretBase<double>
    {
       public:
          Konkret1(double val) : KonkretBase<double>(val) { }
          void DoSomething(void) { /* do something */ }
    };
    
    class Konkret2 : public KonkretBase<std::string>
    {
       public:
          Konkret2(const std::string &val) : KonkretBase<std::string>(val) { }
          void DoSomething(void) { /* do something */ }
    };
    
    class Application
    {
       public:
          Application(double x, double y)
          {
             val_x = new Konkret1(x);
             val_y = new Konkret1(y);
          }
          
          Application(const std::string &x, const std::string &y)
          {
             val_x = new Konkret2(x);
             val_y = new Konkret2(y);
          }
          
          void setNewParam(double x, double y)
          {
             static_cast<Konkret1*>(val_x)->setParam(x);
             static_cast<Konkret1*>(val_y)->setParam(y);
          }
          
          void setNewParam(const std::string &x, const std::string &y)
          { 
             static_cast<Konkret2*>(val_x)->setParam(x);
             static_cast<Konkret2*>(val_y)->setParam(y);
          }
          
          void DoSomething(void)
          {
             val_x->DoSomething();
             val_y->DoSomething();
          }
          
       protected:
          Abstract *val_x, *val_y;
    };
    

    Meep Meep

    ???
    Inwieweit soll das gut und richtig sein? Dann muss der Aufrufer ja wissen welche Variante von setNewParam er aufrufen darf!?!



  • headmyshoulder schrieb:

    Ja, an die beiden Varianten habe ich auch schon gedacht. Die Variante mit dem Strings und dem Casten find ich nicht so elegant und die Variante des Parametersetzen bei Erzeugen schränkt die Funktionalität ein wenig ein.

    Das Problem kommt mir recht allgemein vor. Vielleicht gibts es dafür ja irgendeine allgemeine Lösung oder ein Design Pattern was das vereinheitlicht.

    Ja, gibt es. Wurden schon genannt.
    BTW: bei der "Variante mit den Strings" wird nix gecastet sondern "geparst" oder "konvertiert" - was ein grosser Unterschied ist.

    Eine andere oft verwendete Variante ist noch sich eine Art "variant" Typ zu basteln der dann z.B. einen int/double/string/wstring enthalten kann (aber eben nur eines davon), und den man fragen kann was er ist, und den man ggf. rumkonvertieren kann.
    Wenn du der "setParam" Funktion dann einen "variant" gibst der bereits ein double ist muss nixmehr konvertiert werden, wenn's was anderes ist muss eben doch konvertiert werden.

    ----

    Was ist denn der genaue Anwendungsfall? Zu entscheiden was wohl das beste ist ist IMO nur möglich wenn man auch weiss wofür es verwendet werden soll...



  • Das Problem was ich gerade habe ist eine Klasse, die numerisch Ableiten soll, z.B. mit Finiten Differenzen oder Splines oder im Spektralraum, d.h. meine Funktion DoSomething() ist in etwa sowas

    void Deriv(double *in,double *deriv,int length)
    

    Manche Methoden haben Parameter, z.B. bei FD und Splines die Ordnung und die Fensterbreite, andere Verfahren haben keine Parameter. Die Applikationsklasse soll einfach dann in zwei Dimensionen ableiten. Ich glaub ich werde das wie folgt lösen:

    class class Application
    {
    protected:
      Abstract *a_x,*a_y;
    public:
      Application() { a_x=new Konkret1(); a_y=new Konkret2(); };
    
      SetAX(Abstract *_a_x);
      Abstract* GetAX();
    
      // usw.
    };
    
    Application app(); 
    Konkret2 *a_x=new Konkret2();
    a_x->SetParam(10);
    app.SetAX(a_x);
    

    Also die Application wird erstmal mit zwei defaults erzeugt. Wenn man dann halt andere "Ableiter" haben will muss man die per Hand erzeugen und dann einfach der Application übergeben.

    Das sieht für mich jetzt erstmal am einfachsten und elegantesten aus, man ist auch nicht mehr auf eine bestimmte Methode für die Parameter festgelegt, man könnte die zum Beispiel SetOrder(int) oder SetWindowSize(int) nennen, ohne das es KOnflikte gibt. Vielleicht hat jemand aber auch noch eine Idee?


Anmelden zum Antworten