classes within classes

J

jan aerts (RI)

Hi all,

I'm trying to create a little library for drawing my data using SVG. One
of the inherent properties of the drawings that I have to make, is that
each picture can contain one or more tracks with each track containing
one or more features (see ASCII art below).

+----------------------------picture-+
| +----------------------track1----+ |
| | | |
| | x x | |
| | x x | |
| | | |
| | | |
| +--------------------------------+ |
| |
| +----------------------track2----+ |
| | | |
| | xx | |
| +--------------------------------+ |
+------------------------------------+

As a track can _only_ be defined within a picture, and a feature can
_only_ be defined within a track, I have set up the classes as follows:
class Picture
class Track
class Feature
end
end
end

The problem is that some of the properties of a Picture object have to
be readable for its Track objects. This is _not_ simple inheritance,
because Picture and Picture::Track are two completely different things.
As I see it, there are several options:
(A) either pass those properties as arguments every time you create a
new Picture::Track object. Not optimal, because the same piece of data
is copied over and over again.
(B) set those features as global variables. Not optimal as well.
(C) something more elegant?

More elaborately (showing some of the key features)
<LIB CODE SNIPPET>
class Picture
def initialize(width)
@width =3D width
@tracks =3D Array.new
end
attr_accessor :width, :tracks

def add_track(name)
@tracks.push(Picture::Track.new(name)
return @tracks[-1]
end

class Track
# DO NOT CALL INITIALIZE METHOD DIRECTLY: use Picture#add_track
def initialize(name)
@name =3D name
end
attr_accessor :name

def to_svg
# I NEED THE WIDTH OF THE PICTURE HERE
return
some_xml_that_includes_the_width_which_was_defined_in_Picture
end

class Feature
...
end
end
end
</LIB CODE SNIPPET>

<USAGE SNIPPET>
p =3D Picture.new(800) # Creates a new picture of 800pt width.
p.add_track('first_track')
p.add_track('second_track')
</USAGE SNIPPET>

In the end, the question is: should I make Picture#width a global
variable, should I add width as an argument to
Picture::Track#initialize, or is there a more elegant solution? (I hope
it's the last one...)

Any help would be very much appreciated,
jan.

Dr Jan Aerts
Bioinformatics Group
Roslin Institute
Roslin, Scotland, UK
+44 131 527 4200

---------The obligatory disclaimer--------
The information contained in this e-mail (including any attachments) is
confidential and is intended for the use of the addressee only. The
opinions expressed within this e-mail (including any attachments) are
the opinions of the sender and do not necessarily constitute those of
Roslin Institute (Edinburgh) ("the Institute") unless specifically
stated by a sender who is duly authorised to do so on behalf of the
Institute.=20
 
J

Jan Svitok

Hi all,

I'm trying to create a little library for drawing my data using SVG. One
of the inherent properties of the drawings that I have to make, is that
each picture can contain one or more tracks with each track containing
one or more features (see ASCII art below).

+----------------------------picture-+
| +----------------------track1----+ |
| | | |
| | x x | |
| | x x | |
| | | |
| | | |
| +--------------------------------+ |
| |
| +----------------------track2----+ |
| | | |
| | xx | |
| +--------------------------------+ |
+------------------------------------+

As a track can _only_ be defined within a picture, and a feature can
_only_ be defined within a track, I have set up the classes as follows:
class Picture
class Track
class Feature
end
end
end

The problem is that some of the properties of a Picture object have to
be readable for its Track objects. This is _not_ simple inheritance,
because Picture and Picture::Track are two completely different things.
As I see it, there are several options:
(A) either pass those properties as arguments every time you create a
new Picture::Track object. Not optimal, because the same piece of data
is copied over and over again.
(B) set those features as global variables. Not optimal as well.
(C) something more elegant?

More elaborately (showing some of the key features)
<LIB CODE SNIPPET>
class Picture
def initialize(width)
@width = width
@tracks = Array.new
end
attr_accessor :width, :tracks

def add_track(name)
@tracks.push(Picture::Track.new(name)
return @tracks[-1]
end

class Track
# DO NOT CALL INITIALIZE METHOD DIRECTLY: use Picture#add_track
def initialize(name)
@name = name
end
attr_accessor :name

def to_svg
# I NEED THE WIDTH OF THE PICTURE HERE
return
some_xml_that_includes_the_width_which_was_defined_in_Picture
end

class Feature
...
end
end
end
</LIB CODE SNIPPET>

<USAGE SNIPPET>
p = Picture.new(800) # Creates a new picture of 800pt width.
p.add_track('first_track')
p.add_track('second_track')
</USAGE SNIPPET>

In the end, the question is: should I make Picture#width a global
variable, should I add width as an argument to
Picture::Track#initialize, or is there a more elegant solution? (I hope
it's the last one...)

Any help would be very much appreciated,
jan.

Hi,

two ideas:
1. pass the picture (it)self to the Track constructor and then query
whatever you need
2. have all the svg generating stuff in another class that will be
given the Picture object, and will iterate through it, creating the
xml along the way (ERB template comes to my mind...)

Jan
 
A

ara.t.howard

Hi all,

I'm trying to create a little library for drawing my data using SVG. One
of the inherent properties of the drawings that I have to make, is that
each picture can contain one or more tracks with each track containing
one or more features (see ASCII art below).

+----------------------------picture-+
| +----------------------track1----+ |
| | | |
| | x x | |
| | x x | |
| | | |
| | | |
| +--------------------------------+ |
| |
| +----------------------track2----+ |
| | | |
| | xx | |
| +--------------------------------+ |
+------------------------------------+

As a track can _only_ be defined within a picture, and a feature can
_only_ be defined within a track, I have set up the classes as follows:
class Picture
class Track
class Feature
end
end
end

The problem is that some of the properties of a Picture object have to
be readable for its Track objects. This is _not_ simple inheritance,
because Picture and Picture::Track are two completely different things.
As I see it, there are several options:
(A) either pass those properties as arguments every time you create a
new Picture::Track object. Not optimal, because the same piece of data
is copied over and over again.

this is not true - ruby __always__ passes references (unless you are
seializing an object over a wire or to file) so there's no harm in having a
reference to a 'parent' object. in fact, given your spec, with all the
'_only_' clauses, it makes perfect sense to design the nested classes as a
factory chain, eg

class Picture
def new_track *a, &b
Track.new self, *a, &b
end

class Track
def initialize picture, *a, &b
@picture = picture
end

def new_feature *a, &b
Feature.new self, *a, &b
end

class Feature
def initialize track, *a, &b
@track = track
end
end
end
end


so

picture = Picture.new

track = picture.new_track :foo, :bar

feature = track.new_feature :bar, :foo

this also has the nice feature that children prevent any required parents from
having the gc evaporate them - so long as we have a reference up the object
will not disappear. in fact, i'd probably setup the association as a two-way:


class Picture
Tracks = []

def new_track *a, &b
t = Track.new self, *a, &b
ensure
Tracks << t
end

def each_track &b
Tracks.each &b
end

class Track
def initialize picture, *a, &b
@picture = picture
end

Features = []

def new_feature *a, &b
f = Feature.new self, *a, &b
ensure
Features << f
end

def each_feature &b
Features.each &b
end

class Feature
def initialize track, *a, &b
@track = track
end
end
end
end


etc.

-a
 
J

jan aerts (RI)

-----Original Message-----
From: Jan Svitok [mailto:[email protected]]=20
Sent: 13 November 2006 15:03
To: ruby-talk ML
Subject: Re: classes within classes
=20
+----------------------------picture-+
| +----------------------track1----+ |
| | | |
| | x x | |
| | x x | |
| | | |
| | | |
| +--------------------------------+ |
| |
| +----------------------track2----+ |
| | | |
| | xx | |
| +--------------------------------+ | =20
+------------------------------------+

<SNIP>

More elaborately (showing some of the key features) <LIB=20 CODE SNIPPET> =20
class Picture
def initialize(width)
@width =3D width
@tracks =3D Array.new
end
attr_accessor :width, :tracks

def add_track(name)
@tracks.push(Picture::Track.new(name)
return @tracks[-1]
end

class Track
# DO NOT CALL INITIALIZE METHOD DIRECTLY: use Picture#add_track
def initialize(name)
@name =3D name
end
attr_accessor :name

def to_svg
# I NEED THE WIDTH OF THE PICTURE HERE
return
some_xml_that_includes_the_width_which_was_defined_in_Picture
end

class Feature
...
end
end
end
</LIB CODE SNIPPET>

<USAGE SNIPPET>
p =3D Picture.new(800) # Creates a new picture of 800pt width.
p.add_track('first_track')
p.add_track('second_track')
</USAGE SNIPPET>

In the end, the question is: should I make Picture#width a global=20
variable, should I add width as an argument to=20
Picture::Track#initialize, or is there a more elegant solution? (I=20
hope it's the last one...)

Any help would be very much appreciated, jan.
=20
Hi,
=20
two ideas:
1. pass the picture (it)self to the Track constructor and=20
then query whatever you need 2. have all the svg generating=20
stuff in another class that will be given the Picture object,=20
and will iterate through it, creating the xml along the way=20
(ERB template comes to my mind...)
=20
Jan
=20
=20
Thanks for your swift reply, Jan.

I did look briefly at these two options, but didn't go for them
previously. However, passing the Picture object itself _might_ actually
be the best solution. I suppose the code would then look something like:

class Picture
def add_track(self, name)
@tracks.push(Picture::Track.new(self, name))
end

class Track
def initialize(picture, name)
width =3D picture.width
name =3D name
end
end
end

I'll probably go for this one...

jan.
 
J

jan aerts (RI)

=20
-----Original Message-----
From: (e-mail address removed) [mailto:[email protected]]=20
Sent: 13 November 2006 15:38
To: ruby-talk ML
Subject: Re: classes within classes
=20
+----------------------------picture-+
| +----------------------track1----+ |
| | | |
| | x x | |
| | x x | |
| | | |
| | | |
| +--------------------------------+ |
| |
| +----------------------track2----+ |
| | | |
| | xx | |
| +--------------------------------+ |
+------------------------------------+

As a track can _only_ be defined within a picture, and a=20 feature can=20
_only_ be defined within a track, I have set up the classes=20 as follows:
class Picture
class Track
class Feature
end
end
end
this also has the nice feature that children prevent any=20
required parents from having the gc evaporate them - so long=20
as we have a reference up the object will not disappear. in=20
fact, i'd probably setup the association as a two-way:
=20
=20
class Picture
Tracks =3D []
=20
def new_track *a, &b
t =3D Track.new self, *a, &b
ensure
Tracks << t
end
=20
def each_track &b
Tracks.each &b
end
=20
class Track
def initialize picture, *a, &b
@picture =3D picture
end
=20
Features =3D []
=20
def new_feature *a, &b
f =3D Feature.new self, *a, &b
ensure
Features << f
end
=20
def each_feature &b
Features.each &b
end
=20
class Feature
def initialize track, *a, &b
@track =3D track
end
end
end
end
=20
=20
etc.

Thanks. You really made that easy for me...
 
R

Robert Klemme

Hi all,

I'm trying to create a little library for drawing my data using SVG. One
of the inherent properties of the drawings that I have to make, is that
each picture can contain one or more tracks with each track containing
one or more features (see ASCII art below).

+----------------------------picture-+
| +----------------------track1----+ |
| | | |
| | x x | |
| | x x | |
| | | |
| | | |
| +--------------------------------+ |
| |
| +----------------------track2----+ |
| | | |
| | xx | |
| +--------------------------------+ |
+------------------------------------+

As a track can _only_ be defined within a picture, and a feature can
_only_ be defined within a track, I have set up the classes as follows:
class Picture
class Track
class Feature
end
end
end

The problem is that some of the properties of a Picture object have to
be readable for its Track objects. This is _not_ simple inheritance,
because Picture and Picture::Track are two completely different things.
As I see it, there are several options:
(A) either pass those properties as arguments every time you create a
new Picture::Track object. Not optimal, because the same piece of data
is copied over and over again.
(B) set those features as global variables. Not optimal as well.
(C) something more elegant?

Why don't you just add a member "container" or "parent" to the Feature
(and maybe also Track) so a Track knows all of its Features and the
Features all know the Track that they are part of? That's a common
idiom for tree storage if you need to navigate both directions (from
root to leaf and vice versa).

Another note: while I can see why you nest classes that might become a
bit unreadable. Also it does not prevent any outside code from
accessing class Track and Feature - basically just the names get more
complex through the nesting. An alternative approach is to just define
a module as namespace and nest all classes in it.

module PictureApp
class Picture
end

class Track
end

class Feature
end
end

Kind regards

robert
 
D

David Vallner

--------------enig2012FD265F47C7CAABF8305E
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable

Paul said:
why can't you just use inheritage and mixins? would that generate a wor= se
design then what you currently have?
=20

http://www.parashift.com/c++-faq-lite/multiple-inheritance.html#faq-25.4
- notably the comments in Rule #1

While a C++ text, the idea is relevant - C++ multiple inheritance was
used to implement mixins (Rule #2 in that article encourages that), and
the same rules should apply to Ruby as well.

In fact, most of the uses of normal inheritance I see in Ruby code is to
save you (a little) typing over composition, instead of overriding /
implementing the parent's methods. The latter is usually unnecessary
because of dynamic typing and blocks.

David Vallner


--------------enig2012FD265F47C7CAABF8305E
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: OpenPGP digital signature
Content-Disposition: attachment; filename="signature.asc"

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (MingW32)

iD8DBQFFWOyOy6MhrS8astoRAqodAJ4xQjLceZN710CTbVo1owiyjmdlNwCcDJno
wUKflsKA6GmJcbIQvOxXLVE=
=lHyi
-----END PGP SIGNATURE-----

--------------enig2012FD265F47C7CAABF8305E--
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top