0

I have an abstract object inherited by objects with very different needs, but I need to get all of them in the same container at a point.

I have two main ideas, the first :

Define all methods in the abstract object in that way :

class AbstractObject
{
    ...

    virtual bool isObjectSpecial ()
    {
        /* Default behavior : you are not special, so return false. */
        return false;
    };

    virtual void doSomethingSpecial ()
    {
        /* Default behavior : you are really not special, so do nothing.*/
    };

    ...
}

class SpecificObjectA : public AbstractObject
{
    ...

    bool isObjectSpecial () override 
    {
        return true;
    };

    void doSomethingSpecial () override
    {
        /* Do the special A thing ... */
    };

    ...
}

class SpecificObjectB : public AbstractObject
{
    ...

    bool isObjectSpecial () override 
    {
        return true;
    };

    void doSomethingSpecial () override
    {
        /* Do the special B thing ... */
    };

    ...
}

class RegularObjectA : public AbstractObject
{
    ...
}

/* ... and so on. */

And the use case will be :

if ( abstractObjectPtr->isObjectSpecial() )
    abstractObjectPtr->doSomethingSpecial();

The second idea is : Define only common methods to the base abstract and create an other abstract object containing methods for the special case in that way.

class AbstractObject
{
    ...
}

class AbstractSpecialObject : public AbstractObject
{
    ...

    void doSomethingSpecial () = 0; // NOTE: Pure virtual here !

    ...
}

class SpecificObjectA : public AbstractSpecialObject
{
    ...

    void doSomethingSpecial () override
    {
        /* Do the special A thing ... */
    };

    ...
}

class SpecificObjectB : public AbstractSpecialObject
{
    ...

    void doSomethingSpecial () override
    {
        /* Do the special B thing ... */
    };
    
    ...
}

class RegularObjectA : public AbstractObject
{
    ...
}

/* ... and so on. */

And the use case will be :

auto specialAbstractObjectPtr = dynamic_cast< AbstractSpecialObject * >(abstractObjectPtr);

if ( specialAbstractObjectPtr != nullptr )
    specialAbstractObjectPtr->doSomethingSpecial();

From a full OOP perspective, I prefer the second solution, but I don't like the dynamic casting. In the other hand, I don't know what could be wrong with the first one, just the fact it forces the abstract object to be responsible for all methods.

What will you choose and why ? And, did someone have an other solution ?

  • Wait.. so you "prefer" the second one, but don't "like" it ? Not an envious position to be sure. – WhozCraig Jan 06 '21 at 18:41
  • haha, yes, I don't like the dynamic pointer casting. It's not nice for readability, and you must check it. ** Mistake fixed. – Sébastien Bémelmans Jan 06 '21 at 18:43
  • @SébastienBémelmans If your need is to find if an object is inherited from another object, I would suggest you take a look at [`std::is_base_of<,>`](https://en.cppreference.com/w/cpp/types/is_base_of). – D-RAJ Jan 06 '21 at 18:44
  • @DhirajWishal That doesn't work after type erasure – Mooing Duck Jan 06 '21 at 18:45
  • 1
    " I need to get all of them in the same container at a point." Well _that_ is the source of the problem. Don't do that, and the problem goes away. – Mooing Duck Jan 06 '21 at 18:45
  • @MooingDuck Yes, but 75% of the final object is the same to others... If I want to split this container, I need to split a lot of code. I know you're right. – Sébastien Bémelmans Jan 06 '21 at 18:47
  • 4
    Sounds like you're violating the [Liskov Substitution principle.](https://stackoverflow.com/questions/56860/what-is-an-example-of-the-liskov-substitution-principle). That usually leads to pain. – user4581301 Jan 06 '21 at 18:48
  • @MooingDuck Oh now I get the problem :). Are the all possible child classes known beforehand? – D-RAJ Jan 06 '21 at 18:48
  • @user4581301 So if I don't want to break the rules, I should stay to the first solution ? – Sébastien Bémelmans Jan 06 '21 at 18:54
  • 2
    First off make sure you're breaking the rule. If all the base class has to do is be a container, you can add a few virtual functions to help you either generalize the behaviour (a `do_it` function that just does whatever, or something along the lines of the visitor pattern) so you don't care what type an object really is or add a virtual function that helps you determine the type of the object. I prefer the first because it's less brittle and scales well as the number of possible classes increases. – user4581301 Jan 06 '21 at 19:00
  • 2
    instead of this `isSpecial` flag, the derived classes should implement a `doSomething` appropriately such that you can call the base class' `doSomething` no matter waht is the actual derived type, thats the point of using virtual functions after all – 463035818_is_not_an_ai Jan 06 '21 at 19:05
  • @user4581301 Yes. I think in my case, there is really no advantage to split up the objects, because this is one method really different against all the others in common. And there is also no advantage to add an other abstract layer. – Sébastien Bémelmans Jan 06 '21 at 19:15
  • @largest_prime_is_463035818 I know, it was more for code readability I wanted to do that. In fact thhe method doSomething, does nothing at abstractObject level. So don't really need to make "if ( x->isSpecial() )". – Sébastien Bémelmans Jan 06 '21 at 19:17

0 Answers0