How to avoid type comparision using polymorphism



  • Hi everybody!

    I'm facing a new problem while developing an application and I'd like to hear some advices regarding its design.

    I have a class (ObjectAction) which defines what should be done on specific situations. As I want to have different kinds of those "actions", I was thinking to define ObjectAction like this:

    @class ObjectAction {
    public:
    virtual void perform(...) = 0;
    virtual void writeToFile() = 0:
    virtual void readFromFile(...) = 0;
    };@

    So I can define its behavior by subclassing ObjectAction. For example:

    @class ExampleAction : public ObjectAction {
    public:
    QString getData() const { return data; }
    void setData(const QString& newData) { data = newData; }
    void perform(...) {
    qDebug() << data;
    }
    // Other definitions missing
    .......
    private:
    QString data;
    };

    class OtherAction : public ObjectAction {
    public:
    ......
    void perform() { /*something else */ }
    .....
    private:
    QHash<QString, int> data;
    };@
    It seems to be the good choice, but there is a problem: I want to provide an editor which allows user to set the ObjectAction to use, and he should be able to change the action kind and its data, so in my editor, if the action is of type ExampleAction, I will show a QLineEdit with the current data, but if the action is of type OtherAction, I should show some other widget to allow modifying the QHash. The same thing applies when I need to construct the action from data read from a file. The question is: How should I determine the type? Or better, how should I draw the editor for each type?

    Thank you very much!



  • One (and not the only) way to do this is to have some function in your base class show the editor. Like that:
    @
    Class objectaction {
    public:
    virtual void show_editor();
    };

    class other_action : public objectaction {
    show_editor() {
    // show this classes special editor and adjust hash
    }
    };
    @



  • Hi elephanten, thanks for replying!

    I had already considered that way, but there are two reasons I don't like it:

    1. It polutes the class declaration, because the editor is only needed in a separate application that allows user to manage the "actions" of some objects and then it's saved to a file. In the main application I just need to read that file and "perform" them
    2. I would anyway need to figure the current action type in order to provide a QComboBox with action types and setting the current type there

    Thank you very much!



  • For determining the type of an actual action, you will need some overwritten virtual method that you declare in the base class (probably as pure virtual) anyways.

    For your enumeration problem: You have some choices. The naive way would be to keep track on all the choices in your GUI application manually. But it's easy to forget adding a new one there.

    Another approach would be a factory method in the base class. Add an enum with all the possible action subtypes, add a generic createAction(subtype) factory method that returns a base class pointer containing the correct subclass object. Using that enum, you can also create a listSubtypes() method - it can return a list of numbers, a string list or whatever you need. So, if you add a new action subclass, you just need to add it to those two methods. You can make them static, so that you don't need a base class object. Using that method, I would also add that createEditor() method to the subclasses. You don't need to take care of adding a new editor in case you create a new subclass.



  • Hi Volker, thanks for your reply!

    So, the base class should look like this:

    @class ObjectAction {
    public:
    enum ActionType {
    Type1, Type2
    };
    virtual ActionType type() const = 0;
    virtual void perform(...) = 0;
    virtual void writeToFile() = 0:
    virtual void readFromFile(...) = 0;

       virtual QWidget* getEditor() = 0;
    
       static QStringList subtypes() { /*definition missing*/ }
       static ObjectAction* createAction(ActionType type) { /*definition missing*/ }
    

    };@

    Yesterday I was reading about some design patterns and I found the Factory Method, which is basically what you are suggesting to me.

    But I don't know how to manage the editor lifetime. As I said previously, I won't need to use it in the main application, so I don't want to waste memory by creating it if I won't use it. Maybe the correct approach is another static function? Something like this:

    @static QWidget* getEditor(ActionType type) {
    switch(type): {
    case Type1: {
    static Type1Widget *widget = new Type1Widget();
    return widget;
    }
    case Type2:
    ........
    }
    }@

    I marked the function as static because there would be a memory leak if it wasn't static, am I wrong?

    Thank you very much!



  • I would add the getEditor() method to the ObjectAction class (as pure virtual:

    @
    class ObjectAction {
    public:
    virtual QWidget* createEditor(QWidget *parent = 0) = 0;

    // ...
    };
    @

    And then have the actual subclasses return the editor, eg.

    @
    class LineEditAction : public ObjectAction {
    public:
    // constructors etc.

        QWidget* createEditor(QWidget *parent = 0) {
            return new QLineEdit(parent);
        }
    

    };
    @

    In your managing code you just call

    @
    QWidget *editor = myObjectAction->createEditor(this);
    @



  • Hi again Volker!

    That approach seems to be very interesting, but anyway I would end up deleting and recreating widgets.
    Basically, I show the proper widget for an action when user changes the current item in a QListView, so I would have to delete and get the editor again each time the current item changes!

    Thank you very much!



  • Basically you should consider if you want to save memory and hence recreate the editor each and every time your user needs it; or you want to gain maximum performance and have editor created once and for the lifetime of application or whatever relevant section of your code.



  • And I could have th editor created only once (per type) using static functions and variables, right?

    Something like this:

    @class LineEditAction : public ObjectAction {
    public:
    static QWidget* getStaticEditor(LineEditAction action) {
    static QWidget
    editor = new QLineEdit();
    editor->setText(action->text);
    return editor;
    }

    virtual QWidget* createEditor() {
    return getStaticEditor(this);
    }
    };@

    Thank you again!



  • You can put the editor into a static class member and return this one using a nonstatic, virtual method on the object.



  • Seems like I closed my browser before sending my last message.

    Anyway, thank you very much, Volker! I thought it would create a variable per instance, but it makes sense that there is only one static variable per class.

    It's probably the best way I can implement it.

    It's solved now!


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.