【发布时间】:2023-01-26 22:28:13
【问题描述】:
当 mixin 中的方法依赖于 mixin 到的类的方法时,我想问一下设计模式。下面的例子是用 python 写的,但我相信其他语言也会出现这个问题。
例如,假设我有以下两个混入,我想注入到某个类中。在下面的代码中,我想注入 f 但 f 要求类 mixin-ed 实现 g 因为 g 将在 f 中使用
from abc import ABC, abstractmethod
class MixinBase(ABC):
@abstractmethod
def f(self, a: int) -> int: ...
# the main function that we want to mix-in
@abstractmethod
def g(self, a: int) -> int: ...
# a method that we know that is used in f()
class Mixin1(MixinBase):
def f(self, a: int) -> int: return self.g(a) ** 2
class Mixin2(MixinBase):
def f(self, a: int) -> int: return self.g(a) + 2
现在,我的问题是,注入此类 mixin 的更好做法是什么?
例子
我可以想出以下两种混合方式。第一种情况是隐式的:
class ImplicitExample:
def g(self, a: int): return a
## and other methods ...
class ImplicitExampleWithMixin1(ImplicitExample, Mixin1): ...
class ImplicitExampleWithMixin2(ImplicitExample, Mixin2): ...
这种混合是隐含的,因为 ImplicitExample 的实现者隐含地知道混入对 ImplicitExample 的依赖性。
另一种混合方式是显式继承MixinBase,以便保证g被实现。
class ExplicitExample(MixinBase):
def g(self, a: int): return a
# and other methods ...
class ExplicitExampleWithMixin1(ExplicitExample, Mixin1): ...
class ExplicitExampleWithMixin2(ExplicitExample, Mixin2): ...
我认为以上两个例子各有利弊。第一个显式是更简单的依赖图,但实现者必须知道隐式依赖。另一方面,对于第二个显式示例,实现者的精神压力较小,但这会导致菱形依赖图。如果 MixIn 很少,那还好,但如果很多,精神压力可能会很大。
【问题讨论】:
-
第一个很奇怪,因为
g似乎没有理由存在除了期待使用混合的子类。 -
也就是说,
ImplicitExample本身是另一种混入,但与Mixin的子类(过于)紧密耦合。 -
第二个也遇到同样的问题。如果你想要一个公共的
g,在MixinBase的直接子类中定义它,然后有它作为Mixin1和Mixin2的共同父级。 -
考虑反转依赖关系。如果
f需要依赖其他人提供g,那么就让它采取必要的方法作为争论,并让调用者担心如何让适当的函数通过。 -
您只需要此模式的“名称”,操作系统是否有您想要实现但无法实现的结果?否则,不确定为什么这会有一个 distict 名称——你只是在使用抽象基础。
标签: python multiple-inheritance mixins