Let's say I'm trying to create a package for the users to make new classes of cars. Originally I have a simple code structure:
class Car:
def __init__(self, diesel_engine):
self._engine = diesel_engine
def _start(self):
pass
# Then the user can do
class Truck(Car):
pass
But now I want to enhance the functionality and cater to both conventional car and electric car in the package. The problem is that the constructor of the base class Car
would now need to accept either diesel_engine
or electric_motor
; and also, the class method _start
would perform different things accordingly (so for example, it will call _ignite
or _close_circuit
).
Am I better off adding the derived classes in my package:
class Conventional(Car):
def _start(self):
pass
class ElectricVehicle(Car):
def _start(self):
pass
or is there a better way by using, e.g., decorator? Essentially I want the end-user to make new derived classes by passing in appropriate arguments and deriving from a single base class (because all other class methods are identical except for _start
)
CodePudding user response:
The abstract Car
should accept an abstract Engine
.
class Car:
def __init__(self, engine: Engine):
self._engine = engine
def start(self):
self._engine.start()
In the real world, engines can be treated as black boxes with some inputs (gas/electricity) and some outputs (axle rotation, exhaust). Engine
s do their best to hide things a Car
does not need to deal with whenever possible. The following interface provides a way to start the engine and control its speed:
class Engine:
def start(self):
raise NotImplementedError
def set_speed(self, speed):
raise NotImplementedError
class DieselEngine(Engine):
def start(self):
self._ignite()
def set_speed(self, speed):
self._set_gas_rate(speed / KILOMETERS_PER_LITER)
At some point, though, it becomes better to compose and use data to make decisions rather than an inheritance hierarchy. Unless the hierarchy is strictly disjoint with no overlaps across multiple traits, I would avoid inheritance and use other ways to emulate "polymorphic behavior".
For instance, if Car
needs to pipe the engine's exhaust to the outside air, it's a bit silly to define another subclass of Engine
called ExhaustEngine
. At this point, interfaces (HasExhaust
), PODs, dictionaries, and so on might be easier to work with.
CodePudding user response:
The derived classes should set the engine type. The generic car doesn't know that. Same with, for example, number of wheels.
class Car:
def __init__(self):
self.engine = 'unknown'
class Truck(Car):
def __init__(self):
super().__init__()
self.engine = 'diesel'
class ElectricCar(Car):
def __init__(self):
super().__init__()
self.engine = 'electric'