Object access heirarchies

I often have this problem in my projects.

I have a bunch of classes to do various things in my program, but
once instantiated they each need access to each other. For example,
in the adventure game I am working on, I have a Parser object that
needs to manipulate a Player, Map and Things in the map.

I normally do such things by passing objects around when initializing,
like so:

parser = Parser(Player.new(World.new(Map.new, Things.new)))

That works, although it seems a little long winded - for the
parser to modify a thing, it needs to set something like:
player.world.thing["ball"].name = "bat"

Later, the Things needed access to the Map so I could set thing.location
to a map.room object. So I changed thing.location to be a string --
thing.location_name and made a function that could look up a location
based on it's name: map["garden"].

What I want to know is: what's the best way? I had globals $player, $world, $map
earlier and that makes things a bit easier but it also makes me nervous
(perhaps just because I have been anti-global-indoctrinated for 20 years now)
I'm still a bit vague on Modules. Any ideas?

Les

Leslie Viljoen wrote:

I often have this problem in my projects.

I have a bunch of classes to do various things in my program, but
once instantiated they each need access to each other. For example,
in the adventure game I am working on, I have a Parser object that
needs to manipulate a Player, Map and Things in the map.

I normally do such things by passing objects around when initializing,
like so:

parser = Parser(Player.new(World.new(Map.new, Things.new)))

That works, although it seems a little long winded - for the
parser to modify a thing, it needs to set something like:
player.world.thing["ball"].name = "bat"

Later, the Things needed access to the Map so I could set thing.location
to a map.room object. So I changed thing.location to be a string --
thing.location_name and made a function that could look up a location
based on it's name: map["garden"].

What I want to know is: what's the best way? I had globals $player, $world, $map
earlier and that makes things a bit easier but it also makes me nervous
(perhaps just because I have been anti-global-indoctrinated for 20 years now)
I'm still a bit vague on Modules. Any ideas?

Les

Ah, one thread up. Perhaps my question has already been answered in the last day.

Leslie Viljoen wrote:

I often have this problem in my projects.

I have a bunch of classes to do various things in my program, but
once instantiated they each need access to each other. For example,
in the adventure game I am working on, I have a Parser object that
needs to manipulate a Player, Map and Things in the map.

I normally do such things by passing objects around when initializing,
like so:

parser = Parser(Player.new(World.new(Map.new, Things.new)))

That works, although it seems a little long winded - for the
parser to modify a thing, it needs to set something like:
player.world.thing["ball"].name = "bat"

Later, the Things needed access to the Map so I could set thing.location
to a map.room object. So I changed thing.location to be a string --
thing.location_name and made a function that could look up a location
based on it's name: map["garden"].

What I want to know is: what's the best way? I had globals $player, $world, $map
earlier and that makes things a bit easier but it also makes me nervous
(perhaps just because I have been anti-global-indoctrinated for 20 years now)
I'm still a bit vague on Modules. Any ideas?

Les

What about a single base class?

   class Game
     attr_reader :parser, :world, :map, :things, :player

     def initialize
       @parser = Parser.new(self)
       @world = World.new(self)
       @map = Map.new(self)
       @things = Things.new(self)
       @player = Player.new(self)
     end

     class World
       def initialize(game)
         @game = game
       end

       def map
         @game.map
       end

       # ....
     end

     # ....
   end

Cheers,
Daniel

Leslie Viljoen wrote:

I often have this problem in my projects.

I have a bunch of classes to do various things in my program, but
once instantiated they each need access to each other. For example,
in the adventure game I am working on, I have a Parser object that
needs to manipulate a Player, Map and Things in the map.

I normally do such things by passing objects around when initializing,
like so:

parser = Parser(Player.new(World.new(Map.new, Things.new)))

That works, although it seems a little long winded - for the
parser to modify a thing, it needs to set something like:
player.world.thing["ball"].name = "bat"

Later, the Things needed access to the Map so I could set thing.location
to a map.room object. So I changed thing.location to be a string --
thing.location_name and made a function that could look up a location
based on it's name: map["garden"].

What I want to know is: what's the best way? I had globals $player,
$world, $map
earlier and that makes things a bit easier but it also makes me nervous
(perhaps just because I have been anti-global-indoctrinated for 20 years
now)
I'm still a bit vague on Modules. Any ideas?

Les

Modules are typically used not for organizing instances, but for
grouping methods and constants, so that chuncks of code can be reused in
different situations.

Why not simply replace the globals with a Game class and an instance
that has attributes for player, world, map, etc.?

If your program is complex enough, you may find a technique called
"dependency injection" useful. It's kind of like the Game class
suggestion, with a little extra syntactic sugar. DI is a way of
extracting the connection logic (which you are now doing by "passing
objets around when initializing") from the rest of the object creation
code. With DI, you define containers for your instances, and you can
decide whether a particular kind of instance should be unique in the
container, or there should be one instance for each key (such as a
location name).

There are several DI frameworks for ruby (see RAA). Here's an example
using my own MinDI library:

require 'mindi'

Parser = Struct.new :game # add parser state vars here
Player = Struct.new :game, :location, :contents
World = Struct.new :game # add state vars (:time, maybe)
Map = Struct.new :game
  # The :game attr is useful if you want to write methods of
  # these classes (Player for example) that refer to the other
  # game objects. But it's not strictly necessary.

Room = Struct.new :name, :contents

# Sometimes, a struct is not enough...
class Thing
  attr_accessor :name
  attr_reader :location

  def initialize name, location = nil
    @name = name
    @location = location
  end

  # Moving a Thing updates the Room it's in or Player who has it
  def location=(loc)
    @location.contents.delete self if @location
    @location = loc
    @location.contents << self if @location
  end
end

class GameContainer
  extend MinDI::Container

  # These are some singletons--one instance each.
  parser { Parser.new self }
  player { Player.new self, start_room, }
  world { World.new self }
  map { Map.new self }

  # A multiton.
  # The |name| means there can be many things--one for each name.
  # internally, there is a hash stored in @thing that maps
  # each name strign to a Thing.
  thing { |name| Thing.new name }

  # The shovel (unique with that name).
  shovel { thing "Shovel" }

  room { |name| Room.new name, }

  start_room { room "garden" }

  # Set up some initial conditions
  def initialize
    # create and locate the shovel
    shovel.location = start_room
  end
end

game = GameContainer.new

# The shovel is already defined:
p game.shovel
puts

# Create a new thing:
ball = game.thing("ball")
ball.location = game.room "basement"
p ball
puts

player = game.player

# pick up the shovel and anything else in the start room
# This could be made into a #pick_up method of Game or of Player
player.location.contents.each { |thing| thing.location = player }
p player.contents.map {|thing| thing.name}
puts

# move the player
p player.location.name
player.location = game.room "basement"
p player.location.name
puts

# get the ball
player.location.contents.each { |thing| thing.location = player }
p player.contents.map {|thing| thing.name}
puts

# You can define new methods on the GameContainer that access the internal
# storage used by the "service" methods.
class GameContainer
  def rooms
    @room.values
    # because internally the "room" service uses a hash stored in
    # the @room instance var
  end

  def things
    @thing.values
  end
end

# Show all the rooms
p game.rooms

# Show all the things
p game.things.map {|thing| thing.name}

__END__

Output:

#<Thing:0xb7d862fc @name="Shovel", @location=#<struct Room
name="garden", contents=[#<Thing:0xb7d862fc ...>]>>

#<Thing:0xb7d85078 @name="ball", @location=#<struct Room
name="basement", contents=[#<Thing:0xb7d85078 ...>]>>

["Shovel"]

"garden"
"basement"

["Shovel", "ball"]

[#<struct Room name="garden", contents=>, #<struct Room
name="basement", contents=>]
["ball", "Shovel"]

···

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

If it really bothers you you could construct an ObjectManager, which could
allow you to lookup names in a heirachy.

ball = ObjectManager.reference("/gameobjects/world/objects/balls/1")

Sort of JNDI like.

leslie wrote:

What I want to know is: what's the best way? I had globals $player,
$world, $map
earlier and that makes things a bit easier but it also makes me nervous
(perhaps just because I have been anti-global-indoctrinated for 20 years
now)

You could do it with class variables:
class Map
  private_class_method :new
  @@map = nil
  def Logger.create
    @@map = new unless @@map
    @@map
  end
end

Now you can call from each object that needs the map object:
map=Map.create and it will create a new one if none exists, or send you
the already created object.

Edwin

···

--
Posted via http://www.ruby-forum.com/\.

I am a newbie on ruby, and I am a bit confused by this code, it seems
like a snake eating its own tail! game contains an object world which
is defined as containing game?

Can someone explain this a little to me? How is that different from
using a @@ class object?

Joel VanderWerf wrote:

Leslie Viljoen wrote:

I often have this problem in my projects.

I have a bunch of classes to do various things in my program, but
once instantiated they each need access to each other. For example,
in the adventure game I am working on, I have a Parser object that
needs to manipulate a Player, Map and Things in the map.

I normally do such things by passing objects around when initializing,
like so:

parser = Parser(Player.new(World.new(Map.new, Things.new)))

That works, although it seems a little long winded - for the
parser to modify a thing, it needs to set something like:
player.world.thing["ball"].name = "bat"

Later, the Things needed access to the Map so I could set thing.location
to a map.room object. So I changed thing.location to be a string --
thing.location_name and made a function that could look up a location
based on it's name: map["garden"].

What I want to know is: what's the best way? I had globals $player,
$world, $map
earlier and that makes things a bit easier but it also makes me nervous
(perhaps just because I have been anti-global-indoctrinated for 20 years
now)
I'm still a bit vague on Modules. Any ideas?

Les

Modules are typically used not for organizing instances, but for
grouping methods and constants, so that chuncks of code can be reused in
different situations.

Why not simply replace the globals with a Game class and an instance
that has attributes for player, world, map, etc.?

If your program is complex enough, you may find a technique called
"dependency injection" useful. It's kind of like the Game class
suggestion, with a little extra syntactic sugar. DI is a way of
extracting the connection logic (which you are now doing by "passing
objets around when initializing") from the rest of the object creation
code. With DI, you define containers for your instances, and you can
decide whether a particular kind of instance should be unique in the
container, or there should be one instance for each key (such as a
location name).

There are several DI frameworks for ruby (see RAA). Here's an example
using my own MinDI library:

require 'mindi'

Parser = Struct.new :game # add parser state vars here
Player = Struct.new :game, :location, :contents
World = Struct.new :game # add state vars (:time, maybe)
Map = Struct.new :game
# The :game attr is useful if you want to write methods of
# these classes (Player for example) that refer to the other
# game objects. But it's not strictly necessary.

Room = Struct.new :name, :contents

# Sometimes, a struct is not enough...
class Thing
attr_accessor :name
attr_reader :location

def initialize name, location = nil
   @name = name
   @location = location
end

# Moving a Thing updates the Room it's in or Player who has it
def location=(loc)
   @location.contents.delete self if @location
   @location = loc
   @location.contents << self if @location
end
end

class GameContainer
extend MinDI::Container

# These are some singletons--one instance each.
parser { Parser.new self }
player { Player.new self, start_room, }
world { World.new self }
map { Map.new self }

# A multiton.
# The |name| means there can be many things--one for each name.
# internally, there is a hash stored in @thing that maps
# each name strign to a Thing.
thing { |name| Thing.new name }

# The shovel (unique with that name).
shovel { thing "Shovel" }

room { |name| Room.new name, }

start_room { room "garden" }

# Set up some initial conditions
def initialize
   # create and locate the shovel
   shovel.location = start_room
end
end

game = GameContainer.new

# The shovel is already defined:
p game.shovel
puts

# Create a new thing:
ball = game.thing("ball")
ball.location = game.room "basement"
p ball
puts

player = game.player

# pick up the shovel and anything else in the start room
# This could be made into a #pick_up method of Game or of Player
player.location.contents.each { |thing| thing.location = player }
p player.contents.map {|thing| thing.name}
puts

# move the player
p player.location.name
player.location = game.room "basement"
p player.location.name
puts

# get the ball
player.location.contents.each { |thing| thing.location = player }
p player.contents.map {|thing| thing.name}
puts

# You can define new methods on the GameContainer that access the internal
# storage used by the "service" methods.
class GameContainer
def rooms
   @room.values
   # because internally the "room" service uses a hash stored in
   # the @room instance var
end

def things
   @thing.values
end
end

# Show all the rooms
p game.rooms

# Show all the things
p game.things.map {|thing| thing.name}

__END__

Output:

#<Thing:0xb7d862fc @name="Shovel", @location=#<struct Room
name="garden", contents=[#<Thing:0xb7d862fc ...>]>>

#<Thing:0xb7d85078 @name="ball", @location=#<struct Room
name="basement", contents=[#<Thing:0xb7d85078 ...>]>>

["Shovel"]

"garden"
"basement"

["Shovel", "ball"]

[#<struct Room name="garden", contents=>, #<struct Room
name="basement", contents=>]
["ball", "Shovel"]

Thanks a lot for that reply, it's very interesting indeed.
It's a little over my head, so I am looking into DI.

Les

anne001 wrote:

I am a newbie on ruby, and I am a bit confused by this code, it seems
like a snake eating its own tail! game contains an object world which
is defined as containing game?

Can someone explain this a little to me? How is that different from
using a @@ class object?

Say you have three classes, A, B, and C, and you want their instances to be able to communicate. These three classes will all be instantiated together, so we know that the other objects are there. You then write a class, let's just call it Base, that you instantiate instead of the three classes. It will insatntiate them instead, and pass a reference to itself to each of them.

   class Base
     attr_reader :a, :b, :c

     def initialize
       @a, @b, @c = A.new(self), B.new(self), C.new(self)
     end

     # We'll keep the other classes in here, so we don't
     # pollute the main namespace
     class A
       def initialize(base)
         @base = base
         @b, @c = base.b, base.c
       end
     end

     class B
       def initialize(base)
         @base = base
         @a, @c = base.a, base.c
       end
     end

     class C
       def initialize(base)
         @base = base
         @a, @b = base.a, base.b
       end
     end
   end

Daniel Schierbeck wrote:

anne001 wrote:

I am a newbie on ruby, and I am a bit confused by this code, it seems
like a snake eating its own tail! game contains an object world which
is defined as containing game?

Can someone explain this a little to me? How is that different from
using a @@ class object?

Say you have three classes, A, B, and C, and you want their instances to be able to communicate. These three classes will all be instantiated together, so we know that the other objects are there. You then write a class, let's just call it Base, that you instantiate instead of the three classes. It will insatntiate them instead, and pass a reference to itself to each of them.

  class Base
    attr_reader :a, :b, :c

    def initialize
      @a, @b, @c = A.new(self), B.new(self), C.new(self)
    end

    # We'll keep the other classes in here, so we don't
    # pollute the main namespace
    class A
      def initialize(base)
        @base = base
        @b, @c = base.b, base.c
      end
    end

    class B
      def initialize(base)
        @base = base
        @a, @c = base.a, base.c
      end
    end

    class C
      def initialize(base)
        @base = base
        @a, @b = base.a, base.b
      end
    end
  end

Ah, that is so clever.