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オブジェクトの参照を保持することですが、これは避けたいと思います。より良い方法を説明していただけますか?

編集

Google から、呼び出し元のメソッドを見つけることができました。これにより、行番号が得られます。ただし、行番号よりも少し抽象的なオブジェクト参照を探していました。

編集2

Pythonでこれを行うことができます

編集3

pry のこのスピンオフが最適のようです。誰かが宝石なしでこれを行う方法を見つけることができれば、それで私自身の質問に答えるかもしれません.

4

2 に答える 2

3

これは強力な合成であり、コンポジットへの参照なしではコンポーネントを構築できませんでした。

つまり、そのコンポジットでそれらを初期化する必要があります (つまりselfBarインスタンスに渡します)。所有者への参照が必要なため、これがその方法です。たとえばbar.foo = foo、後で設定することもできますが、より緩いリンクである集約を実装します。

参照を受け取っていない場合は、どこかで見つける必要があります。シングルトンを作成することはできますがFoo、グローバル変数のようなものであるためアンチパターンです。Barインスタンスは、別のオブジェクトに自分の の参照を要求できますFoo。のインスタンスが複数ある場合はFoo、たとえば ID を使用してそれらを識別する必要がありますが、これはBarインスタンスにその ID が必要であることを意味します。では、のインスタンスの参照を直接操作してみませんFooか?

結論として、私見の唯一の良い解決策は、新しいインスタンスFooを構築するときに参照を渡すことです。Bar意図が OO パラダイムを尊重することである場合、IMHO はその重要な関係の実装を隠すのは悪い考えです。

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

コールスタックにアクセスできるようにする興味深いプロジェクトです。現在のケースでは、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

このようにして、各バーはそれを作成した 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 に答える