【问题标题】:Referencing parent objects from children从子对象引用父对象
【发布时间】:2015-07-01 00:33:53
【问题描述】:

假设你有一个User 类:

class User
  attr_accessor :widgets
end

还有一个Widget

class Widget
  attr_accessor :owner
end

然后您将一些小部件分配给用户:

user = User.new
widget = Widget.new
widget.owner = user
widget2 = Widget.new
widget2.owner = user
user.widgets = [widget, widget2]

现在你有一个 userwidgetsowner 的递归。 user.inspect 为每个小部件显示一次相同的 user 引用,使输出混乱:

user.widgets.first.owner.widgets.first.owner
=> #<User:0x00000001cac820 @widgets=[#<Widget:0x00000001ca45f8 @owner=#<User:0x00000001cac820 ...>>, #<Widget:0x00000001c87a20 @owner=#<User:0x00000001cac820 ...>>]>                                      

如果我们要将此数据结构简化为我们将拥有的哈希:

{ user:
    { widgets: [ { widget: ... },
                 { widget: ... } ]
    }
}

我们可以传递它而不是分配widget.owner,并且引用父级user 很容易。

我想知道是否有一种方法可以通过子对象访问父对象,而不必将owner 分配给所有子对象,一个可以像这样工作的接口:

user = User.new
widget = Widget.new
user.widgets = [widget]
widget.parent
# => #<User:... @widgets=[#<Widget:...>]>

【问题讨论】:

  • 你有什么问题?
  • 有没有办法通过子对象访问父对象而不必将owner分配给所有子对象?
  • 任何你想访问的对象都必须有一个引用,否则你不能访问它。
  • 是的,但是对于哈希数据结构示例,我可以传递该哈希并从小部件中引用它,以便访问父对象的属性。它不会被实例化为User,但至少数据会在那里。
  • 是的,这是有道理的,但似乎是多余的。从数据建模的角度来看,您不会想要无限散列。但从 OOD 的角度来看,无论对象嵌套的深度如何,您都可能希望访问小部件所有者。

标签: ruby oop recursion variable-assignment


【解决方案1】:

您正在寻找的是自定义编写器。在ObjectBaseObject 类上没有parent 方法或等效方法,因为实现它需要对象跟踪碰巧指向它的所有其他对象。但是,当您需要该功能时,自定义编写器使其易于实现。

class Widget
    attr_accessor :owner
end

class User
  attr_reader :widgets

  def widgets=(widgets)
    @widgets = widgets
    widgets.each do |widget|
      widget.owner = self
    end
  end
end

user = User.new
widget = Widget.new
user.widgets = [widget]
widget.owner #=> #<User:... @widgets=[#<Widget:...>]>

请注意,此自定义编写器仅涵盖常规分配,例如 user.widgets = [widget]。如果您想做user.widgets &lt;&lt; widget 之类的操作,则不会为新小部件分配所有者。如果您希望能够做到这一点,您要么必须monkeypatch Array like this(不推荐),要么必须创建一个可能继承自ArrayWidgetCollection 类。这就是ActiveRecord::Associations 所做的。说到这一点,如果你碰巧在使用 Rails,一定要考虑使用 ActiveRecord 来为你做这一切。看起来你在问普通的旧红宝石,所以我给你一个香草红宝石的答案。

【讨论】:

  • 我认为您正确回答了上述问题。但我仍然想知道从 OOD 的角度来看,这是否是最好的方法。这似乎是一种非常以数据为中心的方法。您以 ActiveRecord 为例,这是我试图避免的模式。我想知道使用messaging 来实现相同的目标会是什么感觉。
  • 你确定这段代码真的只为widget.owner返回#&lt;User:... @widgets=[#&lt;Widget:...&gt;]&gt;吗?
【解决方案2】:

想分享我想出的解释。它没有确凿的证据,但可能会有所帮助。

首先,这样的循环链接对象没有任何问题。如果循环链出现问题,代码将无法正常工作,它会崩溃或显示错误。所以它可能会以某种方式处理这些循环引用,但如果你明白变量只是对对象的引用,那真的很有意义。

我的意思是当您简单地访问一个用户实例user 时,它不会只是递归地加载其中的所有内容。它只是什么都不做,或者只是取出参考。真正设置递归的是inspect 方法,它递归地检查实例中的所有实例变量。但它确实使用.... 处理深度检查。

所以你真正的问题应该只是让检查看起来紧凑。您可以覆盖该方法,使其不会递归,并给您一个很好的消息。示例:

class User
  attr_accessor :widgets
  def initialize
    @widgets =[]
  end
  def inspect
    "[User:objid=#{object_id};widgets=#{widgets.size}]"
  end
end

class Widget
  attr_accessor :owner

  def inspect
    "#[Widget:objid=#{object_id}]"
  end
end

界面可以保持不变。

user = User.new
widget = Widget.new
widget.owner = user
widget2 = Widget.new
widget2.owner = user
user.widgets = [widget, widget2]
user.widgets.first.owner.widgets.first.owner
# => #[User:objid=-590412418;widgets=2]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-24
    • 1970-01-01
    • 2012-11-13
    • 1970-01-01
    • 2017-07-14
    • 1970-01-01
    相关资源
    最近更新 更多