What is good design in this simple case:
Let’s say I have a base class Car with a method FillTank(Fuel fuel) where
fuel is also a base class which have several leaf classes, diesel, ethanol etc.
On my leaf car class DieselCar.FillTank(Fuel fuel) only a certain type of fuel
is allowed (no surprises there:)). Now here is my concern, according to my interface every car can be tanked with any fuel, but that seems wrong to me, in every FillTank() implementation check the input fuel for the correct type and if not throw error or something.
How can I redesign such case to a more accurate one, is it even possible?
How to design a base method which takes a base-class for input without getting these “strange results”?
If there is a hard boundary between types of cars and types of fuel, then
FillTank()has no business being in the baseCarclass, since knowing that you have a car doesn’t tell you what kind of fuel. So, for this to ensure correctness at compile time,FillTank()should be defined in the subclasses, and should only take theFuelsubclass that works.But what if you have common code that you don’t want to repeat between the subclasses? Then you write a protected
FillingTank()method for the base class that the subclass’s function calls. Same thing goes forFuel.But what if you have some magic car that runs on multiple fuels, say diesel or gas? Then that car becomes a subclass of both
DieselCarandGasCarand you need to make sure thatCaris declared as a virtual superclass so you don’t have twoCarinstances in aDualFuelCarobject. Filling the tank should Just Work with little or no modification: by default, you’ll get bothDualFuelCar.FillTank(GasFuel)andDualFuelCar.FillTank(DieselFuel), giving you an overloaded-by-type function.But what if you don’t want the subclass to have a
FillTank()function? Then you need to switch to run time checking and do what you thought you had to: make the subclass checkFuel.typeand either throw an exception or return an error code (prefer the latter) if there is a mismatch. In C++, RTTI anddynamic_cast<>are what I would recommend. In Python,isinstance().