Let's say we have a concrete class Apple
. (Apple objects can be instantiated.)
Now, someone comes and derives an abstract class Peach
from Apple. It's abstract because it introduces a new pure virtual function. The user of Peach is now forced to derive from it and define this new function. Is this a common pattern? Is this correct to do?
Sample:
class Apple
{
public:
virtual void MakePie();
// more stuff here
};
class Peach : public Apple
{
public:
virtual void MakeDeliciousDesserts() = 0;
// more stuff here
};
Now let's say we have a concrete class Berry
. Someone derives an abstract class Tomato
from Berry. It's abstract because it overwrites one of Berry's virtual functions, and makes it pure virtual. The user of Tomato has to re-implement the function previously defined in Berry. Is this a common pattern? Is this correct to do?
Sample:
class Berry
{
public:
virtual void EatYummyPie();
// more stuff here
};
class Tomato : public Berry
{
public:
virtual void EatYummyPie() = 0;
// more stuff here
};
Note: The names are contrived and do not reflect any actual code (hopefully). No fruits have been harmed in the writing of this question.
Re Peach from Apple:
Re Tomato from Berry:
Juice()
- imposes certain requirements and makes certain promises. Derived classes' implementations of Juice()
must require no more and promise no less. Then a DerivedTomato IS-A Berry and code which only knows about Berry is safe.Possibly, you will meet the last requirement by documenting that DerivedTomatoes must call Berry::Juice()
. If so, consider using Template Method instead:
class Tomato : public Berry
{
public:
void Juice()
{
PrepareJuice();
Berry::Juice();
}
virtual void PrepareJuice() = 0;
};
Now there is an excellent chance that a Tomato IS-A Berry, contrary to botanical expectations. (The exception is if derived classes' implementations of PrepareJuice
impose extra preconditions beyond those imposed by Berry::Juice
).
It would seem to me like an indication of a bad design. Could be forced if you wanted to take a concrete definition from a closed library and extend it and branch a bunch of stuff off it, but at that point I'd be seriously considering the guideline regarding Encapsulation over Inheritance.. If you possibly can encapsulate, you probably should.
Yeah, the more I think about it, this is a Very Bad Idea.
If you use the recommended practice of having inheritance model "is-a" then this pattern would pretty much never come up.
Once you have a concrete class, you are saying that it is something that you can actually create an instance of. If you then derive an abstract class from it, then something that is an attribute of the base class is not true of the derived class, which should set of klaxons that something's not right.
Looking at your example, a peach is not an apple, so it should not be derived from it. Same is true for Tomato deriving from Berry.
This is where I would usually advise containment, but that doesn't even seem to be a good model, since an Apple does not contain a Peach.
In this case, I would factor out the common interface -- PieFilling or DessertItem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With