OO design: avoiding an infinite loop in classes initializing each other?

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.

Simplified code:

class A
  def initialize
    b = B.new
  end

  def method_foo
    # do something with b
  end
end

class B
  def initialize
    a = A.new
  end

  def method_bar
    # do something with a
  end
end

Any help gratefully received!

···

--
Posted via http://www.ruby-forum.com/.

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.

Simplified code:

class A
   def initialize
     b = B.new
   end

   def method_foo
     # do something with b
   end
end

class B
   def initialize
     a = A.new
   end

   def method_bar
     # do something with a
   end
end

Any help gratefully received!

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?

Extended example:

class A
  def initialize b = nil
    @b = b || B.new(self)
  end

  def do_something_with_b
    @b.say_bar
  end

  def say_foo
    puts "foo"
  end
end

class B
  def initialize a = nil
    @a = a || A.new(self)
  end

  def do_something_with_a
    @a.say_foo
  end

  def say_bar
    puts "bar"
  end
end

a = A.new
b = B.new

b.do_something_with_a # => foo
a.do_something_with_b # => bar

···

--
Posted via http://www.ruby-forum.com/.

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?

I think I understand. Thank you very much!

···

--
Posted via http://www.ruby-forum.com/.

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.

Why not let Ruby do the defaulting for you? For example:

#!/usr/bin/env ruby

class A
def initialize(b = B.new(self))
   @b = b
end

def method_foo
   # do something with b
end
end

class B
def initialize(a = A.new(self))
   @a = a
end

def method_bar
   # do something with a
end
end

a = A.new
p a
b = B.new
p b

__END__

produces the right results.

ratdog:tmp mike$ ./try.rb
#<A:0x007fafc4128b50 @b=#<B:0x007fafc4128b28 @a=#<A:0x007fafc4128b50 ...>>>
#<B:0x007fafc4128970 @a=#<A:0x007fafc4128948 @b=#<B:0x007fafc4128970 ...>>>

Is this bad form?

Mike

···

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.

Hope that helps...

--

Mike Stok <mike@stok.ca>
http://www.stok.ca/~mike/

The "`Stok' disclaimers" apply.

... 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.

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Why not let Ruby do the defaulting for you? For example:

#!/usr/bin/env ruby

class A
  def initialize(b = B.new(self))
    @b = b
  end

...

produces the right results.

Even better! However, it is More Magic....

···

On 09/02/2013 08:37 AM, Mike Stok wrote: