Circular dependecy on classes

(Anatol Pomozov) #1

I have several classes that have circular dependencies on each other.
See following example.

class Solder
  @can_upgrade_to = [Knight]
  #or use constant from Knight class

  def upgrade!(to)
    raise "Unable to upgrade" unless @can_upgrade_to.contain(to)
  end
end

class Knight < Solder
end

I cant use Knight constant (class name) before Solder and can't use
Solder before Knight.

I am just come from Java lang and for Java circular class deps is not
a problem.
But ruby is a dynamic language and it creates classes just on script
executing. (right?)

So my questions is: how to solve this problem? What is the best way to
deal with it.

···

--
anatol

(Robert K.) #2

The easiest soltuion: Reopen class Solder after you defined class
Knight and put all the
code there.

But you can as well use #inherited to track all sub classes of Solder.
Thus making sub class registration automatic. Something along the
lines of (untested):

class Solder
  def self.inherited(sub_class)
    def sub_class.inherited(cl) superclass.inherited(cl) end
    (@sub_classes ||= []) << sub_class
  end

  def upgrade_to?(cl)
    raise "error" unless cl.ancestors.include? cl
    ...
  end
end

Btw, did you mean "Soldier"?

Kind regards

robert

···

2005/8/26, Anatol Pomozov <anatol.pomozov@gmail.com>:

I have several classes that have circular dependencies on each other.
See following example.

class Solder
  @can_upgrade_to = [Knight]
  #or use constant from Knight class

  def upgrade!(to)
    raise "Unable to upgrade" unless @can_upgrade_to.contain(to)
  end
end

class Knight < Solder
end

I cant use Knight constant (class name) before Solder and can't use
Solder before Knight.

I am just come from Java lang and for Java circular class deps is not
a problem.
But ruby is a dynamic language and it creates classes just on script
executing. (right?)

So my questions is: how to solve this problem? What is the best way to
deal with it.

(Brian Schröder) #3

You could reopen the class. That works like predeclaration in other languages.

class Soldier
end

class Knight < Soldier
end

class Soldier
  @can_upgrade_to = [Knight]

  ...
end

regards,

Brian

···

On 26/08/05, Anatol Pomozov <anatol.pomozov@gmail.com> wrote:

I have several classes that have circular dependencies on each other.
See following example.

class Solder
  @can_upgrade_to = [Knight]
  #or use constant from Knight class

  def upgrade!(to)
    raise "Unable to upgrade" unless @can_upgrade_to.contain(to)
  end
end

class Knight < Solder
end

I cant use Knight constant (class name) before Solder and can't use
Solder before Knight.

I am just come from Java lang and for Java circular class deps is not
a problem.
But ruby is a dynamic language and it creates classes just on script
executing. (right?)

So my questions is: how to solve this problem? What is the best way to
deal with it.

--
anatol

--
http://ruby.brian-schroeder.de/

Stringed instrument chords: http://chordlist.brian-schroeder.de/

(Derek Wyatt) #4

I think you're simply using an instance variable where a method is
what you want. Take advantage of the runtime compile/binding that
you're currently having problems with. Do this instead:

class Solder
~ def can_upgrade_to
~ [Knight]
~ end
end

And your problems go away. You don't have to open it up later. If
you want it private, you can simply make it private.

Regs,
D

Anatol Pomozov wrote:

I have several classes that have circular dependencies on each other.
See following example.

class Solder
  @can_upgrade_to = [Knight]
  #or use constant from Knight class

  def upgrade!(to)
    raise "Unable to upgrade" unless @can_upgrade_to.contain(to)
  end
end

class Knight < Solder
end

I cant use Knight constant (class name) before Solder and can't use
Solder before Knight.

I am just come from Java lang and for Java circular class deps is not
a problem.
But ruby is a dynamic language and it creates classes just on script
executing. (right?)

So my questions is: how to solve this problem? What is the best way to
deal with it.

- --
Derek Wyatt - C++ / Ruby / Unix Programmer
http://derekwyatt.org

(David A. Black) #5

Hi --

I have several classes that have circular dependencies on each other.
See following example.

class Solder

s/der/dier/g :slight_smile:

@can_upgrade_to = [Knight]
#or use constant from Knight class

There's a big difference, though. What you're doing here is assigning
to an instance variable of the class instance Soldier. And what
you're doing here:

def upgrade!(to)
   raise "Unable to upgrade" unless @can_upgrade_to.contain(to)
end
end

is looking up an instance variable of an *instance* of class Soldier.

Simplified example:

class C
   @x = 1
   def meth
     p @x
   end
   p @x # 1
end

C.new.meth # nil

There are two @x's here. They are both instance variables, but they
belong to different objects. One belongs to the object C (which is an
instance of Class). The other belongs to whatever objects are
instances of C.

Constants are a different thing entirely. If you define a constant in
a class, it's visible throughout. It doesn't belong to the class in
the same private way that a class's instance variables do.

David

···

On Fri, 26 Aug 2005, Anatol Pomozov wrote:

--
David A. Black
dblack@wobblini.net

(Robert K.) #6

Doesn't solve the OP'S problem because it was about dependencies:
Knight has to be defined when it is used.

Kind regards

robert

···

2005/8/26, Derek Wyatt <derek@derekwyatt.org>:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I think you're simply using an instance variable where a method is
what you want. Take advantage of the runtime compile/binding that
you're currently having problems with. Do this instead:

class Solder
~ def can_upgrade_to
~ [Knight]
~ end
end

And your problems go away. You don't have to open it up later. If
you want it private, you can simply make it private.

(Anatol Pomozov) #7

Thanks for your quick answers.

Somewhere (i did not remember) I saw following trick

def Soldier
  @can_upgrade_to = [:Knight] #This list != list of all ancestors

  def upgrade?(to)
     @can_upgrade_to.include?(to.to_s.to_sym)
  end
end

but the solution provided by Derek seems more natural for me.

Thanks to all.

···

On 8/26/05, Derek Wyatt <derek@derekwyatt.org> wrote:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I think you're simply using an instance variable where a method is
what you want. Take advantage of the runtime compile/binding that
you're currently having problems with. Do this instead:

class Solder
~ def can_upgrade_to
~ [Knight]
~ end
end

And your problems go away. You don't have to open it up later. If
you want it private, you can simply make it private.

Regs,
D

Anatol Pomozov wrote:
> I have several classes that have circular dependencies on each other.
> See following example.
>
> class Solder
> @can_upgrade_to = [Knight]
> #or use constant from Knight class
>
> def upgrade!(to)
> raise "Unable to upgrade" unless @can_upgrade_to.contain(to)
> end
> end
>
> class Knight < Solder
> end
>
> I cant use Knight constant (class name) before Solder and can't use
> Solder before Knight.
>
> I am just come from Java lang and for Java circular class deps is not
> a problem.
> But ruby is a dynamic language and it creates classes just on script
> executing. (right?)
>
> So my questions is: how to solve this problem? What is the best way to
> deal with it.
>

- --
Derek Wyatt - C++ / Ruby / Unix Programmer
http://derekwyatt.org
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.4 (MingW32)

iD8DBQFDDxIfwwHFeC88e2IRAk4TAKDZYh8c9N4fA+yS8noijJinCuYmtACfUmys
4DjDoOkRivEjy604G24Hqcw=
=gTVJ
-----END PGP SIGNATURE-----

--
anatol

(David A. Black) #8

Hi --

···

On Fri, 26 Aug 2005, Anatol Pomozov wrote:

Thanks for your quick answers.

Somewhere (i did not remember) I saw following trick

def Soldier
@can_upgrade_to = [:Knight] #This list != list of all ancestors

def upgrade?(to)
    @can_upgrade_to.include?(to.to_s.to_sym)
end
end

but the solution provided by Derek seems more natural for me.

And it has the advantage of working :slight_smile: What you've got above (even
given that you mean class rather than def) does not work. (See my
earlier post.)

David

--
David A. Black
dblack@wobblini.net

(David A. Black) #9

Hi --

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I think you're simply using an instance variable where a method is
what you want. Take advantage of the runtime compile/binding that
you're currently having problems with. Do this instead:

class Solder
~ def can_upgrade_to
~ [Knight]
~ end
end

And your problems go away. You don't have to open it up later. If
you want it private, you can simply make it private.

Doesn't solve the OP'S problem because it was about dependencies:
Knight has to be defined when it is used.

As long as it's defined before the method is called it should be OK:

   class C
     def can_upgrade_to
       [D]
     end
   end

   class D
   end

   p C.new.can_upgrade_to[0].new # #<D:0x401c0db8>

David

···

On Fri, 26 Aug 2005, Robert Klemme wrote:

2005/8/26, Derek Wyatt <derek@derekwyatt.org>:

--
David A. Black
dblack@wobblini.net

(Derek Wyatt) #10

class Solder
~ def can_upgrade_to
~ [Knight]
~ end
end

And your problems go away. You don't have to open it up later. If
you want it private, you can simply make it private.

Doesn't solve the OP'S problem because it was about dependencies:
Knight has to be defined when it is used.

David already mentioned this, but remember that when the method is
defined, it doesn't matter that "Knight" has no meaning as at this
point it's just method definition. On method _execution_ it had
better resolve to something or you're going to get an error. But in
the OP's case, this is a non-issue as the def and exec deliniation
is well-defined.

This is a ruby-ish (or loosley defined as a "scripting"-ish) type of
situation as in compiled languages you'd need to forward define the
relationship (which Java basically does behind the scenes) in the
C++ idiom of defining it as a class before using it like this:

class Knight;

class Soldier
{
~ Something that uses Knight
};

class Knight : public Soldier
{
};

We just get a shortcut with Ruby. Others mentioned the equivalent
of the above by giving the idea of re-opening the class later on,
which is perfectly acceptable and follows the traditional C++ type
of requirement for this situation. But if we can make it shorter
and easier to read, then why not? I really love this language. :slight_smile:

Regs,
D

(Robert K.) #11

Ooops, I really ovelooked that one. Time to go home I guess. Sorry
for the noise.

Kind regards

robert

···

2005/8/26, Derek Wyatt <derek@derekwyatt.org>:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

>>class Solder
>>~ def can_upgrade_to
>>~ [Knight]
>>~ end
>>end
>>
>>And your problems go away. You don't have to open it up later. If
>>you want it private, you can simply make it private.
>
>
> Doesn't solve the OP'S problem because it was about dependencies:
> Knight has to be defined when it is used.

David already mentioned this, but remember that when the method is
defined, it doesn't matter that "Knight" has no meaning as at this
point it's just method definition. On method _execution_ it had
better resolve to something or you're going to get an error. But in
the OP's case, this is a non-issue as the def and exec deliniation
is well-defined.