[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

[newbie] The Duck Problem, or accessing one instance var from another

Vladimir Agafonkin

1/24/2006 2:20:00 AM

Hi!

What is the best way to access class' instance variables from a method
of another instance variable of the same class that is a class itself?
:-)

OK, Let's say I have Duck class with an instance variable
@quack_behaviour of QuackBehaviour class inside. Duck#quack method
calls one of the QuackBehaviour methods, and I want to access some of
the intance variables (say, @name) of the caller Duck object from that
method.

One way is to set an attr_accessor :name (or use instance_variable_get)
and pass "self" as a parameter to the @quack_behaviour method. But it
seems for me that it is not the most appropriate way of doing this. Or
is it?

6 Answers

Ara.T.Howard

1/24/2006 3:21:00 AM

0

Robert Klemme

1/24/2006 8:15:00 AM

0

Vladimir Agafonkin wrote:
> Hi!
>
> What is the best way to access class' instance variables from a method
> of another instance variable of the same class that is a class itself?
> :-)
>
> OK, Let's say I have Duck class with an instance variable
> @quack_behaviour of QuackBehaviour class inside. Duck#quack method
> calls one of the QuackBehaviour methods, and I want to access some of
> the intance variables (say, @name) of the caller Duck object from that
> method.
>
> One way is to set an attr_accessor :name (or use
> instance_variable_get) and pass "self" as a parameter to the
> @quack_behaviour method. But it seems for me that it is not the most
> appropriate way of doing this. Or is it?

Sounds like an application of state or strategy pattern. I posted some
example code a few days / weeks ago. Basically, if your two classes are
rather tightly coupled you can have each instance point to the other.
That way both can access methods on the other instance. The only ugly
thing being that you have to manually keep references in sync.

HTH

robert

Austin Ziegler

1/24/2006 5:16:00 PM

0

On 24/01/06, Vladimir Agafonkin <agafonkin@gmail.com> wrote:
> What is the best way to access class' instance variables from a method
> of another instance variable of the same class that is a class itself?
> :-)
>
> OK, Let's say I have Duck class with an instance variable
> @quack_behaviour of QuackBehaviour class inside. Duck#quack method
> calls one of the QuackBehaviour methods, and I want to access some of
> the intance variables (say, @name) of the caller Duck object from that
> method.
>
> One way is to set an attr_accessor :name (or use
> instance_variable_get) and pass "self" as a parameter to the
> @quack_behaviour method. But it seems for me that it is not the most
> appropriate way of doing this. Or is it?

Huh?

class QuackBehaviour
def loudly
"LOUDLY"
end
end

class Duck
def initialize
@quack_behaviour = QuackBehaviour.new
@name = "Daffy"
end

def quack
@quack_behaviour.loudly
end
end

There's nothing in the above that even remotely allows for
QuackBehaviour#loudly to reach inside of the Duck instance. You will, as
you said, need to either register the duck instance or pass +self+ as a
parameter.

-austin
--
Austin Ziegler * halostatue@gmail.com
* Alternate: austin@halostatue.ca


Vladimir Agafonkin

1/24/2006 5:39:00 PM

0

Thanks! I will keep that in mind. Though I think that it complicates
things a lot in exchange for using just "attr" instead of, say,
"@obj.attr" for accessign an attribute.

Vladimir Agafonkin

1/24/2006 6:06:00 PM

0

Thanks, Robert. You're right, a strategy pattern (I just started
reading "Head First Design Patterns" of O'Reilly). What do you mean by
manually keep references synchronized?

I implented my solution with cross-references, and it seems quite
automated (though there's still room for improvement). Here's what I
came with (in short):

#================================================
class Duck
attr_reader :name

def initialize(name, fly_behaviour=:fly_with_wings,
quack_behaviour=:quack)
@fly_behaviour = FlyBehaviour.new(self, fly_behaviour)
@quack_behaviour = QuackBehaviour.new(self, quack_behaviour)
@name = name
end

def fly
@fly_behaviour.perform
end

def quack
@quack_behaviour.perform
end
end

class Behaviour
def initialize(object, behaviour);
@obj = object;
@behaviour = behaviour;
end

def perform
send(@behaviour)
end

def self.list;
self.protected_instance_methods
end
end

class FlyBehaviour < Behaviour
protected
def fly_with_wings
puts "#{@obj.name} is flying!"
end

def do_not_fly
puts "#{@obj.name} can't fly. :-("
end
end

class QuackBehaviour < Behaviour
protected
def quack
puts "#{@obj.name} is quacking! Quack!!"
end

def squeak
puts "#{@obj.name} is quacking! Squeak!!!"
end
end

class RubberDuck < Duck
def initialize(name)
super(name,:do_not_fly,:squeak)
end
end

ducks = []
ducks << Duck.new("George")
ducks << RubberDuck.new("Wacky")

ducks.each do |duck|
duck.quack
duck.fly
end

p QuackBehaviour.list
#================================================

The next big thing would be to implement generating behaviour-related
methods in the Duck class on the fly. Through method_missing, maybe?
And, of course, behaviour instance variables too. That seems a bit
harder to do, but I'm a programming newbie after all! :-) The perfect
use of the solution would look like:

usually_able_to :fly, :quack, :swim

that looks similiar to Rails has_many and belongs_too. Maybe I need to
check it's code for an idea.

And there's another question: how to make all methods defined in
Behaviour children protected? The code would be more attractive if I
hadn't to write "protected" in the beginning of each behaviour child.

Vladimir Agafonkin

1/24/2006 6:06:00 PM

0

Thanks, Robert. You're right, a strategy pattern (I just started
reading "Head First Design Patterns" of O'Reilly). What do you mean by
manually keep references synchronized?

I implented my solution with cross-references, and it seems quite
automated (though there's still room for improvement). Here's what I
came with (in short):

#================================================
class Duck
attr_reader :name

def initialize(name, fly_behaviour=:fly_with_wings,
quack_behaviour=:quack)
@fly_behaviour = FlyBehaviour.new(self, fly_behaviour)
@quack_behaviour = QuackBehaviour.new(self, quack_behaviour)
@name = name
end

def fly
@fly_behaviour.perform
end

def quack
@quack_behaviour.perform
end
end

class Behaviour
def initialize(object, behaviour);
@obj = object;
@behaviour = behaviour;
end

def perform
send(@behaviour)
end

def self.list;
self.protected_instance_methods
end
end

class FlyBehaviour < Behaviour
protected
def fly_with_wings
puts "#{@obj.name} is flying!"
end

def do_not_fly
puts "#{@obj.name} can't fly. :-("
end
end

class QuackBehaviour < Behaviour
protected
def quack
puts "#{@obj.name} is quacking! Quack!!"
end

def squeak
puts "#{@obj.name} is quacking! Squeak!!!"
end
end

class RubberDuck < Duck
def initialize(name)
super(name,:do_not_fly,:squeak)
end
end

ducks = []
ducks << Duck.new("George")
ducks << RubberDuck.new("Wacky")

ducks.each do |duck|
duck.quack
duck.fly
end

p QuackBehaviour.list
#================================================

The next big thing would be to implement generating behaviour-related
methods in the Duck class on the fly. Through method_missing, maybe?
And, of course, behaviour instance variables too. That seems a bit
harder to do, but I'm a programming newbie after all! :-) The perfect
use of the solution would look like:

usually_able_to :fly, :quack, :swim

that looks similiar to Rails has_many and belongs_too. Maybe I need to
check it's code for an idea.

And there's another question: how to make all methods defined in
Behaviour children protected? The code would be more attractive if I
hadn't to write "protected" in the beginning of each behaviour child.