如何始终如一地子类化合作的整体 类

How to consistently subclass an ensemble of cooperating classes

假设我有一组(可能是抽象的)基classes,它们以某种方式合作,我想以这样的方式子class它们,子classes 知道其各自的合作子 classes(例如,它具有其他 classes 作为 class 属性)。

字面上添加属性对于少数 classes 来说似乎真的很混乱。

我能想到的一种方法是 class 抽象 classes 的属性,它会引用 dictionary class 属性(相同的字典所有 classes),通过 mixin 避免在 superclass 模块中重复代码。这样,我只需要为每个 subclass 添加一个属性(并添加一个引用模块中所有 class 的字典),请参见下面的代码。

是否有既定的设计模式来实现这种事情?

示例:

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)

问题可能不是你想的那样。

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

如果我的 类 中的一个人依赖“超过少数 类”,我会担心。这就是我认为的问题,您应该尝试解决。

此外,mixin 解决方案有一个主要缺点:ConcreteB 知道 ConcreteCConcreteD 而它应该只知道 ConcreteA。 类 之间的依赖关系是模糊的。相反,对依赖项进行硬编码应该是一种更清晰的解决方案,因为 类 之间的关系是明确的。

因此这似乎比 mixin 更好:

class ConcreteB(AbstractB):
    _a_class = ConcreteA

class ConcreteC(AbstractC):
    _a_class = ConcreteA
    _b_class = ConcreteB

但有时硬编码 ConcreteBConcreteA 之间的关系并不是最好的选择。如果您想使用 ConcreteA2 而不是 ConcreteA 怎么办?

class ConcreteA(AbstractA):
    pass

class ConcreteA2(AbstractA):
    pass

为了使代码更通用,您可以使用(如您在评论中所写)__init__:

的参数
class ConcreteB(AbstractB):
    def __init__(self, a_class):
        self._a_class = a_class

class ConcreteC(AbstractC):
    def __init__(self, a_class, b_class):
        self._a_class = a_class
        self._b_class = b_class

但是现在,您可能有一组不一致的 类:

b = ConcreteB(ConcreteA)
c = ConcreteC(ConcreteA2, ConcreteB)

如果代码库增长并且对象的初始化跨多个模块分派,则可能会发生这种情况。为避免这种情况,您可以使用 Factory Pattern:

的变体
class Factory:
    def __init__(a_class, b_class, c_class, d_class):
        self._a_class = a_class
        self._b_class = b_class
        self._c_class = c_class

    def concreteA(self):
        return self._a_class()

    def concreteB(self):
        return self._b_class(self._a_class)

    def concreteC(self):
        return self._c_class(self._a_class, self._c_class)

现在,您确定 BC 共享相同的 a_class

此设计可帮助您确保依赖关系明确且一致。