I have two classes, each of which use methods from the other class. But
because they initialize an instance of each other, this creates an
infinite loop!
I would like to keep them separate and not merge them. What's the best
/ most elegant / proper way of solving this problem? There is probably
something simple that I'm just not seeing.
class A
def initialize b = nil @b = b || B.new(self)
end
end
class B
def initialize a = nil @a = a || A.new(self)
end
end
a = A.new
p a # #<A:0x21dbf9e0 @b=#<B:0x21dbf9cc @a=#<A:0x21dbf9e0 ...>>>
···
On 09/01/2013 10:53 AM, Andrew S. wrote:
I have two classes, each of which use methods from the other class. But
because they initialize an instance of each other, this creates an
infinite loop!
I would like to keep them separate and not merge them. What's the best
/ most elegant / proper way of solving this problem? There is probably
something simple that I'm just not seeing.
I obviously don't know the specifics of what you're doing, but I have to say the general pattern has a distinct odour to it. This smacks of too-tightly coupled classes, and probably low cohesion. I'd seriously consider refactoring this, and thinking it out a bit further so your classes are more independent.
···
On Sep 1, 2013, at 12:53 PM, Andrew S. <lists@ruby-forum.com> wrote:
I have two classes, each of which use methods from the other class. But
because they initialize an instance of each other, this creates an
infinite loop!
I would like to keep them separate and not merge them. What's the best
/ most elegant / proper way of solving this problem? There is probably
something simple that I'm just not seeing.
The `b = nil` means that b is optional. If it is not passed by the caller (as in: `A.new`), then the initial value of b is nil. If an argument is provided by the caller (as in: `A.new(self)`), then that value is assigned to b.
In those two cases, this line
@b = b || B.new(self)
will behave differently.
If b was passed in as an argument to A.new, then the effect is
@b = b
If not, then the effect is
@b = B.new(self)
Having these two cases is what makes it possible to set up the circular reference that you wanted. When you construct an A like so:
a = A.new
there is no argument. So A.new calls B.new. The trick is that it calls B.new with an argument: B.new(self). Since an argument is passed to B.new, it simply assigns that argument (an instance of A) to its @a attribute.
Hope that helps...
···
On 09/01/2013 03:25 PM, Andrew S. wrote:
Hi,
Thanks for replying! Well, I've tested it and it works (see extended
example below). But I don't know why it works!
Why set the arguments to nil? Why does @b = b || B.new(self) work?
A little "dependency injection" to decouple your classes...
class A
attr_reader :container
def initialize container @container = container
end
def b
container.b
end
def do_something_with_b
b.say_bar
end
def say_foo
puts "foo"
end
end
class B
attr_reader :container
def initialize container @container = container
end
def a
container.a
end
def do_something_with_a
a.say_foo
end
def say_bar
puts "bar"
end
end
class MyContainer
def a @a ||= A.new(self)
end
def b @b ||= B.new(self)
end
end
mc = MyContainer.new
mc.a.do_something_with_b # ==> bar
mc.b.do_something_with_a # ==> foo
···
On 09/02/2013 11:14 AM, Tamara Temple wrote:
On Sep 1, 2013, at 12:53 PM, Andrew S. <lists@ruby-forum.com> wrote:
I have two classes, each of which use methods from the other class. But
because they initialize an instance of each other, this creates an
infinite loop!
I would like to keep them separate and not merge them. What's the best
/ most elegant / proper way of solving this problem? There is probably
something simple that I'm just not seeing.
I obviously don't know the specifics of what you're doing, but I have to say the general pattern has a distinct odour to it. This smacks of too-tightly coupled classes, and probably low cohesion. I'd seriously consider refactoring this, and thinking it out a bit further so your classes are more independent.
On 2013-09-02, at 12:31 AM, Joel VanderWerf <joelvanderwerf@gmail.com> wrote:
On 09/01/2013 03:25 PM, Andrew S. wrote:
Hi,
Thanks for replying! Well, I've tested it and it works (see extended
example below). But I don't know why it works!
Why set the arguments to nil? Why does @b = b || B.new(self) work?
The `b = nil` means that b is optional. If it is not passed by the caller (as in: `A.new`), then the initial value of b is nil. If an argument is provided by the caller (as in: `A.new(self)`), then that value is assigned to b.
In those two cases, this line
@b = b || B.new(self)
will behave differently.
If b was passed in as an argument to A.new, then the effect is
@b = b
If not, then the effect is
@b = B.new(self)
Having these two cases is what makes it possible to set up the circular reference that you wanted. When you construct an A like so:
a = A.new
there is no argument. So A.new calls B.new. The trick is that it calls B.new with an argument: B.new(self). Since an argument is passed to B.new, it simply assigns that argument (an instance of A) to its @a attribute.
... or at least do not employ such a circular dependency for construction.
Kind regards
robert
···
On Mon, Sep 2, 2013 at 8:14 PM, Tamara Temple <tamouse.lists@gmail.com> wrote:
On Sep 1, 2013, at 12:53 PM, Andrew S. <lists@ruby-forum.com> wrote:
I have two classes, each of which use methods from the other class. But
because they initialize an instance of each other, this creates an
infinite loop!
I would like to keep them separate and not merge them. What's the best
/ most elegant / proper way of solving this problem? There is probably
something simple that I'm just not seeing.
I obviously don't know the specifics of what you're doing, but I have to say the general pattern has a distinct odour to it. This smacks of too-tightly coupled classes, and probably low cohesion. I'd seriously consider refactoring this, and thinking it out a bit further so your classes are more independent.