【问题标题】:Case when method in mixin depends on a method of the class that mixin-ed to当 mixin 中的方法依赖于 mixin 到的类的方法时的情况
【发布时间】:2023-01-26 22:28:13
【问题描述】:

当 mixin 中的方法依赖于 mixin 到的类的方法时,我想问一下设计模式。下面的例子是用 python 写的,但我相信其他语言也会出现这个问题。

例如,假设我有以下两个混入,我想注入到某个类中。在下面的代码中,我想注入 ff 要求类 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的直接子类中定义它,然后有作为Mixin1Mixin2 的共同父级。
  • 考虑反转依赖关系。如果f需要依赖其他人提供g,那么就让它采取必要的方法作为争论,并让调用者担心如何让适当的函数通过。
  • 您只需要此模式的“名称”,操作系统是否有您想要实现但无法实现的结果?否则,不确定为什么这会有一个 distict 名称——你只是在使用抽象基础。

标签: python multiple-inheritance mixins


【解决方案1】:

就个人而言,我可能只是在 Mixin 的文档字符串中记录依赖关系,并将 Mixin.g() 定义为文档字符串加上一行:raise NotImplementedError()。我也可能会跳过在 Mixin 类的所有外部文档中定义 g()。详细信息可能取决于个人偏好以及您希望Mixin 的使用方式/由谁使用;一个为内部使用定义 Mixin 的小组项目将对可移植性和文档有不同的要求,而不是公共可访问的开源库的一部分。这些要求应该塑造你在课堂设计中的想法。

如果你有更复杂的依赖关系——多个事物以不同的方式调用g(),几个必须一起工作的嵌套函数/mixins,或者其他复杂情况,那么我建议多考虑一下依赖关系图。但是正如你在这里介绍的那样?哪个都好

我不会强调细节的原因:

  • 根据定义,Mixins don't have to be independent, instantiable classes
  • Diamond inheritance in Python 可能会令人困惑,但只要您给g() 一个不太可能被意外复制的记录完备的名称,那么它本身就不是问题……而且您可能无论如何都应该这样做。
  • 对于您的单元测试,实例化一个单独的类,它以某种最小的方式定义MixinUser.g()并练习Mixin.f()。这演示了正确的用法(因此它是文档)并确保如果子类化 Mixin 的人遵循您的意图,Mixin.f() 将按预期工作。

简而言之,您有几个选择,但只要您记录依赖关系并为其编写一些测试,您作为问题的一部分提出的大多数问题在实践中就不太可能成为问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-18
    • 2011-07-02
    • 1970-01-01
    • 2011-11-01
    • 2015-04-15
    • 2012-09-17
    • 2016-06-04
    相关资源
    最近更新 更多