Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris

Comment on

( #3333=superdoc: print w/ replies, xml ) Need Help??

Objects with Private Variables

Designed to answer the question: how can I give my object "private" variables?


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? :)

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.


The Constructor

You start by declaring your object normally:
package Person; use strict; sub new { my $type = shift; my $class = ref $type || $type; my $self = { NAME => undef, AGE => undef };
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.

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.

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.

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:

my $closure = sub { my $field = shift; @_ and $self->{$field} = shift; $self->{$field}; };
Pretty simple: the closure takes a field, sets the field if it's given a value, and returns the value either way.

Now the magic: we're going to bless our closure into $class so that the closure *is* the object!

bless $closure, $class; $closure; }
We create a new closure object, blessing it into $class, and return it; and we're done.

Accessor Methods

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:
sub name { &{ $_[0] }("NAME", @_[1 .. $#_ ] ) } sub age { &{ $_[0] }("AGE", @_[1 .. $#_ ] ) }
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.

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.


You can use this object just as you would any other. Here's a short example:
use strict; use Person; my $person = new Person; $person->name("Foo Bar"); $person->age(22); print $person->name, " is ", $person->age, " years old.\n";

What's Neat About This

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.

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).

Which, I think, is pretty neat.

See Also

This tutorial is based on the example given in Tom Christiansen's perltoot, so look there for his explanation and some other fun tricks.

In reply to Objects with Private Variables by btrott

Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":

  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • Outside of code tags, you may need to use entities for some characters:
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.
  • Log In?

    What's my password?
    Create A New User
    and the web crawler heard nothing...

    How do I use this? | Other CB clients
    Other Users?
    Others drinking their drinks and smoking their pipes about the Monastery: (14)
    As of 2014-10-31 20:10 GMT
    Find Nodes?
      Voting Booth?

      For retirement, I am banking on:

      Results (223 votes), past polls