perltutorial
btrott
<h1>Objects with Private Variables</h1>
Designed to answer the question: how can I give my
object "private" variables?<p>
<h2>Introduction</h2>
Many perl objects are implemented as hash references, which
are inherently unprivate. Developers from some other, more
highly-private languages often find this unsatisfactory,
particularly since Perl is "interpreted" and not compiled
into binaries--after all, if anyone can look at your source
and see what variables you're using internally in your object,
and then they can access those variables, isn't that
insecure/dangerous? :)<p>
Anyway, implemented as hash references, it's difficult to
really protect your data. But if you implement your object
as a closure, you can protect your data as much as you'd
like! This method is described in [perltoot], and it's
pretty neat.
<h2>Implementation</h2>
<h3>The Constructor</h3>
You start by declaring your object normally:
<code>
package Person;
use strict;
sub new {
my $type = shift;
my $class = ref $type || $type;
my $self = {
NAME => undef,
AGE => undef
};
</code>
Up until here, everything looks normal: you've declared
a package "Person", and you've started to create a
constructor; you determine the class that this object
will be [bless]'ed into; and you set up the data fields.
A person will have a name and an age.<p>
If we were implementing our object as a hash reference,
we'd bless $self into $class and be done with it. But
that would let any user of our object mess with
NAME and AGE directly, rather than through our accessor
methods.<p>
So, instead of a hash reference, we implement the object
as a closure. A closure is a subroutine reference that
has access to the lexical variables that were in scope when
it was created. So, in this case, we're most concerned
with our closure being able to access the fields in $self.<p>
The closure will be an anonymous subroutine that will act
as an accessor/setter method. We'll give it a field name
(NAME or AGE), and a possible second argument (a value
to set that field to), and it will return the value of
the field. So let's implement that:
<code>
my $closure = sub {
my $field = shift;
@_ and $self->{$field} = shift;
$self->{$field};
};
</code>
Pretty simple: the closure takes a field, sets the field
if it's given a value, and returns the value either
way.<p>
Now the magic: we're going to [bless] our closure into
$class so that the closure *is* the object!
<code>
bless $closure, $class;
$closure;
}
</code>
We create a new closure object, blessing it into $class,
and return it; and we're done.
<h3>Accessor Methods</h3>
Now we just need to write our accessor methods. We need
one of these for each data fields; they'll be the interface
to our object. They'll receive the object (the closure)
as their zeroth argument, and then any additional
information (such as the value to set the field to) as
additional arguments:
<code>
sub name { &{ $_[0] }("NAME", @_[1 .. $#_ ] ) }
sub age { &{ $_[0] }("AGE", @_[1 .. $#_ ] ) }
</code>
As mentioned, these receive the closure (a code
reference) as the zeroth argument, so they invoke that
code reference. The arguments they provide are the field
name ("NAME" or "AGE"), and then a list of any other
arguments that were handed to them.<p>
Thus, what happens is that the closure (which, remember,
has access to our data fields via $self) gets the name
of the field, sets the value of the field if a value
is provided, then returns the value.
<h2>Usage</h2>
You can use this object just as you would any other. Here's
a short example:
<code>
use strict;
use Person;
my $person = new Person;
$person->name("Foo Bar");
$person->age(22);
print $person->name, " is ", $person->age, " years old.\n";
</code>
<h2>What's Neat About This</h2>
From a user's perspective, this looks and acts just like a
"normal" (ie. hash reference) object. But the user has no
way at all of accessing or modifying your data fields
unless you've defined an accessor method.<p>
For example, you could define a new data field
(called "PRIVATE", for example)--and if you didn't define
an accessor method, the user would have no way of accessing
that field. In your implementation, however, you could access
it as much as you wished (although you'd have to go through
the closure to do so).<p>
Which, I think, is pretty neat.
<h2>See Also</h2>
This tutorial is based on the example given in Tom
Christiansen's [perltoot], so look there for his
explanation and some other fun tricks.