Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

comment on

( [id://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?

Introduction

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.

Implementation

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.

Usage

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

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



  • Are you posting in the right place? Check out Where do I post X? to know for sure.
  • Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
    <code> <a> <b> <big> <blockquote> <br /> <dd> <dl> <dt> <em> <font> <h1> <h2> <h3> <h4> <h5> <h6> <hr /> <i> <li> <nbsp> <ol> <p> <small> <strike> <strong> <sub> <sup> <table> <td> <th> <tr> <tt> <u> <ul>
  • Snippets of code should be wrapped in <code> tags not <pre> tags. In fact, <pre> tags should generally be avoided. If they must be used, extreme care should be taken to ensure that their contents do not have long lines (<70 chars), in order to prevent horizontal scrolling (and possible janitor intervention).
  • Want more info? How to link or How to display code and escape characters are good places to start.
Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others studying the Monastery: (8)
As of 2024-03-28 12:28 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found