Chris C
Chris C

Reputation: 2023

A cleaner code alternative to polymorphism

Building a GUI system and I have a few classes for different GUI components that derive from a base "GUIcontrol" class. What I want is to have just one function to return any type of component but be able to work with the functions specific to that component type (functions of the derived class). I noticed that the polymorphism approach is going to become a problem I have to declare all the derived functions in the base which is unnecessary for this, since I will never create an object just from the base class.

class GUIcontrol {
  protected:
    std::string _name;
    // these two methods (along with name()) will be used by all types
    virtual void position(/*parameters*/)
    virtual void useImage(/*parameters*/)

    // these should be only in derived types
    virtual void setHotSpot(/*parameters*/);
    virtual void setScrollButtons(/*parameters*/);
  public:
    std::string name();
  /*etc*/
}

class GUIbutton : public GUIcontrol {
  public:
    void setHotSpot(/*parameters*/);
}

class GUIscrollBar : public GUIcontrol {
  public:
    void setScrollButtons(/*parameters*/);
}

GUIcontrol* GUIsystem::getControl(std::string name);

The problem with this is that if I want to add more functions unique to GUIbutton or GUIscrollBar, or any functions to other derived GUI classes, I also have to declare them virtual in the base class so the compiler doesn't complain about something like "setHotSpot" not being a member of the base class it returns.

The base class does have member functions that will apply to all the derived classes, such as telling the object where it should be positioned, what image it needs to use, what it should be called, etc. But I don't want to keep stuffing the base class with other functions that need to stay exclusive to certain derived classes.

As I keep adding more virtual functions I would end up with a huge blob object for the base class. Can I design this in a cleaner way? Note that I am still not sure if I want to use static_cast/dynamic_cast for getControl() to solve this but just want to know if there are any other ways around this to clean it up.

Upvotes: 0

Views: 1242

Answers (4)

Keith
Keith

Reputation: 6834

If I have this right: You want to be able to pass around base class objects but have a clean way to call specific derived class methods where the derived class implements those methods?

Sounds like the 'mixin' pattern might help:

struct Base
{
    virtual ~Base() {}
};


struct Mixin
{
    virtual ~Mixin() {}
    virtual void mixedMethod() = 0;
};

struct Concrete : Base, Mixin
{
    virtual void mixedMethod() { std::cout << "Mixing" << std:: endl; }
};

Base* create() { return new Concrete;}

bool mixIt(Base& b)
{
    Mixin* m = dynamic_cast<Mixin*>(&b);
    if (m)
        m->mixedMethod();
    return m;
}    

void test ()
{
    Base* b = create();
    assert(mixIt(*b));
    Base base;
    assert(!mixIt(base));   
}

[ Yes, real code never uses struct for polymorhic classes; just keeping it compact.]

The idea here is that the availability of a given method is encapsulated in the Mixin class, which is an pure abstract base class, possibly with only a single pure virtual function. If you want "know" your base class object is of the derived type, you can call the mixin classes method. You can wrap the test and the call in a non-member function; this allows you to keep the base calss interface itself clean.

Upvotes: 0

Puppy
Puppy

Reputation: 146920

The base class is exclusively common functionality. If you want your method to behave differently for different controls, use dynamic_cast. If you want it to act the same for all controls, use a virtual method.

This is your problem:

What I want is to have just one function to return any type of component but be able to work with the functions specific to that component type (functions of the derived class).

What you want is to treat them the same but differently. Huh. I wonder how you're going to make that work. You need to decide if you want to treat them all the same, or if you want to treat them differently.

Upvotes: 1

Melv
Melv

Reputation: 9

Type checking and then downcasting isn't the right way to do this. What you should be doing is placing generic methods onto your base class which perform the types of operations you want, and then overriding them in subclasses. For example, if you want the GUIControl to be able to draw itself, then put a doDraw() method on the base class, then override that in each subclass to do as is needed. If you instead put a getTitleBar(), getText() etc. methods on your subclass, then have the caller downcast and calls those specific methods depending on the type, your encapsulation is broken. If you have some common code that multiple subclasses need to do their drawing, then you factor this out either through another parent class, or through composition. Using dynamic_cast, or putting specific methods on the generic subclass, will likely make your code worse.

Upvotes: 0

Anon.
Anon.

Reputation: 59983

The base class should only contain methods for functionality common to all controls.

If you're going to use functionality that only makes sense for one type of control, you should be checking that the control is of the correct type anyway, and can then cast it to that type.

Upvotes: 4

Related Questions