Extending Objects Without Inheritance

Hi,

I'm no great OO expert (or Ruby expert) so maybe the answer to this is
obvious or maybe it's impossible.

I currently have a bunch of classes which I use to parse a data file.
Essentially there are fve layers of containers:

PDB -> Model -> Chain -> Residue -> Atom

A PDB object contains one or more Model objects each of which contain
one or more Chain objects each of which contain... etc down to Atoms.
Note that Model doesn't inherit from PDB or Chain from Model - each
class is just a container for the next one down.

The problem I have is that sometimes I have extra data associated with
the Atom and/or Residue objects. This extra data always comes later
(from a different data file) and usually won't be present. when I need
to, I'd like to add this data to the (already created data structure)
with the minimum amount of work/fuss.

As far as I can see I have the following options:

1. Modify the Atom and Residue classes to include the extra data, set
to nil originally and then filled in when needed. I'd rather not do
this because the code is part of a bigger project which I don't have
direct control over, and it seems rather inelegant to have these extra
fields which hardly ever get used.

2. Create a new AtomExtra class which inherits from Atom but adds
instance variables and accessors for the new data. Then when I read
the extra data I transform each Atom object into an AtomExtra object
and add the new data. I guess the crux of my question is what's the
neatest, Rubyist way of doing this: Is there a smart way of 'morphing'
an object into another type of object (Atom to AtomExtra in this case)
or do I have to create a new object, copy all the data across and then
reattach the new object into the container structure.

Thanks in advance for any advice or help you can give!

Alex Gutteridge wrote:

The problem I have is that sometimes I have extra data associated with
the Atom and/or Residue objects. This extra data always comes later
(from a different data file) and usually won't be present. when I need
to, I'd like to add this data to the (already created data structure)
with the minimum amount of work/fuss.

As far as I can see I have the following options:

1. Modify the Atom and Residue classes to include the extra data, set
to nil originally and then filled in when needed. I'd rather not do
this because the code is part of a bigger project which I don't have
direct control over, and it seems rather inelegant to have these extra
fields which hardly ever get used.

2. Create a new AtomExtra class which inherits from Atom but adds
instance variables and accessors for the new data. Then when I read
the extra data I transform each Atom object into an AtomExtra object
and add the new data. I guess the crux of my question is what's the
neatest, Rubyist way of doing this: Is there a smart way of 'morphing'
an object into another type of object (Atom to AtomExtra in this case)
or do I have to create a new object, copy all the data across and then
reattach the new object into the container structure.

Thanks in advance for any advice or help you can give!

# 3. Create a ExtraAtomFunctionality module that you
# extend your Atom objects with dynamically.

class Atom
   #...
end

module ExtraAtomFunctionality
   attr_accessor :my_extra_stuff
end

a = Atom.new
# ...then later when you find you need to augment the object...
a.extend ExtraAtomFunctionality
a.my_extra_stuff = "Rogue electron"

# It may eat more memory than your other suggestions,
# since it would make singleton classes for each
# augmented Atom instance.

···

--
(\[ Kent Dahl ]/)_ _~_ _____[ Kent Dahl - Kent Dahl ]_____/~
  ))\_student_/(( \__d L b__/ Master of Science in Technology )
( \__\_õ|õ_/__/ ) _) Industrial economics and technology management (
  \____/_ö_\____/ (____engineering.discipline_=_Computer::Technology___)

Alex Gutteridge wrote:

Hi,

I'm no great OO expert (or Ruby expert) so maybe the answer to this is
obvious or maybe it's impossible.

I currently have a bunch of classes which I use to parse a data file.
Essentially there are fve layers of containers:

PDB -> Model -> Chain -> Residue -> Atom

A PDB object contains one or more Model objects each of which contain
one or more Chain objects each of which contain... etc down to Atoms.
Note that Model doesn't inherit from PDB or Chain from Model - each
class is just a container for the next one down.

The problem I have is that sometimes I have extra data associated with
the Atom and/or Residue objects. This extra data always comes later
(from a different data file) and usually won't be present. when I need
to, I'd like to add this data to the (already created data structure)
with the minimum amount of work/fuss.

As far as I can see I have the following options:

1. Modify the Atom and Residue classes to include the extra data, set
to nil originally and then filled in when needed. I'd rather not do
this because the code is part of a bigger project which I don't have
direct control over, and it seems rather inelegant to have these extra
fields which hardly ever get used.

Probably the caffeine hasn't hit me yet, but I'll suggest this anyway: why not define the extra accessors you might need and assign to them only when needed? No extra memory is used until you assign, as the following shows:

class Atom
   attr_accessor :x, :y, :extra1, :extra2
   def initialize x, y
     @x, @y = x, y
   end
end

atom = Atom.new(1,2)

p atom
p atom.instance_variables

atom.extra1 = "extra stuff"

p atom
p atom.instance_variables

__END__

Output:

#<Atom:0x401c68e0 @y=2, @x=1>
["@y", "@x"]
#<Atom:0x401c68e0 @y=2, @x=1, @extra1="extra stuff">
["@y", "@x", "@extra1"]

"Kent Dahl" <kentda+news@stud.ntnu.no> schrieb im Newsbeitrag
news:c9n0f1$i61$1@orkan.itea.ntnu.no...

Alex Gutteridge wrote:
> The problem I have is that sometimes I have extra data associated with
> the Atom and/or Residue objects. This extra data always comes later
> (from a different data file) and usually won't be present. when I need
> to, I'd like to add this data to the (already created data structure)
> with the minimum amount of work/fuss.
>
> As far as I can see I have the following options:
>
> 1. Modify the Atom and Residue classes to include the extra data, set
> to nil originally and then filled in when needed. I'd rather not do
> this because the code is part of a bigger project which I don't have
> direct control over, and it seems rather inelegant to have these extra
> fields which hardly ever get used.
>
> 2. Create a new AtomExtra class which inherits from Atom but adds
> instance variables and accessors for the new data. Then when I read
> the extra data I transform each Atom object into an AtomExtra object
> and add the new data. I guess the crux of my question is what's the
> neatest, Rubyist way of doing this: Is there a smart way of 'morphing'
> an object into another type of object (Atom to AtomExtra in this case)
> or do I have to create a new object, copy all the data across and then
> reattach the new object into the container structure.
>
> Thanks in advance for any advice or help you can give!

# 3. Create a ExtraAtomFunctionality module that you
# extend your Atom objects with dynamically.

class Atom
   #...
end

module ExtraAtomFunctionality
   attr_accessor :my_extra_stuff
end

a = Atom.new
# ...then later when you find you need to augment the object...
a.extend ExtraAtomFunctionality
a.my_extra_stuff = "Rogue electron"

# It may eat more memory than your other suggestions,
# since it would make singleton classes for each
# augmented Atom instance.

Very nice!

And then there's also

4: Make Atom inherit OpenStruct

That way you can query and add arbitrary attributes at any time.

5: Add a hash member to atom which receives all additional data. Or, a
variant, put hash - like functionality into Atom:

class Atom
  def initialize
    @values = {}
  end

  def (key)
    @values[key]
  end

  def =(key, val)
    @values[key]= val
  end
end

Then you can do
atom = Atom.new
atom["foo"] = "bar"
atom[:length] = 4

6: store additional data outside of Atom

Keep a hash for the duration of your processing and store additional data
there with Atoms as keys.

But I'd favor #3, cause it's cleaner and you have more control over which
attributes are there. (This helps in catching typos also.) You can even
have several modules that extend Atom, depending on the usage context.

#4 is really only interesting if you never know which attributes you will
add and there are potentially many.

Kind regards

    robert

Kent Dahl <kentda+news@stud.ntnu.no> wrote in message news:<c9n0f1$i61$1@orkan.itea.ntnu.no>...

Alex Gutteridge wrote:
> The problem I have is that sometimes I have extra data associated with
> the Atom and/or Residue objects. This extra data always comes later
> (from a different data file) and usually won't be present. when I need
> to, I'd like to add this data to the (already created data structure)
> with the minimum amount of work/fuss.
>
> As far as I can see I have the following options:
>
> 1. Modify the Atom and Residue classes to include the extra data, set
> to nil originally and then filled in when needed. I'd rather not do
> this because the code is part of a bigger project which I don't have
> direct control over, and it seems rather inelegant to have these extra
> fields which hardly ever get used.
>
> 2. Create a new AtomExtra class which inherits from Atom but adds
> instance variables and accessors for the new data. Then when I read
> the extra data I transform each Atom object into an AtomExtra object
> and add the new data. I guess the crux of my question is what's the
> neatest, Rubyist way of doing this: Is there a smart way of 'morphing'
> an object into another type of object (Atom to AtomExtra in this case)
> or do I have to create a new object, copy all the data across and then
> reattach the new object into the container structure.
>
> Thanks in advance for any advice or help you can give!

# 3. Create a ExtraAtomFunctionality module that you
# extend your Atom objects with dynamically.

class Atom
   #...
end

module ExtraAtomFunctionality
   attr_accessor :my_extra_stuff
end

a = Atom.new
# ...then later when you find you need to augment the object...
a.extend ExtraAtomFunctionality
a.my_extra_stuff = "Rogue electron"

# It may eat more memory than your other suggestions,
# since it would make singleton classes for each
# augmented Atom instance.

Yes, thanks for your reply, I actually stumbled on this solution
myself just as you posted. It does exactly what I need, but your last
comment worries me slightly. My structures typically contain 1000's of
Atoms so I wonder how much memory this dynamic augmentation magick
will gobble up. Hopefully, in these days of gigabytes of RAM it won't
be a problem, but we'll see...

Alex Gutteridge

Joel VanderWerf <vjoel@PATH.Berkeley.EDU> wrote in message news:<40BF7269.3050807@path.berkeley.edu>...

Alex Gutteridge wrote:
> Hi,
>
> I'm no great OO expert (or Ruby expert) so maybe the answer to this is
> obvious or maybe it's impossible.
>
> I currently have a bunch of classes which I use to parse a data file.
> Essentially there are fve layers of containers:
>
> PDB -> Model -> Chain -> Residue -> Atom
>
> A PDB object contains one or more Model objects each of which contain
> one or more Chain objects each of which contain... etc down to Atoms.
> Note that Model doesn't inherit from PDB or Chain from Model - each
> class is just a container for the next one down.
>
> The problem I have is that sometimes I have extra data associated with
> the Atom and/or Residue objects. This extra data always comes later
> (from a different data file) and usually won't be present. when I need
> to, I'd like to add this data to the (already created data structure)
> with the minimum amount of work/fuss.
>
> As far as I can see I have the following options:
>
> 1. Modify the Atom and Residue classes to include the extra data, set
> to nil originally and then filled in when needed. I'd rather not do
> this because the code is part of a bigger project which I don't have
> direct control over, and it seems rather inelegant to have these extra
> fields which hardly ever get used.

Probably the caffeine hasn't hit me yet, but I'll suggest this anyway:
why not define the extra accessors you might need and assign to them
only when needed? No extra memory is used until you assign, as the
following shows:

class Atom
   attr_accessor :x, :y, :extra1, :extra2
   def initialize x, y
     @x, @y = x, y
   end
end

atom = Atom.new(1,2)

p atom
p atom.instance_variables

atom.extra1 = "extra stuff"

p atom
p atom.instance_variables

__END__

Output:

#<Atom:0x401c68e0 @y=2, @x=1>
["@y", "@x"]
#<Atom:0x401c68e0 @y=2, @x=1, @extra1="extra stuff">
["@y", "@x", "@extra1"]

Yes, this works as well, I obviously hadn't realised quite how dynamic
Ruby's OO system is. My code essentially looks like this now:

class Atom
    attr_accessor :id
    def initialize id
        @id = id
    end
end

atom = Atom.new('foo')
puts atom.id

class Atom
    attr_accessor :extra
end

atom.extra = 'bar'
puts atom.extra

__END__

Output:
foo
bar

Though the second class definition for Atom is in a different file. If
I understand correctly, when this second definition is pulled in,
every Atom object is magically extended to include accessors for the
@extra variable. This allows me to extend Atom objects without
fiddling with the original Atom definition (which is part of a larger
project and can't be easily editted). This almost seems too simple to
be true!

Alex Gutteridge wrote:

Though the second class definition for Atom is in a different file. If
I understand correctly, when this second definition is pulled in,
every Atom object is magically extended to include accessors for the
@extra variable. This allows me to extend Atom objects without
fiddling with the original Atom definition (which is part of a larger
project and can't be easily editted). This almost seems too simple to
be true!

Yep, you can reopen classes as often as you like, and add methods to existing objects.