从子对象引用父对象
Referencing parent objects from children
假设你有一个 User
class:
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]
现在你有一个递归 user
→ widgets
→ owner
。 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:...>]>
您要找的是一位定制作家。在 Object
或 BaseObject
class 上没有 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 << widget
的操作,则不会为新小部件分配所有者。如果您希望能够做到这一点,您要么必须 monkeypatch Array
like this (not recommended), or you'll have to create a WidgetCollection
class that likely inherits from Array
. That's what ActiveRecord::Associations 做到。说到这一点,如果您碰巧使用 Rails,一定要考虑使用 ActiveRecord 来为您完成所有这些工作。看起来你问的是普通的 ruby 所以我给你一个普通的 ruby 答案。
想分享我的解释。它没有确凿的证据,但可能会有所帮助。
首先,像这样的循环链接对象没有任何问题。如果循环链出现问题,代码将无法正常工作,它会崩溃或显示错误。所以它可能以某种方式处理这些类型的循环引用,但如果你理解变量只是对对象的引用,它真的很有意义。
我的意思是,当您简单地访问一个用户实例 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]
假设你有一个 User
class:
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]
现在你有一个递归 user
→ widgets
→ owner
。 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:...>]>
您要找的是一位定制作家。在 Object
或 BaseObject
class 上没有 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 << widget
的操作,则不会为新小部件分配所有者。如果您希望能够做到这一点,您要么必须 monkeypatch Array
like this (not recommended), or you'll have to create a WidgetCollection
class that likely inherits from Array
. That's what ActiveRecord::Associations 做到。说到这一点,如果您碰巧使用 Rails,一定要考虑使用 ActiveRecord 来为您完成所有这些工作。看起来你问的是普通的 ruby 所以我给你一个普通的 ruby 答案。
想分享我的解释。它没有确凿的证据,但可能会有所帮助。
首先,像这样的循环链接对象没有任何问题。如果循环链出现问题,代码将无法正常工作,它会崩溃或显示错误。所以它可能以某种方式处理这些类型的循环引用,但如果你理解变量只是对对象的引用,它真的很有意义。
我的意思是,当您简单地访问一个用户实例 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]