While working on some Object Oriented training, I found myself in some cognitive disagreement. I was flip-flopping between two implementations. One used inheritance, one used composition.
If you follow along much, you'll know that I'm very strongly on the use composition side. There are a very few situations I find inheritance a valid choice, and almost all of them end up with the class using composition becoming a passthrough. Inheritance removes the boilerplate and creates what I call "Knowledge Classes".
My current situation creates a knowledge class... but some of the composition is shared across classes.
The premise is a pizza shop (very similar to this one) and the sticking point is the toppings.
Simplified situation: Toppings - Regular cost $0.10; Meat cost $0.25.
Regular: Cheese, Mushroom, Olives
Meat: Pepperoni, Bacon, Ham
NOTE: A lot of the code below is simplified and breaks other practices. The discussion is about composition vs inheritance, simplicity helps.
public abstract class Topping{
private readonly string _description;
private readonly ToppingCost _cost;
protected Topping(string desc, ToppingCost cost){ ... };
public string Description() => _description;
public float Cost() => _cost.PrimitiveValue();
}
I also have defined the cost of meat and regular into their own ToppingCost
objects; what they look like, doesn't matter.
The challenge I have is between these two implementions of a specific topping;
Using Additional Inheritance
public abstract class RegularTopping : Topping{
protected RegularTopping(string desc):base(desc, new RegularToppingCost()){}
}
public sealed class MushroomTopping : RegularTopping{
public MushroomTopping() : base("Mushroom") { }
}
Using Composition
public sealed class MushroomTopping : Topping
{
public MushroomTopping() : base("Mushroom", new RegularToppingCost()) { }
}
Each Regular Topping will have to provide the same topping price. This is repeating code.
This is why I go back and forth. I prefer composition in this case, strongly. Inheritance feels wrong... evne though it's already using inheritance for Topping
.
Why?
I have to figure out why I don't like using inheritance here. Why does it feel wrong?
There are two major reasons I found that I strongly favor, and will argue for, composition over inheritance here.
The lesser of the two is that MushroomTopping
no longer controls it's own price. That critical detail has been removed from this classes' control.
The other is the realization that to change the cost of a single topping, we have to change that objects inheritance strucutre. It took me a moment, and it feels pretty profound and VERY anti-OO: To change an implementation detail, NOT BEHAVIOR - we have to change it's inheritance strucutre.... Whoa... That screams wrong to me.
When you use inheritance to reduce composition code, you require inheritance changes for implementation details.
To me, this is the crucial point of why we don't want to structure this code with inheritance. To change the implementation detail, we have to change the heirarchy. Changing what something IS for something as simple as it's cost changing... feels wrong. Really really wrong.
Composition, Not Inheritance
In this case, composition, to me, is a clear winner. We can change the cost and not have to change anything other than the object used in composition. That's reusable code. If we have to change the inheritance structure, we do not have a good inheritance design in place.