|Perl: the Markov chain saw|
Objects with Private Variablesby btrott (Parson)
|on Apr 20, 2000 at 23:26 UTC||Need Help??|
Objects with Private VariablesDesigned to answer the question: how can I give my object "private" variables?
IntroductionMany 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 ConstructorYou start by declaring your object normally:
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:
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!
We create a new closure object, blessing it into $class, and return it; and we're done.
Accessor MethodsNow 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:
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.
UsageYou can use this object just as you would any other. Here's a short example:
What's Neat About ThisFrom 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 AlsoThis tutorial is based on the example given in Tom Christiansen's perltoot, so look there for his explanation and some other fun tricks.