红宝石中的父母/子女关系?

时间:2013-05-10 20:24:42

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

编辑

从谷歌我可以找到caller method,它会给我一个行号。但是我正在寻找一个对象引用,比行号更抽象。

编辑2

您可以执行此操作in python

编辑3

spinoff of pry似乎最合适。如果有人能找到一种没有宝石的方法,我可以用它回答我自己的问题。

2 个答案:

答案 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