Here's an analogy of our concrete problem to demonstrate the issue at hand.
We need to manufacture cars, with either petrol or diesel related parts (the parts can be the engine and the exhaust - e.g. different requirements for exhaust for diesel engines).
Engine
+ engineCapacity: Integer
DieselEngine: Engine
PetrolEngine: Engine
+ octanes (Integer)
We then have the car manufacturer interface:
public interface CarManufacturer {
Engine createEngine(Integer engineCapacity);
Exhaust createExhaust(Engine engine);
}
and its implementors
DieselCarManufacturer
PetrolCarManufacturer
There's the CarManufacturerGateway
that orchestrates the car building process, selecting a CarManufacturer
from a ManufacturersStore
according to some criteria and then building a car via:
final var engine = carManufacturer.createEngine(engineCapacity);
final var exhaust = carManufacturer.createExhaust(engine);
Problem definition
To create the proper exhaust, in the PetrolCarManufacturer
, we need to use the petrol engine's octanes number (probably a bad analogy) from within the PetrolCarManufacturer::createExhaust
.
But, the CarManufacturer
interface accepts an Engine, thus we cannot access the petrol engine octanes to decide upon the exhaust configuration to create.
Solutions
- instanceof + downcasting: I think it's considered a bad practice and can fail during runtime
- using double dispatch somehow?
- using parameterized types on the interface, e.g.
public interface CarManufacturer<T extends Engine>
and then each implementor will define the concrete type of engine it makes. This also requires theManufacturersStore
to be parameterized in the same way:ManufacturersStore<T extends Engine>
. The latter makes me question this option, as it reads weirdly "a car manufacturer store parameterized on the type of engines it makes".
N.B.: If the analogy fails to demonstrate the issue at hand, I can try to rewrite it with our concrete example.
Engine
base class (with a diesel engine returning a nonsense value like0
or throwing an exception)? CouldcreateExhaust
ask the engine for what type (or types) of exhaust would be appropriate?Engine
interface with a detail that only makes sense for one part of the abstraction (certainly the analogy has failed me here).