Website hosting service by Active-Venture.com
  

 Back to Index

Alternate Object Representations

Nothing requires objects to be implemented as hash references. An object can be any sort of reference so long as its referent has been suitably blessed. That means scalar, array, and code references are also fair game.

A scalar would work if the object has only one datum to hold. An array would work for most cases, but makes inheritance a bit dodgy because you have to invent new indices for the derived classes.

Arrays as Objects

If the user of your class honors the contract and sticks to the advertised interface, then you can change its underlying interface if you feel like it. Here's another implementation that conforms to the same interface specification. This time we'll use an array reference instead of a hash reference to represent the object.

 
    package Person;
    use strict;

    my($NAME, $AGE, $PEERS) = ( 0 .. 2 );

    ############################################
    ## the object constructor (array version) ##
    ############################################
    sub new {
        my $self = [];
        $self->[$NAME]   = undef;  # this is unnecessary
        $self->[$AGE]    = undef;  # as is this
        $self->[$PEERS]  = [];     # but this isn't, really
        bless($self);
        return $self;
    }

    sub name {
        my $self = shift;
        if (@_) { $self->[$NAME] = shift }
        return $self->[$NAME];
    }

    sub age {
        my $self = shift;
        if (@_) { $self->[$AGE] = shift }
        return $self->[$AGE];
    }

    sub peers {
        my $self = shift;
        if (@_) { @{ $self->[$PEERS] } = @_ }
        return @{ $self->[$PEERS] };
    }

    1;  # so the require or use succeeds  

You might guess that the array access would be a lot faster than the hash access, but they're actually comparable. The array is a little bit faster, but not more than ten or fifteen percent, even when you replace the variables above like $AGE with literal numbers, like 1. A bigger difference between the two approaches can be found in memory use. A hash representation takes up more memory than an array representation because you have to allocate memory for the keys as well as for the values. However, it really isn't that bad, especially since as of version 5.004, memory is only allocated once for a given hash key, no matter how many hashes have that key. It's expected that sometime in the future, even these differences will fade into obscurity as more efficient underlying representations are devised.

Still, the tiny edge in speed (and somewhat larger one in memory) is enough to make some programmers choose an array representation for simple classes. There's still a little problem with scalability, though, because later in life when you feel like creating subclasses, you'll find that hashes just work out better.

Closures as Objects

Using a code reference to represent an object offers some fascinating possibilities. We can create a new anonymous function (closure) who alone in all the world can see the object's data. This is because we put the data into an anonymous hash that's lexically visible only to the closure we create, bless, and return as the object. This object's methods turn around and call the closure as a regular subroutine call, passing it the field we want to affect. (Yes, the double-function call is slow, but if you wanted fast, you wouldn't be using objects at all, eh? :-)

Use would be similar to before:

 
    use Person;
    $him = Person->new();
    $him->name("Jason");
    $him->age(23);
    $him->peers( [ "Norbert", "Rhys", "Phineas" ] );
    printf "%s is %d years old.\n", $him->name, $him->age;
    print "His peers are: ", join(", ", @{$him->peers}), "\n";  

but the implementation would be radically, perhaps even sublimely different:

 
    package Person;

    sub new {
	 my $that  = shift;
	 my $class = ref($that) || $that;
	 my $self = {
	    NAME  => undef,
	    AGE   => undef,
	    PEERS => [],
	 };
	 my $closure = sub {
	    my $field = shift;
	    if (@_) { $self->{$field} = shift }
	    return    $self->{$field};
	};
	bless($closure, $class);
	return $closure;
    }

    sub name   { &{ $_[0] }("NAME",  @_[ 1 .. $#_ ] ) }
    sub age    { &{ $_[0] }("AGE",   @_[ 1 .. $#_ ] ) }
    sub peers  { &{ $_[0] }("PEERS", @_[ 1 .. $#_ ] ) }

    1;  

Because this object is hidden behind a code reference, it's probably a bit mysterious to those whose background is more firmly rooted in standard procedural or object-based programming languages than in functional programming languages whence closures derive. The object created and returned by the new() method is itself not a data reference as we've seen before. It's an anonymous code reference that has within it access to a specific version (lexical binding and instantiation) of the object's data, which are stored in the private variable $self. Although this is the same function each time, it contains a different version of $self.

When a method like $him->name("Jason") is called, its implicit zeroth argument is the invoking object--just as it is with all method calls. But in this case, it's our code reference (something like a function pointer in C++, but with deep binding of lexical variables). There's not a lot to be done with a code reference beyond calling it, so that's just what we do when we say &{$_[0]}. This is just a regular function call, not a method call. The initial argument is the string "NAME", and any remaining arguments are whatever had been passed to the method itself.

Once we're executing inside the closure that had been created in new(), the $self hash reference suddenly becomes visible. The closure grabs its first argument ("NAME" in this case because that's what the name() method passed it), and uses that string to subscript into the private hash hidden in its unique version of $self.

Nothing under the sun will allow anyone outside the executing method to be able to get at this hidden data. Well, nearly nothing. You could single step through the program using the debugger and find out the pieces while you're in the method, but everyone else is out of luck.

There, if that doesn't excite the Scheme folks, then I just don't know what will. Translation of this technique into C++, Java, or any other braindead-static language is left as a futile exercise for aficionados of those camps.

You could even add a bit of nosiness via the caller() function and make the closure refuse to operate unless called via its own package. This would no doubt satisfy certain fastidious concerns of programming police and related puritans.

If you were wondering when Hubris, the third principle virtue of a programmer, would come into play, here you have it. (More seriously, Hubris is just the pride in craftsmanship that comes from having written a sound bit of well-designed code.)

 

 

 

Domain name registration service & domain search - 
Register cheap domain name from $7.95 and enjoy free domain services 
 

Cheap domain name search service -
Domain name services at just
$8.95/year only
 

Register domain name -
Buy domain name registration and cheap domain transfer at low, affordable price.

© 2002-2004 Active-Venture.com Web Site Hosting Service

 

[ Never underestimate the bandwidth of a station wagon full of backup tapes hurling down the highway.   ]

 

 
 
 

Disclaimer: This documentation is provided only for the benefits of our web hosting customers.
For authoritative source of the documentation, please refer to http://www.perldoc.com