My Design
Verbal Description
I have a class Model
, of course with some methods on it. Besides that I have a class ModelList
whose childclass represents a List of instances of q child class of Model
. Amongst other things, the use of ModelList
child classes is to provide bulk operations which differ from just delegating the operation to each of the elements of the ModelList
. So, the purpose of the ModelList
child classes is to "vectorize" methods of the corresponding Model
class.
I also use the ModelList
in some places where I want to allow either a childclass of Model
or ModelList
allowed as parameter passed to a function.
ModelList
is knowing (and checking) the type that will be accepted for any of its elements. To make the ModelList
childclass know its corresponding Model
child class, I define this as a class variable element_type
on the ModelList
child class.
Each ModelList
child class is closely coupled to a Model
childclass: One ModelList
class belongs to one Model
class. That's why I put the ModelList
child class as an inner classes to their respective Model
class. An here comes my problem: Because ModelList
needs to know Model
and Model
needs to know ModelList
and both during initialisation of each class, I have a circular dependency between my classes.
Minimum Example
I reduced my code to a Minimum Example to make my design easier understandable:
class Model(ABC):
pass
class ModelList(list):
@classmethod
def __init__(self, elements=None):
elements = list() if not elements else elements
for value in elements:
self._check_type(value)
list.__init__(self, elements)
def _check_type(self, val):
if not isinstance(val, self.__class__.element_type):
raise TypeError(
f"{self.__class__} accepts only instances of {self.__class__.element_type} as elements. `{val}` is not!")
The following leads to the Error free variable 'SomeModel' referenced before assignment in enclosing scope
:
class SomeModel(Model):
class List(ModelList):
element_type = SomeModel # this causes the Error
I do not want to decouple
I know I can get rid of the circular dependency by just decoupling the two classes. But I do want both the Model
class know its corresponding ModelList
class and also I want the ModelList
class to know its Model
class. Each Model
class ought to have one and only one List
attached to it.
Is monkey patching appropriate?
I know I can circumvent the dependency by "monkeypatching" my Model
child class like this:
class SomeModel(Model):
pass
class SomeModelList(ModelList):
element_type = SomeModel
SomeModel.List = SomeModelList
For me it feels like this is a sign of a design flaw. I cannot say why but it feels "wrong".
Questions
- Is monkeypatching appropriate here? Or is it indicating a deeper conceptual problem of my design?
- Which other solutions are there?
- How can I redesign to get rid of this circular dependencny?
- Is it possible to evaluate the
element_type
at some point later when the respectiveModel
childclass is defined?
Aucun commentaire:
Enregistrer un commentaire