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

This is an embarassingly simple question, but its after 5 and my brain is dead. Google and the perl objects book are no help...

Today I refactored and abstracted 3 similar modules I had into one parent class and 3 subclassed. It cut down about half the code. Woo hoo.

Then I realized a shortcoming of my solution. Each of the subclasses has its own version of a certain variable.
package abstracted; our $name = 'parent'; package abstracted::versionA; our $name = 'vA'; package abstracted::versionB; our $name = 'vB';
A shared method moved into the parent class accesses $name. Naturally, it keeps pulling $abstracted::name , while i want $abstracted::versionA::name .

There are 2 ways I can think of solving this:
1. sub new { $self->{'__name'} = \$name }
2. my $name = eval( '\$' . ( ref $self ) . '::name' )
solution #1 is probably better, but i'm trying to cut down on vars i'm tossing in $self -- i'm just tossing too much in there to get things done.

solution #2 is a dirty hack , and probably wildly ineffient - but i only have to modify my code on 1 line ( vs setting the var in 3 sep. constructors )

does anyone have a suggestion or can share an idiomatic way to accomplish this?

Replies are listed 'Best First'.
Re: Accessing class/subclass variables
by Arunbear (Prior) on Mar 25, 2006 at 01:37 UTC
    I'm not sure why you need $name to be a global, but you could try something like this:
    use strict; use warnings; package abstracted; our $name = 'parent'; sub get_that_name { my $self = shift; my $class = ref $self; no strict 'refs'; return ${"${class}::name"}; } package abstracted::versionA; use base 'abstracted'; our $name = 'vA'; package abstracted::versionB; use base 'abstracted'; our $name = 'vB'; package main; foreach my $obj ( map { bless [] => $_ } qw/abstracted abstracted::versionA abstracted::versionB/) { print $obj->get_that_name."\n"; }
    output:
    parent vA vB
    see also symbolic references
Re: Accessing class/subclass variables
by Corion (Patriarch) on Mar 25, 2006 at 08:20 UTC

    If you don't want to write the accessors yourself as in dragonchild's example, you can use Class::Data::Inheritable, which lets you declare and override class variables on a per-class and even per-object basis.

    package abstracted; use base qw(Class::Data::Inheritable); # Set up DataFile as inheritable class data. __PACKAGE__->mk_classdata('name'); __PACKAGE__->name('I call myself the abstracted class'); package abstracted::versionA; use base 'abstracted'; __PACKAGE__->name('vA'); package abstracted::versionB; use base 'abstracted'; __PACKAGE__->name('vB');

    If you want to override the name of abstracted::versionA, you can also call the accessor at runtime.

Re: Accessing class/subclass variables
by dragonchild (Archbishop) on Mar 25, 2006 at 04:48 UTC
    A big question is what you're using this for. There are a few basic scenarios:
    • This is a constant that's overridden in each subclass.

      Solution: use constant NAME => 'vB'; Then, you say $class->NAME or $obj->NAME and you'll get the right one.

    • This is a class variable.

      Solution: You need a place for class data and a method of accessing it. The simplest way would be something like:

      { my %data = ( name => 'vB', ); sub access_class_data { my $class = shift; my ($name, $val) = @_; return $class->SUPER::access_class_data( @_ ) unless exists $data{ $name }; if ( @_ > 1 ) { $data{ $name } = $val; } return $data{ $name }; } }
      The only drawback is that you have to cut'n'paste this code in every class. Furthermore, you have to pre-seed the items in the %data hash. But, it may be sufficient and it is really simple.
    • You're conflating class data and instance data.

      Solution: Put it in the instance.

    My money is on the constant. That's 90% of all class variable usage.


    My criteria for good software:
    1. Does it work?
    2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
      It's two class variables - one is a hash, the other is an array of hashes, so sticking them in a constant is messy ( because then its a constant to an anonymous ref , which is a PITA when dereferencing. Both are used by class instances as configuration settings- i glanced at teh code the other day and realized 'hey, with a few more lines i could totally abract these 3 functions into 1' and did.

      For now, I just stuck a reference to the data in the instance, but i hate doing that with these structures, because now instead of:
      sub func(){ my ($self) = @_; foreach my $a (@b){} foreach my $a (keys %c){} }
      i have
      sub func(){ my ($self) = @_; my @b = @{$self->{'_class_b'}} my %c = %{$self->{'_class_c'}} ... }
      which does that annoying coercion into a new array / hash ( i can't just deref and address the needed items, as i need t o loop and nest through them all)