Imagine I have the following in python (actual language does not really matter, since I have the problem in other languagues like php too. A base class "CarModel" and a derived class "TruckModel" from "CarModel".
class CarModel:
def __init__(self):
self.__wheel_count: int = 0
def set_wheel_count(self, value: int) -> None:
self.__wheel_count = value
class TruckModel(CarModel):
def __init__(self):
super().__init__()
self.__load_in_kg: int = 0
def set_load_in_kg(self, value: int) -> None:
self.__load_in_kg= value
If I now have a mapping class, which should convert e.g. a Dict to my Model, how can I reuse the mapping-method for my derived class? Especially if I have a base class with a lot of setter-methods, I have to repeat the code, which I don't like ("dry").
class VehicleMapper:
def map_car_dict_to_car_model(dict: Dict) -> CarModel:
model: CarModel = CarModel()
model.set_wheel_count(dict['wheelCount'])
return model
def map_truck_dict_to_truck_model(dict: Dict) -> TruckModel:
model: TruckModel= TruckModel()
model.set_load_in_kg(dict['loadInKg'])
model.set_wheel_count(dict['wheelCount']) # ??? How can I re-use the map-method for the base class here ???
return model
I could move the mapping methods to the model classes, then this would work. But I got teached, that model classes should only "hold" data and shouldn't do anything. That's why there are mapper classes, right?
A more cleaner and solid way is to use separate mappers/factories.
And it's even more justified as in your case you also need a configurable mapping from dict keys to respective model property name.
Consider the following pattern:
class CarModel:
def __init__(self):
self.__wheel_count: int = 0
def wheel_count(self, value: int) -> None:
self.__wheel_count = value
wheel_count = property(None, wheel_count)
class TruckModel(CarModel):
def __init__(self):
super().__init__()
self.__load_in_kg: int = 0
def load_in_kg(self, value: int) -> None:
self.__load_in_kg= value
load_in_kg = property(None, load_in_kg)
class VehicleFactory:
"""Vehicle base factory"""
__model__ = None
__map_keys__ = None
@classmethod
def create(cls, data):
model = cls.__model__()
for k, attr in cls.__map_keys__.items():
setattr(model, attr, data[k])
return model
class CarFactory(VehicleFactory):
__model__ = CarModel
__map_keys__ = {'wheelCount': 'wheel_count'}
class TruckFactory(VehicleFactory):
__model__ = TruckModel
__map_keys__ = {'wheelCount': 'wheel_count',
'loadInKg': 'load_in_kg'}
Usage:
car = CarFactory.create({'wheelCount': 4})
print(vars(car)) # {'_CarModel__wheel_count': 4}
truck = TruckFactory.create({'wheelCount': 4, 'loadInKg': 500})
print(vars(truck)) # {'_CarModel__wheel_count': 4, '_TruckModel__load_in_kg': 500}