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

V

Vladimir Agafonkin

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?
 
A

ara.t.howard

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?


harp:~ > cat a.rb
class Duck
attr_accessor "quack_behaviour"
def initialize(quack_behaviour) self.quack_behaviour = quack_behaviour::new(self) end
def quack() quack_behaviour.quack end
def name() "duck" end
end
class QuackBehaviour
def self::duck_attr a
module_eval <<-code
def #{ a }() duck.#{ a } end
def #{ a }=(val) duck.#{ a }=val end
code
end
attr_accessor "duck"
duck_attr "name"
def initialize(duck) self.duck = duck end
def quack() p name end
end

duck = Duck::new(QuackBehaviour)
duck.quack

harp:~ > ruby a.rb
"duck"

is one way. if you need to dynamically change the quack_behaviour object i'd
make a method that pushed/popped/ensured a QuackBehaviour object was in effect
- but having the QuackBehaviour object have a reference to their parent, if
possible, simplifies things.

regards.

-a
 
R

Robert Klemme

Vladimir said:
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
 
A

Austin Ziegler

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
=09 "LOUDLY"
=09end
end

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

def quack
=09 @quack_behaviour.loudly
=09end
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
 
V

Vladimir Agafonkin

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.
 
V

Vladimir Agafonkin

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.
 
V

Vladimir Agafonkin

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.
 

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,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top