从子项引用父对象

时间:2015-07-01 00:33:54

标签: ruby oop recursion variable-assignment

假设你有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]

现在您的递归为userwidgetsowneruser.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:...>]>

2 个答案:

答案 0 :(得分:1)

您正在寻找的是自定义作家。 parentObject类上没有BaseObject方法或等效方法,因为实现该方法需要对象跟踪碰巧指向它的每个其他对象。但是,当您需要该功能时,自定义编写器可以使其简单易用。

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 << widget之类的操作,则不会为新窗口小部件分配所有者。如果您希望能够这样做,则必须monkeypatch Array like this(不推荐),否则您必须创建可能继承自WidgetCollection的{​​{1}}类。这就是ActiveRecord::Associations的作用。说到这一点,如果您碰巧使用Rails,请务必使用ActiveRecord为您完成所有这些操作。看起来你问的是普通的红宝石,所以我给你一个香草红宝石的答案。

答案 1 :(得分:1)

想到分享我已经提出的解释。它没有可靠的证据,但可能有所帮助。

首先,像这样的循环链接对象没有任何问题。如果循环链出现问题,代码将无法正常工作,它会崩溃或显示错误。所以它可能在某种程度上处理这种循环引用,但如果您理解变量只是对象的引用,那么它确实有意义。

我的意思是当您只是访问用户实例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]