Brad
Brad

Reputation: 5532

Implementing partial C++ Interface in parent class

I have a unique case where I need two layers of Interfaces, and would like to have two layers of classes to implement them:

class IFood {
public:
    virtual ~IFood() = default;
    virtual int GetColor() const = 0;
};

class IFruit : public IFood {
public:
    virtual int GetAverageSeedCount() const = 0;
};

class GreenFood : public IFood {
    virtual int GetColor() const override {return GREEN;}
};

class GreenApple : public GreenFood, public IFruit {
    virtual int GetAverageSeedCount() const override {return 5;}
};

(I realize these interfaces don't make perfect sense. Sorry.)

In some cases, I might have a collection of IFood objects, but in others I'll have a collection of IFruit objects. In reality the interface represented by IFood has 5-8 functions.

If this code would compile and run I'd be set. Unfortunately it fails because the compiler doesn't realize GreenApple implements the IFood API (missing GetColor). This does exist in the base GreenFood that we extend, but splitting up the interface implementation doesn't seem to make the compiler happy.

For each function in IFood, I can have the IFruit implement it and just directly call the ParentClass::functionName(). But with 5-8 functions in IFood and dozens of potential fruit types, that isn't as elegant as I'd like.

I'm curious if there is any solution to get the compiler to find these missing APIs in the parent class, or any good way for me to restructure my Interfaces to keep things elegant?

I can provide more concrete examples if needed. Thank you for any tips!

Upvotes: 0

Views: 1458

Answers (2)

Sam Varshavchik
Sam Varshavchik

Reputation: 118292

There are a couple of ways of working around that.

First of all, GreenApple knows that it inherits from both GreenFood and IFruit, and IFruit inherits its own requirement for GetColor() to be implemented, which is implemented in GreenFood(), so from one point of view it's GreenApple's responsibility to take care of this:

class GreenApple : public GreenFood, public IFruit {
    virtual int GetAverageSeedCount() const override {return 5;}
    virtual int getColor() const override {
         return GreenFood::GetColor();
    }
};

A slightly cleaner variation of this, that avoids the ugly explicit class reference:

class GreenFood : public IFood {
    virtual int GetColor() const override
    {
         return GreenFoodColor();
    }

    int GreenFoodColor() const
    {
           return GREEN;
    }
};

class GreenApple : public GreenFood, public IFruit {
    virtual int GetAverageSeedCount() const override {return 5;}
    virtual int getColor() const override {
         return GreenFoodColor();
    }
};

This'll work, but this still winds up with GreenApple inherits from IFood twice. This can create its own issues.

An alternative solution that avoids the duplicate inheritance is to not have IFruit directly in inherit from IFood, but instead require its subclass to inherit from that interface, in some way:

class IFruit {
public:
    virtual int GetAverageSeedCount() const = 0;

    virtual IFood &getIFood()=0;

    virtual const IFood &getIFood() const=0;
};

class GreenApple : public GreenFood, public IFruit {
    virtual int GetAverageSeedCount() const override {return 5;}

    virtual IFood &getIFood() override { return *this; }

    virtual const IFood &getIFood() const { return *this; }
};

Anything that's looking at IFruit knows that there's an IFood hiding somewhere around here, someplace. Perhaps shove an operator IFood & in there, to make the whole process even more transparent.

Upvotes: -1

CGD
CGD

Reputation: 269

Use Virtual keyword for base classes in IFruit and GreenFood.

class IFood {
public:
    virtual ~IFood() = default;
    virtual int GetColor() const = 0;
};

class IFruit : public virtual IFood {
public:
    virtual int GetAverageSeedCount() const = 0;
};

class GreenFood : public virtual  IFood {
    virtual int GetColor() const override {return GREEN;}
};

class GreenApple : public GreenFood, public IFruit {
    virtual int GetAverageSeedCount() const override {return 5;}
};

Upvotes: 2

Related Questions