【问题标题】:pros and cons of using factory vs regular constructor使用工厂与常规构造函数的优缺点
【发布时间】:2012-02-11 02:25:27
【问题描述】:

(使用 Python 3.2,尽管我怀疑它是否重要。)

我有class Dataclass Rules 和课程Result。我用小写来表示类的一个实例。

rules 对象包含的规则如果应用于data 对象,则可以创建result 对象。

我正在决定将实际将规则应用于数据的(相当复杂且不断发展的)代码放置在何处。我可以看到两个选择:

  1. 将该代码放入类Result 方法中,例如parse_rulesResult 构造函数将rules 对象作为参数,并将其传递给self.parse_rules

  2. 将该代码放入一个新类ResultFactoryResultFactory 是一个单例类,它有一个方法,比如build_result,它以rules 作为参数并返回一个新建的result 对象。

这两种方法的优缺点是什么?

【问题讨论】:

    标签: python oop inheritance design-patterns class-factory


    【解决方案1】:

    GRASP design principles 提供了在面向对象设计中将职责分配给类和对象的指南。例如,Creator 模式建议:一般来说,如果以下一个或多个适用,则 B 类应该负责创建 A 类的实例:

    • B 的实例包含或复合聚合了 A 的实例
    • B 的实例记录 A 的实例
    • B 的实例密切使用 A 的实例
    • B 的实例具有 A 实例的初始化信息并在创建时传递它。

    在您的示例中,您有复杂且不断发展的代码来将规则应用于数据。这表明使用Factory Pattern

    将代码放入 Results 是禁忌的,因为 1) 结果不会创建结果,2) 结果不是 信息专家(即他们不具备所需的大部分知识)。

    简而言之,ResultFactory 似乎是一个合理的地方,可以集中了解如何将规则应用于数据以生成结果。如果您尝试将所有这些逻辑推送到结果或规则的类构造函数中,则会导致紧密耦合和内聚损失。

    【讨论】:

    • 感谢您的链接,我现在正在浏览它引用的 Craig Larman 的书。也许我误解了什么,但在我看来,Creator 模式认为 A 类的实例是在 A 的构造函数中创建的,并且只讨论哪个类应该调用该构造函数?
    【解决方案2】:

    第三种情况:

    您可能需要考虑第三种情况:

    • 将代码放入方法Rules.__call__中。
      实例化Result 喜欢:result = rules(data)

    优点:

    • Results 可能完全不知道生成它们的 Rules(甚至可能不知道原来的 Data)。
    • 每个Rules 子类都可以自定义其Result 创建。
    • 感觉很自然(对我来说):Rules 应用于 Data 产量 Result
    • 您将掌握一些 GRASP 原则:
      • 创建者Rules 的实例拥有Result 实例的初始化信息,并在创建时传递。
      • 信息专家:信息专家将把责任交给拥有完成任务所需信息最多的班级。

    副作用:

    • 耦合:您将提高RulesData 之间的耦合:
      • 您需要将整个数据集传递给每个Rules
      • 这意味着每个Rules 应该能够决定将应用哪些数据。

    【讨论】:

    • 请告诉我您的想法,以及是否可以解决您的问题。
    • 我仍然不确定什么有效;当我得到更好的理解时,我会更新。
    【解决方案3】:

    为什么不把规则放在自己的类中呢?如果您创建一个 RuleBase 类,则每个规则都可以从它派生。这样,当数据需要应用规则时,可以使用多态性。数据不需要知道或关心应用了哪些规则实例(除非数据本身知道应该应用哪些规则)。

    当需要调用规则时,数据实例可以全部使用 RuleBase.ExecuteRules() 并将自身作为参数传入。如果 Data 知道需要哪个 Rule,则可以直接从 Data 中选择正确的 Rule 子类。或者可以使用其他一些设计模式,例如责任链,其中 Data 调用该模式并让 Result 返回。

    这将是一次很棒的白板讨论。

    【讨论】:

    • 规则确实存在于它们自己的类中;我没有提到它,但Rules 是一个大类层次结构的根。所以如果我想把转换代码放到Rules里面,当然可以放到这个层次的根目录下。
    【解决方案4】:

    你能让 ResultFactory 成为一个纯函数吗?如果你只需要一个函数,那么创建一个单例对象是没有用的。

    【讨论】:

      【解决方案5】:

      嗯,第二个是彻头彻尾的愚蠢,尤其是在所有单身的情况下。如果Result 需要Rules 来创建一个实例,而没有它就无法创建实例,那么它应该将其作为__init__ 的参数。无需购买图案。

      【讨论】:

      • 那么在我的场景中需要改变什么,才能让类工厂真正有意义?
      • @max:我不知道。应用常识,看看哪种方法更简单。我想将Rules 变成工厂可能是一种选择,如果您可以通过构造函数传递所有数据来单独创建Result 实例。但是如果你总是需要Rules来创建Result,那么它应该是一个ctor参数。
      猜你喜欢
      • 2012-01-31
      • 2016-07-31
      • 1970-01-01
      • 1970-01-01
      • 2010-10-12
      • 1970-01-01
      • 2019-02-17
      • 2014-11-03
      • 1970-01-01
      相关资源
      最近更新 更多