dimanche 15 août 2021

How to consistently subclass an ensemble of cooperating classes

Suppose I have a set of (possibly abstract) base classes which cooperate in a certain way, and I want to subclass them in such a way that the subclasses are aware of its respective co-operating subclasses (e.g. it has the other classes as class attributes).

Literally adding attributes seems really messy for more than a handful of classes.

One way I can think of doing this is to class properties for the abstract classes which would reference a dictionary class attribute (same dictionary for all classes), via mixin to avoid repeating code in the superclass module. This way, I only need to add one attribute for each subclass (and add a dictionary referencing all the classes in the module), see the code below.

Is there an established design pattern to achieve this sort of thing?

Example:

abstract_module:

from abc import ABC

_module_classes_dict = {}


class _ClassesDictMixin:
    _classes_dict = dict()

    @classmethod
    @property
    def _a_class(cls):
        return cls._classes_dict['a']
    
    @classmethod
    @property
    def _b_class(cls):
        return cls._classes_dict['b']

    @classmethod
    @property
    def _c_class(cls):
        return cls._classes_dict['c']


class AbstractA(ABC):
    pass


class AbstractB(_ClassesDictMixin, ABC):
    _classes_dict = _module_classes_dict
    # # Basic solution without using the dict
    # _a_class = AbstractA


class AbstractC(_ClassesDictMixin, ABC):
    _classes_dict = _module_classes_dict
    # # Basic solution without using the dict
    # _a_class = AbstractA
    # _b_class = AbstractB


class AbstractD(_ClassesDictMixin, ABC):
    _classes_dict = _module_classes_dict
    # # Alternative solution without using the dict
    # _a_class = AbstractA
    # _b_class = AbstractB
    # _c_class = AbstractC


_module_classes_dict.update(a=AbstractA, b=AbstractB, c=AbstractC, d=AbstractD)

concrete_module:

from abstract_module import AbstractA, AbstractB, AbstractC, AbstractD

_module_classes_dict = {}


class ConcreteA(AbstractA):
    pass


class ConcreteB(AbstractB):
    _classes_dict = _module_classes_dict
    # # Basic solution without using the dict
    # _a_class = ConcreteA


class ConcreteC(AbstractC):
    _classes_dict = _module_classes_dict
    # # Basic solution without using the dict
    # _a_class = ConcreteA
    # _b_class = ConcreteB


class ConcreteD(AbstractD):
    _classes_dict = _module_classes_dict
    # # Basic solution without using the dict
    # _a_class = ConcreteA
    # _b_class = ConcreteB
    # _c_class = ConcreteC


_module_classes_dict.update(a=ConcreteA, b=ConcreteB, c=ConcreteC, d=ConcreteD)

Aucun commentaire:

Enregistrer un commentaire