I understand why polymorphism achieved through method overriding is very useful. I am asking what problems, if any, might arise with trying to suppress it in certain situations, at the time the polymorphic object is received as an argument (not at the time its class is defined!).
class Car describes the behavior of a car. class FlyingCar describes the behavior of a car that can transform and fly.
I received from somewhere the object of class Car or its subclass. I have no control over what they pass me.
I know that due to the technical limitations of my graphics engine I cannot display the a flying car. Or perhaps I want the player to finish the particular mission without using the flying capability. Thus, I was thinking to simply disable the car's ability to fly by making it look as if it's an object of class Car. I was thinking of using downcasting, but it appears it won't work.
It maybe impossible, but if I find a way to do that in the language that I use, is it bad design? If so, why, and what's the alternative?
I can't use something like a copy constructor to create an object of class Car from the one I received because the resulting copying of all the data is too expensive (the Car object is huge).
Thanks!
EDIT:
I want to avoid choosing a specific language in this question. Once I pick a language, the answer may well be "it's technically impossible", or "it's possible, but the required hack is too dangerous", etc.
I want to understand whether this is bad design for reasons unrelated to the (in)ability of a certain language to support it.
My opinion would be generally no.
The reason is that it even if you could somehow make your FlyingCar behave only like it would if it were a Car from this point forward, it's still already been operated on as if it were a FlyingCar, and may no longer be in a valid state for a Car.
Maybe the reason your graphics engine can't display a FlyingCar is because of the textures it uses. But someone's already called a load_appropriate_textures
method on it, which has stored its texture data inside it. Changing the FlyingCar into a Car would change what happened if you called load_appropriate_textures
again, but FlyingCar doesn't override the render_car
method, it just puts data where render_car
will find it. So some other poor programmer in your organisation will just end up trying to debug why a Car is failing to render with some error message about FlyingCar textures.
Maybe that won't happen in this one particular case. But it could. And someone could modify Car and FlyingCar later in a way that introduces this sort of problem.
In general, to a FlyingCar "as if" it were a Car, you really have to repeat all the initialisation (and subsequent modifications) again. Repeating later modifications is generally not possible (because they're not recorded), and repeating the initialisation means nothing more than constructing a new Car.
So it seems like "in general" it's a bad idea. In any particular case, if you can find a way to do it, maybe you'll decide it's acceptable. Programmers make compromises every day, it happens. But if it's not possible to do this with full generality, then you always run the risk that later perfectly reasonable changes will be made to Car and/or FlyingCar that make your hacks no longer work.
Really, it sounds like FlyingCar needs to have the functionality to disable its flying functionality. Something like that is always really hard to bolt on after the fact.
You could use composition instead of inheritance (it sounds like your Car
object needs to be refactored into smaller classes anyway: the Car object is huge
).
The Car
object could then contain a component that gives it the capability to fly. To disable the flying ability of the car you then just need to temporarily (or permanently if you want) remove the flying component from the Car
object.
In general, when we create a FlyingCar, we ensure that we extend Car in a way that would work correctly. Hopefully we only rely on Car's interface that Car promises to keep unchanged; if we also rely on some other code in Car, we do it because we own the code.
On the other hand, this doesn't work in the opposite direction. When someone takes a FlyingCar tries to modify it to use it as a Car, there's no guarantee that things won't break, regardless of how careful the user is. After all, FlyingCar only promises that it will behave as a variant of Car if used as is; not that it will behave as Car after someone tries to takes some pieces out of it.
For example, FlyingCar might have modified the functions of various controls at construction. If its methods have been disabled, it won't become Car; it will be just broken.
To answer the question directly: yes, it would be wrong because suppressing certainn subclasses from being passed in would violate Liskov Substitution Principle
The fact that you are needing to do that is a strong smell that refractory may be in order...