I have a Car
with attribute wheel
I have 4 categories of cars:
- TravelStyle: what is the car purpose
- Race: All of the attribute of child class of Race should be
RaceType
- Pleasure:
- All of the attribute of child class of Pleasure should be
PleasureType
- All children class of Pleasure should have method
sing
- All of the attribute of child class of Pleasure should be
- Race: All of the attribute of child class of Race should be
Note: There will always be only these 2 TravelStyles
- FruitStyle: What is the cars fruit
- Strawberry: They have specific
strawberry
attribute - Bananas: They have specific
banana
attribute
- Strawberry: They have specific
Note: In the future, there WILL be the need to add new fruits, like Cherry
.
From this I can only create these objects:
- RaceStrawberryCar: a Strawberry car with Race style
- PleasureStrawberryCar: a Strawberry car with Pleasure style
- RaceBananaCar: a Banana car with Race style
- PleasureBananaCar: a Banana car with Pleasure style
Here is a sample code of an exhaustive way to write these 4 classes:
from dataclasses import dataclass
class PleasureType:
pass
class RaceType:
pass
@dataclass
class RaceStrawberryCar:
wheel: RaceType
strawberry: RaceType
@dataclass
class PleasureStrawberryCar:
wheel: PleasureType
strawberry: PleasureType
def sing(self, views: int) -> list:
raise NotImplementedError
@dataclass
class RaceBananaCar:
wheel: RaceType
bananas: RaceType
@dataclass
class PleasureBananaCar:
wheel: PleasureType
bananas: PleasureType
def sing(self, views: int) -> list:
raise NotImplementedError
However, here there are few flaws:
- Code repetition
- Hard to extend: Every time we need to add a new fruit ( like Cherry ), we'll need to recreate 2 classes.
My question is therefore: do you have any idea of a design to make this class construction more consistent, and easily extendable ? I set the requirements above.
For now, my first intent was the use of the generic types and inheritance. Here is the sample of code:
from abc import ABC
from dataclasses import dataclass
from typing import Generic, TypeVar
class PleasureType:
pass
class RaceType:
pass
T = TypeVar("T")
@dataclass
class Car(Generic[T], ABC):
wheel: T
@dataclass
class RaceCar(Car[RaceType], ABC):
pass
@dataclass
class PleasureCar(Car[PleasureType], ABC):
def sing(self, views: int) -> list:
raise NotImplementedError
@dataclass
class StrawberryCar(Car[T], ABC):
strawberries: T
@dataclass
class RaceStrawberryCar(StrawberryCar[RaceType], RaceCar):
pass
@dataclass
class PleasureStrawberryCar(StrawberryCar[PleasureType], PleasureCar):
pass
@dataclass
class BananaCar(Car[T], ABC):
bananas: T
@dataclass
class RaceBananaCar(BananaCar[RaceType], RaceCar):
pass
@dataclass
class PleasureBananaCar(BananaCar[PleasureType], PleasureCar):
pass
What I don't like about this approach, is that to set a car of Race
type, we need to define it 2 times:
- Defining the generic type of the base
Fruit
car - Defining the inheritance from a
Race
car
I.e. , in the statement class PleasureBananaCar(BananaCar[PleasureType], PleasureCar):
, we define it 2 times:
- By setting the type of
BananaCar
toPleasureType
- By stating that the class inherits from
PleasureCar
Doing this way, creating this class is totally acceptable:
@dataclass
class HybridBananaCar(BananaCar[PleasureType], RaceCar):
pass
And I don't want to be able to do that.
My first idea to overcome this, was to try to define the generic type when inheriting from a class, i.e.:
- If a class inherits from
RaceCar
, then the generic type is set toRaceType
- If a class inherits from
PleasureCar
, then the generic type is set toPleasureType
I tried this, but it obviously doesn't work:
from abc import ABC
from dataclasses import dataclass
from typing import Generic, TypeVar
class PleasureType:
pass
class RaceType:
pass
T = TypeVar("T")
@dataclass
class Car(Generic[T], ABC):
wheel: T
@dataclass
class RaceCar(ABC):
T = RaceType
pass
@dataclass
class PleasureCar(ABC):
T = PleasureType
def sing(self, views: int) -> list:
raise NotImplementedError
@dataclass
class StrawberryCar(Car[T], ABC):
strawberries: list[T]
@dataclass
class RaceStrawberryCar(StrawberryCar, RaceCar):
pass
@dataclass
class PleasureStrawberryCar(StrawberryCar, PleasureCar):
pass
@dataclass
class BananaCar(Car[T], ABC):
bananas: T
@dataclass
class RaceBananaCar(BananaCar, RaceCar):
pass
@dataclass
class PleasureBananaCar(BananaCar, PleasureCar):
pass
Any suggestion is welcomed. I repeat, I am not stuck with this class structure, any other suggestion for class structure that fulfills my requirements are welcome.