ADT and inheritance

Discussion in 'C++' started by maverik, Nov 17, 2009.

  1. maverik

    maverik Guest

    Hi all.

    I have some class that contains only pure virtual functions (class-
    interface):

    class IButton {
    // Contents of the class
    };

    So when I actually implement some type of button I do it like this

    class ImageButton : public IButton {
    // Contents of the class
    };

    It allows me to create a container of buttons without knowledge about
    button types that this container would hold:

    std::vector<IButton *> m_buttons;

    The problem is: when I add some specific function to the inherited
    button class then I can call it only in this way:

    ((ImageButton *)m_buttons)->SetImage(...); // I know that i-th
    button is typeof ImageButton

    Looks ugly a little bit, isn't it?

    Of course I can add this specific function to the base class (IButton)

    class IButton {
    void SetImage(...) = 0;
    };

    but in this case IButton becomes just a bunch of methods from
    different classes.

    Is there a way to make it better?

    Thanks.
    maverik, Nov 17, 2009
    #1
    1. Advertising

  2. maverik wrote:
    > Hi all.
    >
    > I have some class that contains only pure virtual functions (class-
    > interface):
    >
    > class IButton {
    > // Contents of the class
    > };
    >
    > So when I actually implement some type of button I do it like this
    >
    > class ImageButton : public IButton {
    > // Contents of the class
    > };
    >
    > It allows me to create a container of buttons without knowledge about
    > button types that this container would hold:
    >
    > std::vector<IButton *> m_buttons;
    >
    > The problem is: when I add some specific function to the inherited
    > button class then I can call it only in this way:
    >
    > ((ImageButton *)m_buttons)->SetImage(...); // I know that i-th
    > button is typeof ImageButton
    >
    > Looks ugly a little bit, isn't it?
    >


    Yes, c-type casts are always bad.

    > Of course I can add this specific function to the base class (IButton)
    >
    > class IButton {
    > void SetImage(...) = 0;
    > };
    >
    > but in this case IButton becomes just a bunch of methods from
    > different classes.


    What is wrong with that?



    --
    Bolje je ziveti sto godina kao bogatun, nego jedan dan kao siromah!
    Vladimir Jovic, Nov 17, 2009
    #2
    1. Advertising

  3. maverik

    maverik Guest

    > > ((ImageButton *)m_buttons)->SetImage(...); // I know that i-th
    > > button is typeof ImageButton

    >
    > > Looks ugly a little bit, isn't it?

    >
    > Yes, c-type casts are always bad.


    Ok, I actually use static_cast, so the code looks like
    static_cast<ImageButton *>(m_buttons)->SetImage(...);

    > > Of course I can add this specific function to the base class (IButton)

    >
    > > class IButton {
    > >     void SetImage(...) = 0;
    > > };

    >
    > > but in this case IButton becomes just a bunch of methods from
    > > different classes.

    >
    > What is wrong with that?


    Hmmm... one thing is

    class IButton {
    // 100 methods from AButton goes here
    // ...
    // 100 methods from BButton goes here
    // ...
    // 100 methods from CButton goes here
    // ...
    };

    and the programmer who add new methods always should update the base
    class. The other thing is that the purpose of IButton is to include
    common operation for all types of Buttons not every operation from
    every type. I think it's rather good idea, isn't it? If not, please,
    take a minute to explain why.
    maverik, Nov 17, 2009
    #3
  4. maverik

    Daniel Pitts Guest

    maverik wrote:
    > Hi all.
    >
    > I have some class that contains only pure virtual functions (class-
    > interface):
    >
    > class IButton {
    > // Contents of the class
    > };
    >
    > So when I actually implement some type of button I do it like this
    >
    > class ImageButton : public IButton {
    > // Contents of the class
    > };
    >
    > It allows me to create a container of buttons without knowledge about
    > button types that this container would hold:
    >
    > std::vector<IButton *> m_buttons;
    >
    > The problem is: when I add some specific function to the inherited
    > button class then I can call it only in this way:
    >
    > ((ImageButton *)m_buttons)->SetImage(...); // I know that i-th
    > button is typeof ImageButton
    >
    > Looks ugly a little bit, isn't it?
    >
    > Of course I can add this specific function to the base class (IButton)
    >
    > class IButton {
    > void SetImage(...) = 0;
    > };
    >
    > but in this case IButton becomes just a bunch of methods from
    > different classes.
    >
    > Is there a way to make it better?
    >
    > Thanks.

    Usually, the container values aren't configured after the fact, so
    you'll never call SetImage on an object reference you got from m_buttons.

    std::vector<IButton *> m_buttons;

    ImageButton * myButton = new ImageButton();
    myButton->SetImage(...);
    m_buttons.add(myButton);

    IButton should contain methods that you need to support any IButton (in
    Java Swing, for example, JButton has a paintComponent method, which can
    be overridden by subclasses)

    If you are going to cast frequently at runtime, then you've probably got
    the wrong static type.
    Daniel Pitts, Nov 18, 2009
    #4
  5. maverik wrote:
    > I have some class that contains only pure virtual functions (class-
    > interface):
    >
    > class IButton {
    > // Contents of the class
    > };
    >
    > So when I actually implement some type of button I do it like this
    >
    > class ImageButton : public IButton {
    > // Contents of the class
    > };
    >
    > It allows me to create a container of buttons without knowledge about
    > button types that this container would hold:
    >
    > std::vector<IButton *> m_buttons;
    >
    > The problem is: when I add some specific function to the inherited
    > button class then I can call it only in this way:
    >
    > ((ImageButton *)m_buttons)->SetImage(...); // I know that i-th
    > button is typeof ImageButton
    >
    > Looks ugly a little bit, isn't it?
    >
    > Of course I can add this specific function to the base class (IButton)
    >
    > class IButton {
    > void SetImage(...) = 0;
    > };
    >
    > but in this case IButton becomes just a bunch of methods from
    > different classes.
    >
    > Is there a way to make it better?


    I'd advise to use several containers for the derived class pointers:
    one container contains all objects with their base interface IButton,
    one
    contains pointers to all ImageButton objects, and one contains all
    objects of
    type SomeOtherButton. This means that registering objects of type
    ImageButton includes
    registering the object in the list of IButtons as well. This makes the
    code
    much more readable and maintainable than using dynamic_casts, IMHO.

    Regards,
    Stuart
    Stuart Redmann, Nov 18, 2009
    #5
  6. maverik

    James Kanze Guest

    On Nov 18, 12:37 am, Daniel Pitts
    <> wrote:
    > maverik wrote:


    > > I have some class that contains only pure virtual functions
    > > (class- interface):


    > > class IButton {
    > > // Contents of the class
    > > };


    > > So when I actually implement some type of button I do it
    > > like this


    > > class ImageButton : public IButton {
    > > // Contents of the class
    > > };


    > > It allows me to create a container of buttons without
    > > knowledge about button types that this container would hold:


    > > std::vector<IButton *> m_buttons;


    > > The problem is: when I add some specific function to the
    > > inherited button class then I can call it only in this way:


    > > ((ImageButton *)m_buttons)->SetImage(...); // I know that i-th
    > > button is typeof ImageButton


    That really should be:

    ImageButton* p = dynamic_cast< ImageButton* >( m_buttons);
    if ( p != NULL ) // special handling...

    > > Looks ugly a little bit, isn't it?


    > > Of course I can add this specific function to the base class
    > > (IButton)


    > > class IButton {
    > > void SetImage(...) = 0;
    > > };


    > > but in this case IButton becomes just a bunch of methods
    > > from different classes.


    > > Is there a way to make it better?


    > Usually, the container values aren't configured after the
    > fact, so you'll never call SetImage on an object reference you
    > got from m_buttons.


    > std::vector<IButton *> m_buttons;


    > ImageButton * myButton = new ImageButton();
    > myButton->SetImage(...);
    > m_buttons.add(myButton);


    > IButton should contain methods that you need to support any
    > IButton (in Java Swing, for example, JButton has a
    > paintComponent method, which can be overridden by subclasses)


    > If you are going to cast frequently at runtime, then you've
    > probably got the wrong static type.


    Yes. The question is: what the vector is supposed to contain.
    If it's just ImageButton, then it should be
    std::vector< ImageButton* >. Otherwise, users of the vector
    *generally* shouldn't use parts of the interface that are unique
    to ImageButton.

    The one exception I can think of is if you do define an extended
    button interface, e.g.:

    class IExtendedButton : public IButton
    {
    // More pure virtual functions with additional
    // functionality.
    };

    It's perfectly reasonable to imagine cases where you want to use
    some additional features of ExtendedButton, if they are present,
    and fall back to some default handling if they aren't. In such
    a case, something like:

    for ( std::vector< IButton* >::const_iterator
    iter = m_buttons.begin() ;
    iter != m_buttons.end();
    ++ iter ) {
    IExtendedButton* extended
    = dynamic_cast< IExtendedButton* >( *iter );
    if ( extended != NULL ) {
    // use added features...
    } else {
    // use default handling...
    }
    }

    is reasonable. Extending this to a chain of else if... for
    each possible type isn't, but for one, or possibly two
    extensions, OK.

    --
    James Kanze
    James Kanze, Nov 18, 2009
    #6
  7. maverik wrote:
    >>> ((ImageButton *)m_buttons)->SetImage(...); // I know that i-th
    >>> button is typeof ImageButton
    >>> Looks ugly a little bit, isn't it?

    >> Yes, c-type casts are always bad.

    >
    > Ok, I actually use static_cast, so the code looks like
    > static_cast<ImageButton *>(m_buttons)->SetImage(...);


    As James Kanze said, it should have been dynamic_cast

    >
    >>> Of course I can add this specific function to the base class (IButton)
    >>> class IButton {
    >>> void SetImage(...) = 0;
    >>> };
    >>> but in this case IButton becomes just a bunch of methods from
    >>> different classes.

    >> What is wrong with that?

    >
    > Hmmm... one thing is
    >
    > class IButton {
    > // 100 methods from AButton goes here
    > // ...
    > // 100 methods from BButton goes here
    > // ...
    > // 100 methods from CButton goes here
    > // ...
    > };
    >
    > and the programmer who add new methods always should update the base
    > class. The other thing is that the purpose of IButton is to include
    > common operation for all types of Buttons not every operation from
    > every type. I think it's rather good idea, isn't it? If not, please,
    > take a minute to explain why.



    1) it seams very wrong to have so many methods in one class.

    2) can you do what Stuart Redmann suggested? To have different base classes.

    From what you wrote, you have a bad design problem.

    --
    Bolje je ziveti sto godina kao bogatun, nego jedan dan kao siromah!
    Vladimir Jovic, Nov 18, 2009
    #7
  8. maverik

    Daniel Pitts Guest

    James Kanze wrote:
    > On Nov 18, 12:37 am, Daniel Pitts
    > <> wrote:
    >> If you are going to cast frequently at runtime, then you've
    >> probably got the wrong static type.

    >
    > Yes. The question is: what the vector is supposed to contain.
    > If it's just ImageButton, then it should be
    > std::vector< ImageButton* >. Otherwise, users of the vector
    > *generally* shouldn't use parts of the interface that are unique
    > to ImageButton.
    >
    > The one exception I can think of is if you do define an extended
    > button interface, e.g.:
    >
    > class IExtendedButton : public IButton
    > {
    > // More pure virtual functions with additional
    > // functionality.
    > };
    >
    > It's perfectly reasonable to imagine cases where you want to use
    > some additional features of ExtendedButton, if they are present,
    > and fall back to some default handling if they aren't. In such
    > a case, something like:
    >
    > for ( std::vector< IButton* >::const_iterator
    > iter = m_buttons.begin() ;
    > iter != m_buttons.end();
    > ++ iter ) {
    > IExtendedButton* extended
    > = dynamic_cast< IExtendedButton* >( *iter );
    > if ( extended != NULL ) {
    > // use added features...
    > } else {
    > // use default handling...
    > }
    > }
    >
    > is reasonable. Extending this to a chain of else if... for
    > each possible type isn't, but for one, or possibly two
    > extensions, OK.


    That is one approach. The other is to have IButton have a method which
    returns IExtendedButton. The default implementation returns NULL, but
    subclasses can return any object they wish (including themselves if they
    are extended buttons).

    This removes the need for dynamic cast, and makes the concept of
    "extended button" more concrete.
    Daniel Pitts, Nov 18, 2009
    #8
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Kenneth
    Replies:
    4
    Views:
    709
    Tony Morris
    Nov 11, 2004
  2. Gaz
    Replies:
    2
    Views:
    1,552
  3. Tree ADT vs Database

    , Apr 8, 2006, in forum: Java
    Replies:
    9
    Views:
    1,421
    Roedy Green
    Apr 8, 2006
  4. SunScreen
    Replies:
    2
    Views:
    2,935
    Howard
    Nov 18, 2004
  5. Dr Ann Huxtable
    Replies:
    1
    Views:
    368
    Howard
    Nov 29, 2004
Loading...

Share This Page