Cross access between instance variables

  • Thread starter Stefan Salewski
  • Start date
S

Stefan Salewski

I am working on a larger Ruby program, where instance variables of one
class need access to content of another class -- I wonder how I should
solve this problem best.

Assume we have multiple fish tanks, each with multiple fishes. The
fishes should tell us if they like the temperature of the water. We can
solve this task when we gave each fish a reference to its tank:

stefan@AMD64X2 ~/pet $ cat fish_tank.rb
class Fish
def initialize(name, favorite_temperature, tank)
@tank = tank
@n = name
@t = favorite_temperature
end
def tell_state
if @tank.temperature < @t
puts 'I feel cold'
elsif @tank.temperature = @t
puts 'I feel well'
else
puts 'I feel hot'
end
end
end

class Tank
attr_accessor :fish
attr_reader :temperature
def initialize(temperature)
@temperature = temperature
@fish = Array.new
end
end

t1 = Tank.new(18)
f1 = Fish.new('Ruby', 21, t1)
t1.fish << f1
t1.fish.each{|f| f.tell_state}

I am more familiar with a situation, where the tank is a module, so the
fish can access module variables. But now we really need many tanks,
each with very many fishes. Currently it seems to be a bit strange
giving each of thousands fishes a reference to its tank. Another
solution may be to give the .tell_state() function a tank parameter each
time when we call it. (A tank-temperature parameter is not a good idea,
because we may need more, light, food...)

(For my real application the tanks are windows, and fishes are graphical
elements in each window, see
http://www.ssalewski.de/PetEd-Demo.html.en)

Do I miss something?

Best regards,

Stefan Salewski
 
J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

I am working on a larger Ruby program, where instance variables of one
class need access to content of another class -- I wonder how I should
solve this problem best.

Assume we have multiple fish tanks, each with multiple fishes. The
fishes should tell us if they like the temperature of the water. We can
solve this task when we gave each fish a reference to its tank:

stefan@AMD64X2 ~/pet $ cat fish_tank.rb
class Fish
def initialize(name, favorite_temperature, tank)
@tank = tank
@n = name
@t = favorite_temperature
end
def tell_state
if @tank.temperature < @t
puts 'I feel cold'
elsif @tank.temperature = @t
puts 'I feel well'
else
puts 'I feel hot'
end
end
end

class Tank
attr_accessor :fish
attr_reader :temperature
def initialize(temperature)
@temperature = temperature
@fish = Array.new
end
end

t1 = Tank.new(18)
f1 = Fish.new('Ruby', 21, t1)
t1.fish << f1
t1.fish.each{|f| f.tell_state}

I am more familiar with a situation, where the tank is a module, so the
fish can access module variables. But now we really need many tanks,
each with very many fishes. Currently it seems to be a bit strange
giving each of thousands fishes a reference to its tank. Another
solution may be to give the .tell_state() function a tank parameter each
time when we call it. (A tank-temperature parameter is not a good idea,
because we may need more, light, food...)

(For my real application the tanks are windows, and fishes are graphical
elements in each window, see
http://www.ssalewski.de/PetEd-Demo.html.en)

Do I miss something?

Best regards,

Stefan Salewski
Great question! It's especially important when the fish / tank start
affecting each other, ie cold tank takes away 10 life units from the fish,
should the tank do this to the fish, or should the fish look at the tank and
do it to itself? If it should be the fish, then what if it only happens
during certain tank conditions such as proximity to other fish? So maybe it
should be the tank doing this task, but what if the fish is feeling friendly
that day, and can tolerate proximity to other fish? They are so dependent on
each other, who is responsible for what, and how should that relationship be
established?

I'm eager to see responses to this, I've run into the same issue in the
past, and was never satisfied with my solutions.
 
J

Johannes Held

But now we really need many tanks, each with very many fishes.
Currently it seems to be a bit strange giving each of thousands
fishes a reference to its tank.
You can control the fishes within the tank. Every fish is saved in an
array in tank. The tank queries every fish for it's mood …

http://pastie.org/1870880
Another solution may be to give the .tell_state() function a tank
parameter each time when we call it. (A tank-temperature parameter
is not a good idea, because we may need more, light, food...)
Why not? Pass them via a hash?
IMO there's no need for a fish to know its tank.
 
S

Stefan Salewski

You can control the fishes within the tank. Every fish is saved in an
array in tank. The tank queries every fish for it's mood …

http://pastie.org/1870880

Why not? Pass them via a hash?
IMO there's no need for a fish to know its tank.

Thank you for your example.
Of course this is a way to solve the problem. It may look not very
natural, that we have to tell the fish the parameters of its tank, when
we ask the fish about its mood:
mood = fish.tell_mood({:temp => t.temp, :food => t.food_type})

But OK, this is the way to do it. Now I know that I have not missed
something.

Thanks

Stefan Salewski
 
H

Hassan Schroeder

2011/5/7 Stefan Salewski said:
Of course this is a way to solve the problem. It may look not very
natural, that we have to tell the fish the parameters of its tank, when
we ask the fish about its mood:


But OK, this is the way to do it. Now I know that I have not missed
something.

A little late to this, but isn't this a good case for the Observer pattern?
So the fish are always "aware" of the tank environment? Very simply:

-----------------------------
require 'observer'

class Tank
include Observable

def temperature
@temperature
end

def temperature=3D(temperature)
@temperature =3D temperature
changed
notify_observers(temperature)
end

end

class Fish

attr_accessor :tank_temperature

def initialize(tank)
@tank_temperature =3D tank.temperature
tank.add_observer(self)
end

def update(temperature)
puts "temperature changed to #{temperature}"
@tank_temperature =3D temperature
end

end
-----------------------------
And exercise with:
-----------------------------
require 'tank'
require 'fish'

tank =3D Tank.new
tank.temperature=3D 100
fish =3D Fish.new(tank)
puts "fish.tank_temperature: #{fish.tank_temperature}" # =3D> 100

tank.temperature=3D 102
puts "fish.tank_temperature: #{fish.tank_temperature}" # =3D> 102
-----------------------------

FWIW,
--=20
Hassan Schroeder ------------------------ (e-mail address removed)
twitter: @hassan
 
J

John Feminella

You add fish to a tank, not tanks to a fish. An alternative proposal,
that doesn't involve Fish holding a reference to Tanks at all:

class Fish
# ...

def comfortable?(temp_in_degrees_celsius)
(@min..@max).include? temp_in_degrees_celsius
end
end

class Tank
# ...

def add_inhabitant(i)
self.inhabitants << i
end

def update
# ...
# possible temperature change!

self.inhabitants.each do |inhabitant|
logger.warn("#{inhabitant} is experiencing environmental
stress") unless inhabitant.comfortable?(t)
end
end
end
--
John Feminella
Principal Consultant, BitsBuilder
LI: http://www.linkedin.com/in/johnxf
SO: http://stackoverflow.com/users/75170/
 
H

Hassan Schroeder

You add fish to a tank, not tanks to a fish.

POV - you could as well say you "assign" a tank to a particular fish :)

And actually, if I were implementing this myself, I'd probably separate
the Tank from the Environment it provides and have the Fish observe
its environment.

As always, YMMV.
 
J

John Feminella

You add fish to a tank, not tanks to a fish.
POV - you could as well say you "assign" a tank to a particular fish :)

Perhaps, but in this model, at least, the fish can't exist without the
tank, while the same is not true of a tank without the fish. So that's
a strong motivation for considering putting fish in a tank, rather
than the other way around.
And actually, if I were implementing this myself, I'd probably separate
the Tank from the Environment it provides and have the Fish observe
its environment.

There's lots of ways to skin this cat (as there are with pretty much
any software problem). I was just picking what I thought was the
STTCPW.

~ jf
--
John Feminella
Principal Consultant, BitsBuilder
LI: http://www.linkedin.com/in/johnxf
SO: http://stackoverflow.com/users/75170/
 

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

Forum statistics

Threads
473,769
Messages
2,569,577
Members
45,054
Latest member
LucyCarper

Latest Threads

Top