【问题标题】:Python Borg Multiple Inheritance and Shared StatePython Borg 多重继承和共享状态
【发布时间】:2016-07-19 05:12:44
【问题描述】:

我在一个有趣的项目中遇到了这个问题,我必须告诉一个类在指定的开始和结束时间(以及 c 的间隔)的特定时间段内执行操作。

为了论证,请考虑 A 类(具有我调用的 API 的类)必须调用 B 类(它调用 C 类,然后又调用 D 类)和 E(它又调用 F 类和 G 类)。

A -> B -> C -> D

A -> E -> G

现在 B C 和 E 类需要关于时间的上下文。我目前已经进行了设置,以便 A 类将上下文传递给 B 类和 E 类,然后它们根据需要依次传递上下文。

我正在尝试找出在不传递上下文的情况下解决此要求的最佳方法,尽管我很讨厌它,但我正在考虑使用 Highlander(或 Singleton)或 Borg 模式(Monostate 的一种变体) )。我只是想知道我对这些模式的选择。

选项 1

使用传统的博格,例如:

class Borg:
    __shared_state = {}
    def __init__(self):
        self.__dict__ = self.__shared_state

我可以简单地在任何地方实例化这个类,并可以访问我想要的全局状态。

选项 2

单态的另一种变体:

class InheritableBorg:
    __time = (start, end, interval)
    def __init__(self):
        pass

    @property
    def time(self):
        return __time

    @time.setter
    def time(self, time):
        __time = time

理论上,这将允许我通过以下方式进行扩展:

class X(InheritableBorg):
    pass

为了扩展,我会这样做:

class NewInheritableBorg(InheritableBorg):
    __time = (0, 0, 'd')

然后理论上我可以利用多重继承,然后就可以一次访问多个 borgs,例如:

class X(InheritableBorg1, InheritableBorg2):
    pass

我什至可以选择性地覆盖某些东西。

选项 3

如果可能的话,使用一个包装的嵌套函数作为类装饰器/包装器。但是,我只能使用一次,并且需要传递函数句柄。这是基于代理/委托理念的混合。

这在我的脑海中并不具体,但本质上是这样的:

def timer(time)
    def class_wrapper(class_to_wrap):

        class Wrapper(class_to_wrap):
            def __init__(self, *a, **kw):
               super().__init__(*a, **kw)
            def get_time(self):
               return time
        return Wrapper
    return class_wrapper

@timer(time)
class A_that_needs_time_information:
    pass

I think that might work... BUT I still need to pass the function handle.

总结

所有这些都是可能的解决方案,我倾向于多继承 Borg 模式(尽管类包装器很酷)。

  1. 常规的 Borg 模式必须实例化很多次,以至于仅存储一组值似乎开销太大。

  2. Borg mixin 的实例化次数与类的实例化次数一样多。我不明白测试会变得多么困难。

  3. 包装器是超通用的,并且相对容易测试。从理论上讲,因为它是一个函数闭包,所以我应该能够改变一些东西,但它本质上变成了一个单例(在这种情况下,这似乎太复杂了,我不妨只使用常规的单例模式)。此外,传递函数句柄会违背目的。

除了这三种方法,还有其他方法吗?有什么更好的方法(因为没有 DI,Borg 似乎不容易测试)?有没有我似乎遗漏的缺点?

或者,我可以坚持我现在拥有的……根据需要消磨时间。它满足松散耦合要求和 DI 最佳实践……但它太麻烦了。必须有更好的方法!

【问题讨论】:

    标签: python inheritance design-patterns


    【解决方案1】:

    作为一般经验法则,请尽量保持简单。您正在为一个相对简单的问题考虑非常复杂的解决方案。

    根据您提供的信息,您似乎可以将执行操作的请求包装在还包含上下文的对象中。然后你只是在不同的类之间传递一个对象。例如:

    class OperationRequestContext:
        def __init__(self, start_time, end_time):
            self.start_time = start_time
            self.end_time = end_time
    
    class OperationRequest:
        def __init__(self, operation, context):
            self.operation = operation
            self.context = context
    

    如果有其他要求证明考虑更复杂的解决方案是合理的,您应该指定它们。

    【讨论】:

    • 这就是我现在正在做的事情。但我想要一些我不会四处传递东西的东西。这主要是作为 Python 中设计模式的练习而完成的,因此过度复杂化是故意的。在大多数其他项目中,我倾向于尽可能远离全局可变状态:)
    猜你喜欢
    • 1970-01-01
    • 2012-06-21
    • 2017-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-25
    相关资源
    最近更新 更多