我想知道在红宝石中创建双向父子关系的最佳方式是哪种。例如,以下代码:
class Bar
def initialize(name)
@name = name
end
end
class Foo
attr_accessor :children
def initialize(names = %w(john ben maudy))
@children = names.map{|n| Bar.new(n)}
end
end
有了这个,很容易从Foo
个实例到其Bar
个孩子。但它没有相反的方式。例如:
# Instanciates a Foo and get his first son
bar = Foo.new.children.first
# bar can't get to his parent. Essentially, I want
bar.foo
我到目前为止唯一的想法是在self
方法中传递Bar#new
,以保留bar
对象的引用,但我想避免这种情况。你能用更好的方式解释我吗?
编辑
从谷歌我可以找到caller method,它会给我一个行号。但是我正在寻找一个对象引用,比行号更抽象。
编辑2
您可以执行此操作in python
编辑3
这spinoff of pry似乎最合适。如果有人能找到一种没有宝石的方法,我可以用它回答我自己的问题。
答案 0 :(得分:3)
这是strong composition,如果没有对复合材料的引用,则无法构建组件。
这意味着您需要使用该组合初始化它们(即将self
传递给Bar
实例)。这是执行此操作的方法,因为您需要对所有者的引用。您可以在之后设置它,例如bar.foo = foo
,但它将实现更松散的链接,聚合。
如果我们没有收到参考资料,我们需要在某处找到它。我们可以使Foo
成为一个单例,但它是一个反模式,因为它就像一个全局变量。 Bar
个实例可以向另一个对象请求他们Foo
的引用。如果有Foo
的多个实例,则需要使用id来标识它们,但这意味着Bar
个实例需要该ID。那么,为什么不直接使用Foo
?
作为结论,恕我直言,唯一的好解决方案是在构建新的Foo
实例时传递Bar
的引用。如果意图是尊重OO范式,恕我直言,隐藏重要的关系实现是不好的主意。
使用Bar
实例
Foo
实例的代码
class Bar # Class of the component
attr_reader :foo
# the component must be initialized/built
# with a ref to the composite foo
def initialize(name, foo)
@name = name
@foo = foo
end
end
class Foo # Class of the composite
attr_reader :children
def initialize(names = %w(john ben maudy))
@children = names.map do |n|
Bar.new(n, self) # pass self to build the component
end
end
end
bar = Foo.new.children.first
bar.foo
关于binding_of_caller
这是一个可以访问callstack的interesting project。在当前情况下,它将在Bar::initialized
方法中用于查找作为调用者的复合,并要求它返回self
。
但是,该项目需要extend the Ruby implementation。那不是纯粹的红宝石。
答案 1 :(得分:2)
我认为你应该把foos传递给Bar的构造函数,如:
class Bar
def initialize(name, parent)
@name = name
@parent = parent
end
end
class Foo
attr_accessor :children
def initialize(names = %w(john ben maudy))
@children = names.map{|n| Bar.new(n, self)}
end
end
这样每个栏都会引用创建它的foo。
如果你真的不想把foo传递给bar的构造函数,你也可以做类似下面的事情,但我认为它不会让你受益匪浅。
class Bar
attr_accessor :foo
def initialize(name)
@name = name
end
end
class Foo
attr_accessor :children
def initialize(names = %w(john ben maudy))
@children = names.map{|n| Bar.new(n)}
end
def get_nth_child(n)
bar = @children[n]
bar.foo = self
bar
end
end
然后你访问原来的foo,如:
Foo.new.get_nth_child(1).foo