3

我想知道在 ruby​​ 中创建双向父子关系的最佳方法是哪种。例如,下面的代码:

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对象的引用,但我想避免这种情况。你能给我解释一个更好的方法吗?

编辑

从谷歌我可以找到调用者方法,它给了我行号。但是我一直在寻找一个对象引用,比行号更抽象一点。

编辑 2

你可以在python中做到这一点

编辑 3

这种撬动的衍生产品似乎是最合适的。如果有人能在没有宝石的情况下找到一种方法,我可能会用它来回答我自己的问题。

4

2 回答 2

3

这是一个强大的组合,如果没有对组合的引用,就无法构建组件。

这意味着您需要使用该组合来初始化它们(即传递selfBar实例)。这是执行此操作的方法,因为您需要对所有者的引用。例如,您可以在之后设置它bar.foo = foo,但它会实现更松散的链接,即聚合。

如果我们没有收到参考,我们需要在某个地方找到它。我们可以创建Foo一个单例,但它是一种反模式,因为它就像一个全局变量。Bar实例可以向另一个对象请求它们的Foo. 如果有多个实例Foo,则需要使用 id 来识别它们,但这意味着Bar实例需要该 id。那么,为什么不直接使用实例的引用Foo呢?

Foo作为结论,恕我直言,唯一好的解决方案是在构建新Bar实例时传递一个引用。如果意图是尊重 OO 范式,恕我直言,隐藏那个重要的关系实现是个坏主意。

BarFoo实例初始化实例的代码

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

这是一个有趣的项目,可以访问调用堆栈。在当前情况下,它将在Bar::initialized方法中用于查找复合体,谁是调用者,并要求它返回self

但是,该项目需要扩展 Ruby 实现。那么它就不是纯红宝石了。

于 2013-05-10T22:16:46.977 回答
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

这样,每个 bar 都会引用创建它的 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
于 2013-05-10T22:17:09.743 回答