Reputation: 5532
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
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
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