【问题标题】:Modifying state of other objects in a constructor: design no-no?在构造函数中修改其他对象的状态:设计禁止?
【发布时间】:2010-12-07 01:56:23
【问题描述】:

我正在重构一些代码并发现了这个(当然是简化的,但总体思路):

class Variable:
    def __init__(self):
        self.__constraints = []

    def addConstraint(self, c):
        self.__constraints.append(c)

class Constraint:
    def __init__(self, variables):
        for v in variables:
            v.addConstraint(self)

Constraint 的构造函数修改了其他对象的状态而不是它自己的状态,这对我来说有点奇怪。其他人怎么看 - 这可以吗,或者它是重构的主要候选者?

编辑:我关心的不是父/子关系,而是它在构造函数内部而不是在单独的方法中链接。

【问题讨论】:

  • 在初始化程序中执行它(不是构造函数,那将是方法 __new__ -- 到您运行 __init__ 时,实例已经构造,但尚未初始化)保证建立了诸如“每个约束总是存在于一定数量的变量上”的类不变量。否则将意味着所谓的“初始化”约束实际上不满足类不变量;为什么你会认为这种情况更可取?!请查看我的答案以获取更多示例...!

标签: python oop refactoring constructor


【解决方案1】:

我完全同意@djna 的回答,即特定用例是完全合法的——在这里,这是一个对象需要在“出生时”使用一组指定的注册表进行自我注册的示例。

其中一个非常尖锐且极其常见的子情况是一个观察者对象,它的存在严格用于观察给定的可观察对象——将可观察对象传递给观察者的初始化器非常好,并且完全是确保类的正确方法不变的“此观察者类的实例始终与一个可观察者相关联”,如果仅在完成初始化后进行注册,则不会在“出生时”建立。

其他类似的情况包括,例如一个必须始终存在于容器窗口中的小部件对象:实现它有点奇怪,而不是让小部件将父级作为初始化参数并告诉父级“嗨,我是你的新孩子!”。

至少在那些 1-many 情况下,您可以想象强制父级或 observable 拥有一个创建和注册新对象的方法。在像这样的多对多情况下,这种方法的有点由内而外的性质被揭示了——因为约束必须用多个变量注册,所以要求其中任何一个特定的变量来创建都是“违反规定的”约束。另一方面,您提供的代码非常自然。

只有在不能合理地被框定为新对象“注册自身”的情况下,我才会感到有些怀疑(还有一些其他合法的,例如在出生时创建和注册其他辅助对象的对象,但它们无处可去几乎一样常见)。

【讨论】:

    【解决方案2】:

    我个人不一定反对,但是……

    我会选择一种使用模式,并坚持下去。在您的情况下,由于 Variable 已经有一个干净的 addConstraint 方法,我更喜欢使用它。

    否则,您需要添加良好的检查以防止用户构造约束,然后将其添加到变量类(从而添加两次)。

    话虽如此,但使用约束之类的东西,我可能不会这样做。约束似乎是概念上独立于变量的实体。我认为没有任何逻辑原因不能将相同的约束添加到两个单独的变量中。我会这样做,以便您构建约束,然后手动添加它们,特别是出于这个原因。

    【讨论】:

    • 如果一个约束可以明智地“附加到任何变量上”(这似乎几乎没有意义),那么将其构建为未附加的可能是有意义的;但是在通常情况下,给定的习语在约束总是附加到 1+ 个变量的情况下效果更好(__init__ 中的assert variables 会使这一点变得清晰,但如果variables 可以是任何可迭代的,那么断言可以“消费”它,给定的代码仍然很好)。
    【解决方案3】:

    当您有两个密切相关的对象时(即仅其中一个没有意义),这是一种常见用法。最常见的情况:父子关系。当您将子代添加到父代(即parent.children.append(child))时,您通常还会更新child.parent 指针。

    【讨论】:

    • 我知道这是一个常见的设计,这就是我将其重构为的原因。不同之处在于它不是在单独的方法中完成,而是通过将 parent 传递给 child 的构造函数并在构造函数中执行更新来完成。
    • 我认为在构造函数或方法中执行此操作没有太大区别。构造函数只是一个被调用一次的特殊方法。因此,如果孩子没有父母就没有意义,我会添加孩子的构造函数以将所有代码放在一个地方。
    【解决方案4】:

    我将其视为一种自我注册模式。 “你好,我是新来的,请允许我加入。”

    我可能更喜欢使用不同名称的方法,以便更明确目的,但我实际上非常喜欢这种方法。

    【讨论】:

    • IMO 这样做完全没问题。在构造函数中而不是其他方法中进行自注册意味着a)无法忘记它并且b)执行两次的可能性较小
    【解决方案5】:

    我同意你的看法。那是倒退。原因可能有一些很好的原因,但目前还不清楚编程,如果迟早会咬人。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-04
      • 2022-09-27
      • 2016-01-06
      • 2015-06-03
      • 2016-01-13
      • 1970-01-01
      相关资源
      最近更新 更多