2013-05-10 3 views
3

Я хочу знать, что является лучшим способом создать двусторонние отношения между отцом и детьми в рубине. Например, следующий код:Родительские/детские отношения в рубине?

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 объектов, но я хотел бы избежать этого. Можете ли вы объяснить мне лучший способ?

EDIT

От гугле я мог бы найти caller method, что дает мне номер строки. Однако я искал ссылку на объект, немного более абстрактную, чем номер строки.

EDIT 2

Вы можете сделать это in python

EDIT 3

Это spinoff of pry, кажется, лучше всего подходит. Если кто-нибудь найдет способ сделать это без драгоценного камня, я могу ответить на его собственный вопрос.

+0

Предоставление 'наследование Bar' от' Foo' может дать вам доступ вы хотите. – user2276204

+0

@ user2276204, но 'Foo' может быть« обезьяной », а bar может быть« бананом »- отношения имеют смысл без вмешательства – fotanus

+0

Возможно, вы захотите изменить свои имена переменных терминов/экземпляров. Это не имеет никакого отношения к отношениям между родителем и ребенком в смысле ОО и, вероятно, путает вещи. –

ответ

3

Это strong composition, где компонент не может быть построен без ссылки на композитный.

Это означает, что вам необходимо инициализировать их с помощью этого композитного материала (например, передать self в экземпляр Bar). Это способ сделать это, так как вам нужна ссылка на владельца. Вы можете установить его после, например, bar.foo = foo, но он будет реализовывать более свободную ссылку, агрегацию.

Если мы не получим ссылку, мы должны найти ее где-нибудь. Мы можем сделать Foo одноэлементным, но это анти-шаблон, поскольку он похож на глобальную переменную. Bar экземпляры могли запросить другой объект, что является ссылкой на их Foo. Если существует несколько экземпляров Foo, вам необходимо идентифицировать их, например, с идентификатором, но это значит, что для экземпляров Bar нужен этот идентификатор. Тогда почему бы не работать напрямую со ссылкой на экземпляр Foo?

Как заключение, ИМХО единственное хорошее решение заключается в передаче ссылки Foo при создании нового экземпляра Bar. Если намерение состоит в уважении парадигмы ОО, ИМХО, это плохая идея скрыть эту важную взаимосвязь.

код для инициализации 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

Это interesting project делает доступным стек вызовов.В текущем случае он будет использоваться в методе Bar::initialized, чтобы найти композит, который является вызывающим, и попросить его вернуть self.

Однако проект должен быть extend the Ruby implementation. Это тогда не чистый рубин.

+0

Интересная ссылка, спасибо - но решение о себе как аргументе, как я уже сказал, я уже думал. – fotanus

+0

Извините, я пропустил ваш последний вопрос. Я уточню свой ответ. – toch

+1

Какова ваша оппозиция к передаче себя? Это правильный и лучший способ моделирования такого рода отношений. –

2

Я думаю, вы должны просто передать FOOS в конструктор Бара, как:

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 
+0

Прочтите мой вопрос еще раз: 'Единственная идея, которую я имел до сих пор, - это передать себя в новый метод Bar #, чтобы сохранить ссылку на объекты панели, но это уродливо.' – fotanus

+0

@fotanus см. мое редактирование. Он будет работать, но я думаю, что первый вариант лучше. – Andbdrew

+0

Интересно, может быть, лучше переписать оператор '[]'? Это не совсем то, что я искал, но это очень интересно. +1 для творчества. – fotanus