Search code examples
c++abstract-classabstractabstraction

Defining extra implementation specific functions in a class implementing an abstract class


Let's say I'm creating an abstract class Fruit with function eat() and I want to make a few different classes implementing it. However, for the class Kiwi I also want to make a function peel() which wasn't defined in my abstract class.

Is it bad practice to make extra functions in a class implementing an abstract class? I know I could add it to the abstract class and just have it do nothing in the classes that don't use it, but seems bad if I have a lot of other classes it won't be useful in it.


Solution

  • Is it bad practice to make extra functions in a class implementing an abstract class?

    Not at all!

    Suppose we have the following classses:

    class Fruit
    {
    public:
        virtual void Eat() = 0;    // pure virtual functions make the class abstract
    }
    
    class Berry : public Fruit
    {
    public:
        void Eat() override
        { /* eat the berry... */ }
    }
    
    class Kiwi : public Fruit
    {
    public:
        void Eat() override
        { /* eat the kiwi... */ }
    
        void Peel()
        { /* peel the kiwi... */ }
    }
    

    We can certainly Eat() any Fruit we come across so it makes sense that all Fruit objects can be eaten.

    You wouldn't Peel() a Berry before eating it so there is no expectation that a Berry can be Peel()ed. That would be unnecessary for users that are handling a Berry object anyways.

    Before Eat()ing a Kiwi you should Peel() it first, so anyone who is handling a Kiwi object is going to expect the Peel() function to be available and therefore Peel() their Kiwi before they Eat() it.

    Now let's say the user is blindfolded and given a Fruit* someFruit pointer. They don't know whether it's a Berry or a Kiwi but they can certainly try { Eat(); } it anyways because eating fruit always makes sense! If the Fruit was actually a Kiwi and they did not Peel() it first then it is a good idea to have the Kiwi::Eat() function throw an exception (throw "yuk!") or handle this unexpected usage gracefully.

    Although this example is written really specifically about eating fruit, we can generally assume that the user of an object will know what functions are available for that object simply by knowing its type. And when the user does not know the object's type, then it is a good idea to implement some error-checking to handle its functions being used incorrectly.