【问题标题】:How to construct two objects, with each other as a parameter/member如何构造两个对象,彼此作为参数/成员
【发布时间】:2010-09-27 04:01:59
【问题描述】:

我有两个类,每个类都需要一个彼此的实例才能起作用。通常,如果一个对象需要另一个对象来运行,我喜欢在构造函数中传递它。但在这种情况下我不能这样做,因为一个对象必须在另一个对象之前实例化,因此第二个对象不存在传递给第一个对象的构造函数。

我可以通过将第一个对象传递给第二个对象的构造函数来解决此问题,然后在第一个对象上调用 setter 以将第二个对象传递给它,但这似乎有点笨拙,我想知道是否有更好的方式:

backend = new Backend();
panel = new Panel(backend);
backend.setPanel();

我从未对 MVC 进行过任何研究;我想我在这里处理一个模型(后端)和一个视图或控制器(面板)。我可以从 MVC 中获得什么见解?

【问题讨论】:

  • 您的后端/数据层不需要知道任何关于您的表示层的信息。其实应该没有UI层的概念。让它们像这样耦合只会导致很多问题。

标签: model-view-controller language-agnostic oop


【解决方案1】:

在循环构造场景中,我会使用工厂类/工厂方法。我通常会将构造逻辑设为工厂私有(使用友元构造、包级保护或类似方法),以确保没有人可以在不使用工厂的情况下构造实例。

setter/constructor 的使用确实是两个类和工厂之间契约的一部分,所以我就用哪个方便。

正如已经指出的,你真的应该尝试找到一个非循环的解决方案。

【讨论】:

    【解决方案2】:

    最好避免循环引用。我个人会尝试重新考虑我的对象。

    【讨论】:

    • 这个答案并不是特别有用,因为在某些情况下孩子需要知道自己的父母是谁。当您需要这样做时,有安全的解决方案(如代理)。
    • 嗯,这对我很有帮助,因为它证实了我的怀疑,即我需要重新考虑我的对象。 :) 我想我将使用另一个答案中建议的 MVC 侦听器/事件通知类型设计。我认为它仍然是圆形的,但更好一些。
    【解决方案3】:

    是时候看看 MVC 了。 :-) 当你遇到模型-视图-控制器的情况时,共识是模型不应该知道视图控制器(MVC 通常表现为 M-VC),但视图总是知道模型.

    如果模型需要告诉视图某些事情,它会通过通知它的侦听器来实现,它可能有多个。你的观点应该是其中之一。

    【讨论】:

    • 我要补充一点,视图的模型“知识”应该通过定义明确的接口/api 进行引导。应该可以将一种模型替换为实现相同 API 的另一种模型,例如用于测试。
    • 确实如此。这是首先拥有 MVC 模式的主要原因之一。
    • 非常感谢,保罗。我已经发布了关于这将如何使我的代码变得更好的描述。
    【解决方案4】:

    首先,与其他人在这里所说的相反,循环引用没有固有的问题。例如,Order 对象应该具有对下订单人的 Customer 对象的引用。类似地,Customer 对象拥有他所下订单的列表是很自然的。

    在基于引用的语言(如 Java 或 C#)中完全没有问题。在基于值的语言(如 C++)中,您必须小心设计它们。

    也就是说,你的设计:

    backend = new Backend();
    panel = new Panel(backend);
    backend.setPanel(panel);
    

    这几乎是唯一的方法。

    【讨论】:

      【解决方案5】:
      panel = new Panel(backend);
      

      你在这个例程中这样做

        Public Sub Panel(ByVal BackEnd as BackEnd)
              Me.MyBackEnd = BackEnd
              BackEnd.MyPanel = Me
        End Sub
      

      你不需要 BackEnd.SetPanel

      最好使用代理。代理通过引发事件将一个对象链接到另一个对象。父母交给孩子一个代理。当孩子需要父母时,它会在代理上调用 GetRef 方法。代理然后引发一个事件,父使用该事件将自身返回给代理,然后将其交给子。

      事件/委托机制的使用避免了任何循环引用问题。

      所以你有(假设后端是这里的'父')

        Public Sub Panel(ByVal BackEnd as BackEnd)
              Me.MyBackEnd = BackEnd.Proxy
              BackEnd.MyPanel = Me
        End Sub
      
        Public Property MyBackEnd() as BackEnd
           Set (ByVal Value as BackEnd)
              priBackEndProxy = BackEnd.Proxy
           End Set
           Get
              Return priBackEndProxy.GetRef
           End Get
        End Property
      

      这里是关于循环引用问题的更全面的讨论。虽然它专注于在 Visual Basic 6.0 中修复它。

      Dynamic Memory Allocation

      另一种解决方案是将 Panel 和 BackEnd 聚合到另一个对象中。如果两个元素都是 UI 控件并且需要以协调的方式运行,这很常见。

      最后,就 MVC 而言,我建议改用 Model View Presenter 方法。

      基本上,您的表单实现了一个 IPanelForm 接口。它向一个名为 Panel 的类注册自己,该类执行所有 UI 逻辑。 BackEnd 应该具有 Panel 可以在模型更改时挂钩的事件。 Panel 处理事件并通过 IPanelForm 接口更新表单。

      1. 用户点击按钮

      2. 用户单击按钮的表单传递给 Panel

      3. 面板处理按钮并从后端检索数据

      4. 面板格式化数据。

      5. Panel 使用IPanelForm 接口在Form 上显示数据。

      【讨论】:

        【解决方案6】:

        我一直在推迟实施这里学到的经验教训,让我有足够的时间思考正确的方法来做到这一点。正如其他人所说,当后端对象的属性发生变化时,明确区分后端对象的监听器绝对是要走的路。它不仅会解决我在这个问题中提出的具体问题,还会让这段代码中的许多其他不良设计气味看起来更好。实际上有很多不同的后端类(按照我在示例中使用的通用类名),每个类都有自己对应的 Panel 类。甚至还有一些地方可以移动一些东西,以按照相同的模式将其他类对分成后端/面板对,并减少大量传递垃圾作为参数。

        这个答案的其余部分将针对特定语言,因为我使用的是 Java。

        我并不十分担心“JavaBeans”,但我发现过去遵循基本的 JavaBean 约定对我非常有帮助:基本上,使用标准的 getter 和 setter 来获取属性。原来有一个我不知道的 JavaBean 约定在这里真的会有所帮助:绑定属性。绑定属性是可通过标准 getter 和 setter 获得的属性,它们在更改时触发 PropertyChangeEvents。 [我不确定,但 JavaBeans 标准可能会指定所有属性都应该是“绑定属性”。在这一点上,与我无关。还要注意,通过使用 BeanInfo 类来定义 JavaBean 的确切接口,“标准”getter 和 setter 可能非常不标准,但我也从不使用它。](我选择遵循的主要其他 JavaBean 约定或不是在每种情况下都合适的是无参数构造函数;我已经在这个项目中关注它,因为这些后端对象中的每一个都必须是可序列化的。)

        我找到了this blog entry,它非常有助于让我了解绑定的属性/PropertyChangeEvents 问题,并帮助我制定如何重新编写此代码的计划。

        现在我所有的后端对象都继承自一个名为 Model 的通用类,它提供了该系统中每个后端所需的一些东西,包括序列化支持。我将创建一个附加类 JavaBean 作为 Model 的超类,它将提供我需要的 PropertyChangeEvent 支持,由每个 Model 继承。我将更新每个模型中的设置器以在调用时触发 PropertyChangeEvent。我也可能有几个类继承了 JavaBean,这些类在技术上与这些类不同,但它们也可以从将其他类注册为它们的侦听器中受益。 JavaBean 类可能没有完全实现 JavaBean 规范;正如我所说,有几个细节我不关心。但是对于这个项目来说已经足够了。听起来我可以通过从 java.awt.Component 继承来获得所有这些,但这些在任何意义上都不是我可以证明的组件,所以我不想这样做。 (我也不知道它可能需要什么开销。)

        一旦每个模型都是一个 JavaBean,并带有 PropertyChangeEvent 支持,我将进行大量代码清理:当前保留对面板的引用的模型将被更新,面板将自己注册为侦听器。干净多了!模型不必知道(也不应该一开始就知道)当属性更新时 Panel 应该调用自己的哪些方法。

        【讨论】:

          猜你喜欢
          • 2011-07-31
          • 2012-01-19
          • 2014-01-08
          • 2019-12-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-08-21
          • 2013-06-12
          相关资源
          最近更新 更多