如何始终如一地子类化合作的整体 类
How to consistently subclass an ensemble of cooperating classes
假设我有一组(可能是抽象的)基classes,它们以某种方式合作,我想以这样的方式子class它们,子classes 知道其各自的合作子 classes(例如,它具有其他 classes 作为 class 属性)。
字面上添加属性对于少数 classes 来说似乎真的很混乱。
我能想到的一种方法是 class 抽象 classes 的属性,它会引用 dict
ionary 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
知道 ConcreteC
和 ConcreteD
而它应该只知道 ConcreteA
。 类 之间的依赖关系是模糊的。相反,对依赖项进行硬编码应该是一种更清晰的解决方案,因为 类 之间的关系是明确的。
因此这似乎比 mixin 更好:
class ConcreteB(AbstractB):
_a_class = ConcreteA
class ConcreteC(AbstractC):
_a_class = ConcreteA
_b_class = ConcreteB
但有时硬编码 ConcreteB
和 ConcreteA
之间的关系并不是最好的选择。如果您想使用 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)
现在,您确定 B
和 C
共享相同的 a_class
。
此设计可帮助您确保依赖关系明确且一致。
假设我有一组(可能是抽象的)基classes,它们以某种方式合作,我想以这样的方式子class它们,子classes 知道其各自的合作子 classes(例如,它具有其他 classes 作为 class 属性)。
字面上添加属性对于少数 classes 来说似乎真的很混乱。
我能想到的一种方法是 class 抽象 classes 的属性,它会引用 dict
ionary 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
知道 ConcreteC
和 ConcreteD
而它应该只知道 ConcreteA
。 类 之间的依赖关系是模糊的。相反,对依赖项进行硬编码应该是一种更清晰的解决方案,因为 类 之间的关系是明确的。
因此这似乎比 mixin 更好:
class ConcreteB(AbstractB):
_a_class = ConcreteA
class ConcreteC(AbstractC):
_a_class = ConcreteA
_b_class = ConcreteB
但有时硬编码 ConcreteB
和 ConcreteA
之间的关系并不是最好的选择。如果您想使用 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)
现在,您确定 B
和 C
共享相同的 a_class
。
此设计可帮助您确保依赖关系明确且一致。