varanasi has asked for the wisdom of the Perl Monks concerning the following question:

I need help creating objects that relate to one another as I would like. I am uncertain whether I have employed "use parent" correctly. I also cannot understand part of the output from one of my test scripts. (The modules, as I have written them, produce the right results, but I'm not at all certain why they do so or whether I have erred behind the scenes.)

I apologize for the lengthy question. If I knew more, the question would be shorter. I am writing a module to manage "riders" as they interact on and with a common "map." The map is a collection of nodes with various attributes. Riders can change the values of the attributes of the nodes and thereby change the common map.

The relation between my many rider objects and the single map object is what is puzzling me. I have put my modules together like this: A module for rider objects (package Transit), a module for node objects, and a module for the map object (Transit::Map), which is a collection of nodes. The map module "uses" the node module. (I organized the modules in this way and with these names because I wanted my users to worry only about the rider objects. In hindsight I probably should have chosen different names: Rider and Rider::Map make more sense.)

The map module is a parent of the rider module and when the rider module is loaded, it should create a single map object. Here are the relevant parts of the modules.

package Transit; use Moose; . . . use parent 'Transit::Map'; my $map = Transit::Map->new;
-----------------
package Transit::Map; use Moose; . . . has 'map_data' => ( is => 'ro', writer => '_map_data', ); sub BUILD { my $self = shift; my $args = shift; $self->_load_map_data; return; }

The rider module seemed like the correct place to create the single map object. (It didn't feel right to make Transit::Map the parent of the rider module, Transit, but it worked.) Despite my discomfort, my test scripts work fine, i.e. they give the right results.

Nonetheless, in addition to my uncertainty about whether "use parent" is correct, I'm not sure the modules are doing the right thing behind the scenes. When I create two riders, and then use them to print the map object, why don't I get the same hash reference?

use Transit; my $c = Transit->new( ); [change some values of node attributes] my $d = Transit->new( ); say "first map_info object: ", $c->map_data; say "second map_info object: ", $d->map_data;
The output is:
first map_info object: HASH(0xb559c10) second map_info object: HASH(0xae699a8)

I think this output says I have two different map objects, because I have different object references. I think I should only have one, because I should have only one map. And, I don't believe I have two different map objects because the data in the second ($d->map_data) shows the changes made by the first rider. If the map object were an attribute of the rider objects, I would understand, but since the map object should be created when Transit is loaded, I just don't grasp what is going on.

Replies are listed 'Best First'.
Re: How Should I Relate Many Objects Of One Kind To A Single, Common Object Of Another
by tobyink (Canon) on May 24, 2012 at 18:55 UTC

    "The map module is a parent of the rider module"

    Why? That seems odd. If you've really done that, then I don't think you understand inheritance.

    Animal is the parent of Mammal, because mammals are animals. Mammal is the parent of Cat, because cats are mammals. Pet_Shop is not the parent of Cat, because cats are not pet shops.

    Riders don't sound like they are maps, so Map should not be the parent of Rider.

    use parent 'Transit::Map';

    If you're using Moose, then that's not how you do inheritance. Use the Moose extends keyword.

    perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'
      Seemed odd to me, too! I did it this way because it worked, and I couldn't figure out another way to do it.
Re: How Should I Relate Many Objects Of One Kind To A Single, Common Object Of Another
by tobyink (Canon) on May 24, 2012 at 23:17 UTC

    OK, here's a quick little script. The map is the map of the UK, and the nodes are some of its major cities. The riders are a collection of secret agents, one of whom is evil.

    perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'

      Amazing. You rapped this out in three hours?! Thanks.

      By trying to use a rider as the object that ties the pieces together, I created an illogical structure held up with a misuse of inheritance. (I'd still like to use a rider as the primary object, but this approach is undoubtedly more logical and therefore more comprehensible.)

      Thanks again!

        Inheritance is just one relationship that objects and classes can be put into. Delegation, aggregation, composition, etc are sometimes overlooked

        perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'
Re: How Should I Relate Many Objects Of One Kind To A Single, Common Object Of Another
by locked_user sundialsvc4 (Abbot) on May 24, 2012 at 20:31 UTC

    I have a perhaps slightly-unconventional way that I like to handle problems like this, which throws back to how you’d (have to) do it with a database.   Instead of building a lot of “references” from one Perl object to another, I arrange for the various objects to descend from a common parent-class which gives each object a short random-string “moniker” by which any object-instance can be uniquely known.   It is, in effect, a primary-key.   I might then store the objects in a hash by moniker.   I don’t fool around with stuff like use-parent.   I tackle the requirement in-memory exactly as I would have to tackle it in a database schema.