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

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?
:slight_smile:

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

路路路

On Mon, 23 Jan 2006, Vladimir Agafonkin 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? :slight_smile:

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?

--
strong and healthy, who thinks of sickness until it strikes like lightning?
preoccupied with the world, who thinks of death, until it arrives like
thunder? -- milarepa

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?
:slight_smile:

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

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

路路路

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?
:slight_smile:

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?

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

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?
:slight_smile:

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?

This is a slightly different way, based on a soon-to-be-released version
of MinDI (which is a dependency injection framework and hence the
"inject" terminology):

module Injectable
  module Injected
    def method_missing(*args, &block)
      @__injectable__container__ || super
      @__injectable__container__.send(*args, &block)
    rescue NoInjectedMethodError
      super
    end
  end

  def inject_into obj
    obj.extend Injected
    obj.instance_variable_set(:@__injectable__container__, self)
    obj
  end

  def method_missing(m, *rest)
    raise NoInjectedMethodError
  end
end

class Duck
  include Injectable

  def quack; @quack_behavior.quack; end
  def waddle; @waddle_behavior.waddle; end

  def initialize(h)
    @quack_behavior = h[:quack_behavior]
    @waddle_behavior = h[:waddle_behavior]

    inject_into @quack_behavior
    inject_into @waddle_behavior
  end
end

class StandardQuacker
  def quack
    puts "QUACK!"
  end
end

class NoisyWaddler
  def waddle
    quack # note that this propagates to Duck then to quacker
    puts "<waddle>"
    quack
  end
end

duck = Duck.new(
  :quack_behavior => StandardQuacker.new,
  :waddle_behavior => NoisyWaddler.new
)

duck.waddle

__END__

Output:

QUACK!
<waddle>
QUACK!

路路路

--
      vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

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.

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! :slight_smile: 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.

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! :slight_smile: 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.