[12078] in Perl-Users-Digest
Perl-Users Digest, Issue: 5678 Volume: 8
daemon@ATHENA.MIT.EDU (Perl-Users Digest)
Sat May 15 23:07:17 1999
Date: Sat, 15 May 99 20:01:18 -0700
From: Perl-Users Digest <Perl-Users-Request@ruby.OCE.ORST.EDU>
To: Perl-Users@ruby.OCE.ORST.EDU (Perl-Users Digest)
Perl-Users Digest Sat, 15 May 1999 Volume: 8 Number: 5678
Today's topics:
perltootc - OO Tutorial for Class Data in Perl <tchrist@mox.perl.com>
Re: question about STDOUT <rra@stanford.edu>
Re: TROLL ALERT (Re: Perl "constructors") zenin@bawdycaste.org
Special: Digest Administrivia (Last modified: 12 Dec 98 (Perl-Users-Digest Admin)
----------------------------------------------------------------------
Date: 15 May 1999 20:51:39 -0700
From: Tom Christiansen <tchrist@mox.perl.com>
Subject: perltootc - OO Tutorial for Class Data in Perl
Message-Id: <373e32bb@cs.colorado.edu>
Table of contents:
NAME
DESCRIPTION
Class Data as Package Variables
Putting All Your Eggs in One Basket
Inheritance Concerns
The Eponymous Meta-Object
Indirect References to Class Data
Monadic Classes
Translucent Attributes
Class Data as Lexical Variables
Privacy and Responsibility
File-Scoped Lexicals
More Inheritance Concerns
Locking the Door and Throwing Away the Key
NOTES
SEE ALSO
AUTHOR AND COPYRIGHT
ACKNOWLEDGEMENTS
=head1 NAME
perltootc - Tom's OO Tutorial for Class Data in Perl
=head1 DESCRIPTION
When designing an object class, you are sometimes faced the situation
of wanting common state data shared by all objects of that class.
Such I<class data> act somewhat like global variables for the entire
class, but unlike program-wide globals, class data have meaning only to
the class itself.
Here are a few examples where class data might come in handy:
=over
=item *
to keep a count of the objects you've created, or how many are
still extant.
=item *
to extract the name or file descriptor for a logfile used by a debugging
method.
=item *
to access to collective data, like the total amount dispensed by the
ATMs in a network.
=item *
to access the last object created by a class, or the most accessed object,
or to retrieve a list of all objects (e.g. for persistence).
=back
Unlike a true global, class data should not be accessed directly.
Instead, their state is inspected, and perhaps altered, through the
mediated access of I<class methods>. These class data accessor methods
are similar in spirit and function to accessors used to manipulate the
state of instance data on an object. This provides for a clear firewall
between interface and implementation.
You should allow access to access class data either through the class
name or through any object of that class. If we assume that $an_object
is of type Some_Class, and the &Some_Class::population_count method
accesses class data, then these two invocations should both be possible,
and almost certainly equivalent to each other.
Some_Class->population_count()
$an_object->population_count()
The question is, where do you store the state which that method
accesses?
One could in theory simple use instance data stored on every object to
implement class data But you might not want to do that. For one thing,
it would have some small impact on storage efficiency. More importantly,
duplication could lead to error. To top it off, the semantics of
inheritance might be less than crystal clear.
Class data provide a much cleaner solution that emulation through instance
data. Unlike more restrictive languages like C++, where these are called
static data members, Perl provides no formal, syntactic mechanism to
declare class data, any more than it provides a, syntactic mechanism
to declare instance data. Instead, Perl provides the developer with
a broad set of powerful but flexible features that the creative mind
can use to produce a custom design uniquely crafted to the particular
demands of the situation.
A class in Perl is typically implemented as a module. A module consists
of two complementary feature sets: a package for interfacing with the
outside world, and a lexical file scope for privacy. Each of these two
mechanisms can be used to implement class data. That means you get to
decide whether to put your class data in package variables or to put it
in lexical variables.
And those aren't the only decisions to make. If you choose to use package
variables, you can make your class data accessor methods either ignorant
of inheritance or sensitive to it. If you choose lexical variables,
you can elect to permit access to them from anywhere in the entire file
scope, or you can limit direct data access exclusively to the methods
implementing those attributes.
=head1 Class Data as Package Variables
Because a class in Perl is really just a package, using package variables
to hold class data is the most natural choice. This way makes it simple
for each class to have its own class data. Let's say you have a class
called Some_Class that needs a couple of different attributes that
you'd like global to the entire class. The simplest thing to do is
to use $Some_Class::CData1 and $Some_Class::CData2 to represent these.
But we certainly don't want to encourage outsiders to diddle those bits
directly, so we provide access methods to mediate manipulations on them.
In our accessor methods below, we'll for now just ignore the first
argument--that bit to the left of the arrow on method invocation, which
is either a class name or an object reference.
package Some_Class;
sub CData1 {
shift; # XXX: ignore calling class/object
$Some_Class::CData1 = shift if @_;
return $Some_Class::CData1;
}
sub CData2 {
shift; # XXX: ignore calling class/object
$Some_Class::CData2 = shift if @_;
return $Some_Class::CData2;
}
This technique is highly legible and should be completely straightforward
to even the novice Perl programmer. By fully qualifying the package
variables, they stand out clearly when reading the code. Unfortunately,
if you misspell one of these, you've introduced an error that's hard
to catch. It's also somewhat unnerving to see the class name itself
hard-coded in so many places.
Both these problems can be easily fixed. Just add the C<use strict>
pragma, then pre-declare your package variables.
package Some_Class;
use strict;
our($CData1, $CData2); # our() is new to perl5.006
sub CData1 {
shift; # XXX: ignore calling class/object
$CData1 = shift if @_;
return $CData1;
}
sub CData2 {
shift; # XXX: ignore calling class/object
$CData2 = shift if @_;
return $CData2;
}
As with any other global variable, some programmers prefer to start their
package variables with capital letters. This helps clarity somewhat, but
by no longer fully qualifying the package variables, their significance
can be lost when reading the code. You can fix this easily enough by
choosing better names than used here.
=head2 Putting All Your Eggs in One Basket
Just as the mindless enumeration of accessor methods for instance data
grows tedious after the first few (see L<perltoot>), so too does the
repetition begin to grate when listing out accessor methods for class
data. Repetition runs counter to the primary virtue of a programmer:
Laziness, here manifesting as that innate urge every programmer feels
to factor out duplicate code whenever possible.
Here's what to do. First, make just one hash that contains all the data
attributes of the class proper (rather than of an object of that class).
package Some_Class;
use strict;
our %ClassData = ( # our() is new to perl5.006
CData1 => "",
CData2 => "",
);
Now clone off class data accessor methods for each key in the
%ClassData hash.
for my $datum (keys %ClassData) {
no strict "refs"; # to register new methods in package
*$datum = sub {
shift; # XXX: ignore calling class/object
$ClassData{$datum} = shift if @_;
return $ClassData{$datum};
}
}
It's true that you could in theory negotiate a solution employing
an &AUTOLOAD method, but this is unlikely to prove satisfactory.
It would have to distinguish between class data and object data; it could
interfere with inheritance; and it would have to careful about DESTROY.
Such complexity is uncalled for in most cases, and certainly in this one.
You may wonder why we're rescinding strict refs for the loop. We're
manipulating the package's symbol table to introduce new function names
using symbolic references (indirect naming), which the strict pragma would
otherwise forbid. Normally, symbolic references are a dodgy notion at
best, because they can be used accidentally when you aren't meaning to,
and because for most kinds of uses to which beginning Perl programmers
attempt to put them, we have much better approaches, like nested hashes.
But contrary to popular belief, there's absolutely nothing wrong with
willfully using symbolic references to manipulate something that
makes sense only in the context of a package, like method names or
package variables.
Clustering all the class variables in one place has several advantages.
They're easy to spot, initialize, and change. The aggregation also
makes them convenient to access externally, such as from a debugger
or a persistence package. The only possible problem is that we don't
automatically know the name of each class's class object, should it have
one. This issue is addressed below in L<"The Eponymous Meta-Object">.
=head2 Inheritance Concerns
What happens if we subclass a class like those from the preceding
examples? The derived class inherits all the base class's methods,
including those that access class data. But what package is the
class data stored in? The answer is that, as written, class data
are stored in the package into which those methods were compiled.
When you invoke the CData1 method on the name of derived class or on
one of that class's objects, the version shown above is still run, and
you'll access $Some_Class::CData1--or in the method cloning version,
C<$Some_Class::ClassData{CData1}>.
Think of these class methods as executing in the context of their base
class, not in that of their derived class. Sometimes this is exactly
what you want. If Feline subclasses Carnivore, then the population of
Carnivores in the world should go up when a new Feline is born.
But what if you wanted to figure out how many Felines you have apart
from Carnivores? The current approach doesn't support that.
You'll have to decide on a case-by-case basis whether it makes any sense
for class data to be package-relative. If you want it to be so,
then stop ignoring the first argument to the function. It will either
be a class name if the method was invoked directly on a class name,
or else it will be an object reference if the method was
invoked on an object of some class, in which case the ref() function
provides the class of that object. Use the resulting class name
as the package in which to look up the variable.
package Some_Class;
sub CData1 {
my $obclass = shift;
my $class = ref($obclass) || $obclass;
my $varname = $class . "::CData1";
no strict "refs"; # to access package data symbolically
$$varname = shift if @_;
return $$varname;
}
And then do likewise for all other class data attributes (such as CData2,
etc.) that you wish to access package variables in the invoking package
instead of the compiling package as we had previously.
Once again we temporarily disable the strict references ban, because
otherwise we couldn't use the fully-qualified symbolic name for
the package global. This is perfectly reasonable: since all package
variables by definition live in a package, there's nothing wrong with
accessing them via that package's symbol table. That's what it's there
for (well, somewhat).
What about the case of using a single hash for everything and then cloning
methods? What would that look like? The only difference would be the
closure used to produce new method entries for the class's symbol table.
no strict "refs";
*$datum = sub {
my $obclass = shift;
my $class = ref($obclass) || $obclass;
my $varname = $class . "::ClassData";
$varname->{$datum} = shift if @_;
return $varname->{$datum};
}
=head2 The Eponymous Meta-Object
The %ClassData hash in the previous example is the neither the most
imaginative nor the most intuitive of names. Is there something else that
might make more sense, be more useful, or both?
Yes, there is. For the (unblessed) "class meta-object", we'll use a
package variable of the same name as the package itself. In our case,
that's %Some_Class::Some_Class. This is reminiscent of classes that
name their constructors eponymously; that is, class Some_Class would
use &Some_Class::Some_Class as a constructor. Within the scope of a
package Some_Class declaration, the eponymously named hash %Some_Class is
that package's meta-object. This predictable approach has
many benefits. These benefits include having a well-known identifier to
be used to aid in debugging, transparent persistence, or checkpointing,
but the place where the eponymous meta-object will really shine later
when we discuss exciting new topics like monadic classes
and translucent attributes.
package Some_Class;
use strict;
# create class meta-object using that most perfect of names
our %Some_Class = ( # our() is new to perl5.006
CData1 => "",
CData2 => "",
);
# this accessor is calling-package-relative
sub CData1 {
my $obclass = shift;
my $class = ref($obclass) || $obclass;
my $varname = $class . "::" . $class;
no strict "refs"; # to access eponymous meta-object
$varname->{CData1} = shift if @_;
return $varname->{CData1};
}
# but this accessor is not
sub CData2 {
shift; # XXX: ignore calling class/object
no strict "refs"; # to access eponymous meta-object
__PACKAGE__ -> {CData2} = shift if @_;
return __PACKAGE__ -> {CData2};
}
In the second accessor method, the __PACKAGE__ notation was used for
two reasons. First, to avoid hardcoding the literal package name
in the code in case we later want to change that name. Second, to
clarify to the reader that what matters here is the package currently
being compiled into, not the package of the invoking object or class.
If the long string of non-alphabetic characters bothers you, you can
always put the __PACKAGE__ in a variable first.
sub CData2 {
shift; # XXX: ignore calling class/object
no strict "refs"; # to access eponymous meta-object
my $class = __PACKAGE__;
$class->{CData2} = shift if @_;
return $class->{CData2};
}
Even though we're using symbolic references for good not evil, some
folks tend to become unnerved when they see so many places where strict
ref checking is disabled. Given a symbolic reference, you can always
produce a real reference (the reverse is not true, though). So we'll
create a subroutine that does this conversion for us. If invoked as a
function of no arguments, returns a reference to the compiling class's
eponymous hash that we've been using as a meta-object. Invoked as a
class method, it returns a reference to the eponymous hash of its caller.
And when invoked as an object method, this function returns a reference
to the eponymous hash for the object's class.
package Some_Class;
use strict;
our %Some_Class = ( # our() is new to perl5.006
CData1 => "",
CData2 => "",
);
# tri-natured: function, class method, or object method
sub _classobj {
my $obclass = shift || __PACKAGE__;
my $class = ref($obclass) || $obclass;
no strict "refs"; # to convert sym ref to real one
return \%{ $class . "::" . $class };
}
for my $datum (keys %{ _classobj() } ) {
# turn off strict refs so that we can
# register a method in the symbol table
no strict "refs";
*$datum = sub {
use strict "refs";
my $self = shift->_classobj();
$self->{$datum} = shift if @_;
return $self->{$datum};
}
}
=head2 Indirect References to Class Data
A reasonably common strategy for handling class data is to a store
reference to each class attribute on the object itself. This is
a strategy you've probably seen before, such as in L<perltoot> and
L<perlbot>, but there may be baroque variations on this theme
in the example below that you haven't thought before.
package Some_Class;
our($CData1, $CData2); # our() is new to perl5.006
sub new {
my $obclass = shift;
return bless my $self = {
ObData1 => "",
ObData2 => "",
CData1 => \$CData1,
CData2 => \$CData2,
} => (ref $obclass || $obclass);
}
sub ObData1 {
my $self = shift;
$self->{ObData1} = shift if @_;
return $self->{ObData1};
}
sub ObData2 {
my $self = shift;
$self->{ObData2} = shift if @_;
return $self->{ObData2};
}
sub CData1 {
my $self = shift;
my $dataref = ref $self
? $self->{CData1}
: \$CData1;
$$dataref = shift if @_;
return $$dataref;
}
sub CData2 {
my $self = shift;
my $dataref = ref $self
? $self->{CData2}
: \$CData2;
$$dataref = shift if @_;
return $$dataref;
}
As written above, a derived class will inherit these methods, which will
consequently access class data in the base class's package. This is not
necessarily expected behavior in all circumstances. Here's an example
that uses a variable meta-object, taking care to access the proper
package's data.
package Some_Class;
use strict;
our %Some_Class = ( # our() is new to perl5.006
CData1 => "",
CData2 => "",
);
sub _classobj {
my $self = shift;
my $class = ref($self) || $self;
no strict "refs";
# get (hard) ref to eponymous meta-object
return \%{ $class . "::" . $class };
}
sub new {
my $obclass = shift;
my $classobj = $obclass->_classobj();
bless my $self = {
ObData1 => "",
ObData2 => "",
CData1 => \$classobj->{CData1},
CData2 => \$classobj->{CData2},
} => (ref $obclass || $obclass);
return $self;
}
sub ObData1 {
my $self = shift;
$self->{ObData1} = shift if @_;
return $self->{ObData1};
}
sub ObData2 {
my $self = shift;
$self->{ObData2} = shift if @_;
return $self->{ObData2};
}
sub CData1 {
my $self = shift;
$self = $self->_classobj() unless ref $self;
my $dataref = $self->{CData1};
$$dataref = shift if @_;
return $$dataref;
}
sub CData2 {
my $self = shift;
$self = $self->_classobj() unless ref $self;
my $dataref = $self->{CData2};
$$dataref = shift if @_;
return $$dataref;
}
We're now not only strict refs clean, using an eponymous meta-object
seems to make the code cleaner. Unlike the previous version, this one
does the something reasonable in the face of inheritance: it affects the
class meta-object in the invoking class instead of the one into which
the method was initially compiled.
Inquisitive folk can easily access data in the class meta-object, making
it easy to dump the complete class state using an external mechanism such
as for debugging or to implement a persistent class. This works because
the class meta-object is a package variable, has a well-known name,
clusters all its data together. (To be honest, transparent persistence
is not always feasible, but it's certainly an appealing idea.)
There's still no check that the object accessor methods have not been
invoked on the classname. If strict ref checking is enabled, you'd
blow up. If not, then you get the eponymous meta-object. What you do
with--or about--this is up to you. The next two sections demonstrate
innovative uses for this powerful feature.
=head2 Monadic Classes
Several standard modules shipped with Perl provide class interfaces
without any attribute methods whatsoever. The most commonly
used module not numbered amongst the pragmata, the Exporter module, is
a class with neither constructors nor attributes. Its job is simply to
provide a standard interface for modules wishing to export part of their
namespace into that of their caller. Modules use the Exporter's &import
method by setting their inheritance list in their package's @ISA array
to include that package. But Exporter provides no constructor, so you
can't have several instances of the class. In fact, you can't have any.
It doesn't make any sense. All you get is are its methods. Its interface
contains no statefulness, so state data is wholly superfluous.
Another sort of class that pops up from time to time is one that supports
a single, unique instance. Such class are called I<monadic classes>,
or less formally, I<highlander classes>, since there can be only one.
If a class is monadic, where do you store its state, that is, its data
attributes? While you could merely use a slew of package variables,
it's a lot cleaner to use the eponymously named hash. Here's a complete
example of a monadic class:
package Cosmos;
%Cosmos = ();
# accessor method for "name" attribute
sub name {
my $self = shift;
$self->{name} = shift if @_;
return $self->{name};
}
# accessor method for "birthday" attribute
sub birthday {
my $self = shift;
$self->{birthday} = shift if @_;
return $self->{birthday};
}
# accessor method for "stars" attribute
sub stars {
my $self = shift;
$self->{stars} = shift if @_;
return $self->{stars};
}
# oh my - one of our stars just went out!
sub nova {
my $self = shift;
my $count = $self->stars();
$self->stars($count - 1) if $count > 0;
}
# constructor/initializer method - fix by reboot
sub bigbang {
my $self = shift;
%$self = (
name => "the world according to tchrist",
birthday => time(),
stars => 0,
);
return $self; # yes, it's probably a class. SURPRISE!
}
__PACKAGE__ -> bigbang();
Hold on, that doesn't look like anything special. Those attribute accessors
look no different than they would if this were a regular class instead
of a monadic one. The crux of the matter is there's nothing that says
that $self must hold a reference to a blessed object. It just has to
be something you can invoke methods on. Here the package name
itself, Cosmos, works as an object. Look at the &nova method. Is that
a class method or an object method? The answer is that static analysis
cannot reveal the answer. Perl doesn't care, and neither should you.
In the three attribute methods, using C<%$self> really uses the %Cosmos
package variable. After the class is compiled, but before any C<use>
or C<require> returns, we start the universe with a bang.
If like Stephen Hawking, you posit the existence of multiple, sequential,
and unrelated universes, then you can invoke the &bigbang method yourself
at any time to start everything all over again. You might think of
&bigbang as more of an initializer than a constructor, since the function
doesn't allocate new memory; it only initializes what's already there.
But like any other constructor, it does return its self reference, which
you can then use for later method invocations. The astonishing thing is
that the object return by the &bigbang constructor is not a reference
to a blessed object. It's the class's own name. Perl doesn't mind a
whit, and neither should you. A class name is for all virtually all
intents and purposes, a perfectly acceptable object. It has state and
behavior, and although its identity might be deemed a trifle unwavering,
it certainly manifests inheritance, polymorphism, and encapsulation.
And what more can you ask of an object?
What happens, though, if some day down the road, you decide that one universe
at a time just isn't enough. You could write a new class from scratch,
but you already have an existing class that does what you want, except
that it's monadic, and you want more cosmoi than just one.
That's what subclassing is all about. Look how brief your task turns
out to be:
package Multiverse;
use Cosmos;
@ISA = qw(Cosmos);
sub new {
my $protoverse = shift;
my $class = ref $protoverse || $protoverse;
my $self = {};
return bless($self, $class)->bigbang();
}
1;
Because we were careful to be good little creators when we designed our
Cosmos class, we could reuse it without touching a single bit of code
when it came time to write our Multiverse class. The same code that
worked when invoked as a class method continues to work perfectly
well when invoked against separate instances of a derived class.
=head2 Translucent Attributes
The package's eponymous hash be used for more than just containing
per-class, global state data. It can also server as a sort of template
containing default settings for object attributes. These default settings
can then be used in contructors for initialization of a particular object.
The class's eponymous hash can also be used to implement I<translucent
attributes>. A translucent attribute is an object attribute which, when
the value is retrieved from an object that has not intentionally set
that particular attribute, will instead return the value for that field
in the eponymous meta-object. In other words, translucent attributes
"see through" uninitialized object attribute to get at the meta-object's
values instead.
We'll use something of a copy-on-write approach to these translucent
attributes. If you're just fetching values from them, you get
translucency. But if you store a new value to them, that new value
lodges on the current object. On the other hand, if you use the class
as an object and store the attribute value directly on the class, then
the meta-object's value changes, and later fetch operations on
objects with uninitialized values for those attributes will retrieve the
meta-object's new values. Objects with their own initialized values,
however, won't see any change.
Let's look at some concrete examples of using these properties before we
show how to implement them. Suppose that a class named Some_Class
had a translucent data member called "color". First you set the color
in the meta-object, then you create three objects using a constructor
that happens to be named &spawn.
use Some_Class;
Some_Class->color("vermilion");
$ob1 = Some_Class->spawn(); # so that's where Jedi come from
$ob2 = Some_Class->spawn();
$ob3 = Some_Class->spawn();
print $obj3->color(); # prints "vermilion"
Each of these objects' colors is now "vermilion", because that's the value
that the meta-object has for that attribute, and the objects in question
don't have their own color values.
Changing the attribute on one object has no effect on other objects
previously created.
$ob3->color("chartreuse");
print $ob3->color(); # prints "chartreuse"
print $ob1->color(); # prints "vermilion", translucently
If you now use $ob3 to spawn off another object, the new object will
take the color its parent held, which happens to now be "chartreuse".
That's because the constructor will always use the invoking object as
its template for initializing data attributes. When the constructor's
invoking object is the class name, the object used as template is is the
eponymous. But when the invoking object is a reference to an instantiated
object, the &spawn constructor uses the existing object as a template
and copies in its values instead of leaving them translucently undefined.
$ob4 = $ob3->spawn();
print $ob4->color(); # prints "chartreuse"
Now change the color attribute on the entire class:
Some_Class->color("azure");
print $ob1->color(); # prints "azure"
print $ob2->color(); # prints "azure"
print $ob3->color(); # prints "chartreuse"
print $ob4->color(); # prints "chartreuse"
That color change took effect only in the first pair objects, which were
still translucently accessing the meta-object's values. The second pair
had per-object initialized colors, and so didn't change.
One important question remains. Changes to the meta-object are reflected
in translucent attributes in the entire class, but what about
changes to discrete objects? If you change the color of $ob3, does the
value of $ob4 see that change? Or vice-versa. If you change the color
of $ob4, does then the value of $ob3 shift?
$ob3->color("amethyst");
print $ob3->color(); # prints "amethyst"
print $ob4->color(); # hmm: "chartreuse" or "amethyst"?
While one could argue that in certain rare cases it should, let's not
do that. Good taste aside, we want the answer to the question posed in
the comment above to be "chartreuse", not "amethyst". So we'll treat
these attributes somewhat the way process attributes like environment
variables, user and group IDs, or the current working directory behave
after a fork(). In short, you can only change yourself and see those
changes reflected in your unspawned children. Changes to one object will
not propagate up to the parent or down to any existing child objects.
New ones you make later, though, will see it.
If you decide you want to render an object's attribute value translucent
once again after having set it to a discrete value, all you have
to do is call the accessor method with an argument of C<undef>.
$ob4->color(undef); # back to "azure"
Here's a complete implementation of Some_Class as described above.
package Some_Class;
# here's the class meta-object, eponymously named.
# it holds all class data, and also all instance data
# so the latter can be used for both initialization
# and translucency.
our %Some_Class = ( # our() is new to perl5.006
PopCount => 0, # capital for class data
color => "beige", # small for instance data
);
# constructor method
# callable as class method or object method
sub spawn {
my $obclass = shift;
my $class = ref($obclass) || $obclass;
my $self = {};
bless($self, $class);
$class->{PopCount}++;
# init fields from invoking object, or omit if
# invoking object is the class to provide translucency
%$self = %$obclass unless $class;
return $self;
}
# translucent accessor for "color" attribute
# callable as class method or object method
sub color {
my $self = shift;
my $class = ref($self) || $self;
# handle class invocation
unless (ref $self) {
$class->{color} = shift if @_;
return $class->{color}
}
# handle object invocation
$self->{color} = shift if @_;
if (defined $self->{color})) { # not exists!
return $self->{color};
} else {
return $class->{color};
}
}
# class data accessor for "PopCount" attribute
# callable as class method or object method
# but uses object solely to locate meta-object
sub population {
my $obclass = shift;
my $class = ref($obclass) || $obclass;
return $class->{PopCount};
}
# instance destructor
# invoked only as object method
sub DESTROY {
my $self = shift;
my $class = ref $self;
$class->{PopCount}--;
}
If you prefer to install your accessors more generically, you can make
use of the upper-case versus lower-case convention to register into the
package appropriate methods cloned from generic closures.
for my $datum (keys %{ +__PACKAGE__ }) {
*$datum = ($datum =~ /^[A-Z]/)
? sub { # install class accessor
my $obclass = shift;
my $class = ref($obclass) || $obclass;
return $class->{$datum};
}
: sub { # install translucent accessor
my $self = shift;
my $class = ref($self) || $self;
unless (ref $self) {
$class->{$datum} = shift if @_;
return $class->{$datum}
}
$self->{$datum} = shift if @_;
return defined $self->{$datum}
? $self -> {$datum}
: $class -> {$datum}
}
}
Translations to C++, Java, and Python have been left as exercises for
the reader. Be sure to send us mail as soon as you're done.
=head1 Class Data as Lexical Variables
=head2 Privacy and Responsibility
Did you happen to notice how in the previous examples, we didn't
prefix the package variables used for class data with an underscore,
nor did we do so for the names of the hash keys used for instance data?
You don't need little markers on data names to suggest nominal privacy
on your attribute variables or hash keys, because these are B<already>
notionally private! Outsiders have no business whatsoever playing with
anything within a class save through the mediated access of
its documented interface; in other words, through its method invocations.
But if they do go messing around, it's not your fault--it's theirs.
Perl believes in individual responsibility rather than mandated control.
Perl respects you enough to let you choose your own preferred level of
pain, or of pleasure. Perl believes that you are creative, intelligent,
and capable of making your own decisions--and fully expects you to take
complete responsibility for your own actions.
In a perfect world, these admonitions alone would suffice, and everyone
would be intelligent, responsible, happy, and creative. And careful.
One probably shouldn't forget careful, and that's a good bit harder
to expect. Even Einstein would take wrong turns by accident and end up
lost in the wrong part of town.
Some folks get the heebie-jeebies when they see package variables hanging
out there for anyone to reach in and diddle. Some of them are afraid
someone else might do something wicked. The solution? Why, fire the
wicked, of course! But they are also afraid that they or others will
do something not so much wicked as careless, whether by accident or out
of desperation. If we fired everyone who ever got careless, there'd be
nobody left.
Merited or not, paranoia is a dangerous thing, so how do take the edge
off their paranoia? By providing the option of storing class data as
lexicals instead of as package variables as we have up to now done.
The my() operator is the font of all privacy in Perl. It is widely
perceived, and indeed has often been written, that Perl provides no data
hiding, that it affords the class designer no privacy nor isolation,
merely a rag-tag assortment of weak and unenforcible conventions instead.
This perception is demonstrably false and easily disproven. In the
next section, we show how to implement forms of privacy far, far
stronger than those provided in nearly any other object-oriented language.
Compared with the utter privacy that Perl can provide if you but ask,
those languages begin to take on the permissive demeanors of libertine
floosies.
=head2 File-Scoped Lexicals
A lexical variable is visible only through the end of its static scope.
That means that the only code that will be able to access that variable
is that code lying textually below the my() operator through of its
block if it has one, or through the end of the current file if it doesn't.
Starting again with our simplest example given at the start of this
document, we replace our() variables with my() versions.
package Some_Class;
my($CData1, $CData2); # file scope, not in any package
sub CData1 {
shift; # XXX: ignore calling class/object
$CData1 = shift if @_;
return $CData1;
}
sub CData2 {
shift; # XXX: ignore calling class/object
$CData2 = shift if @_;
return $CData2;
}
So much for that old $Some_Class::CData1 package variable and its brethren!
Those are gone now, replaced with lexicals. No one outside the
scope can reach in and diddle the class state without resorting to the
documented interface. Not even subclasses or superclasses of
this one have unmediated access to $CData1. They have to invoke the &CData1
method against Some_Class or an instance thereof, just like anybody else.
To be honest, that last statement assumes you haven't gone off and packed
several classes together into the same file scope, nor strewn your class
implementation across several different files. Accessibility of those
variables is based uniquely on the static file scope. It has nothing to
do with the package. So code in a different file but the same package
(class) could not access those variables, yet code in the same file but a
different package (class) could. There are good reasons why we usually
suggest that one keep a one-to-one mapping between files and packages
and modules and classes. You don't have to, but you're apt to confuse
yourself otherwise.
Requiring even your own class to use accessor methods like anyone else is
probably a good thing. But demanding and expecting that everyone else, be
they subclass or superclass, friend or foe, will all come to your object
through mediation is more than just a good idea. It's absolutely critical
to the model. Let there be in your mind no such thing as "public"
data, nor even that horribly ill-conceived notion of "protected" data.
As soon as you take that first step out of the solid model in which all
data is considered completely private, save from the perspective of its
own accessor methods, you have violated the envelope. And having pierced
that envelope of abstraction, you shall doubtless someday pay the price,
as future changes in the implementation break unrelated code, which is
precisely what used object to avoid in the first place.
If you'd like to aggregate your class data into one lexically scoped,
composite structure, you're perfectly free to do so.
package Some_Class;
my %ClassData = (
CData1 => "",
CData2 => "",
);
sub CData1 {
shift; # XXX: ignore calling class/object
$ClassData{CData1} = shift if @_;
return $ClassData{CData1};
}
sub CData2 {
shift; # XXX: ignore calling class/object
$ClassData{CData2} = shift if @_;
return $ClassData{CData2};
}
To make this more scalable as more class data attributes are added,
we can again register closures into the package symbol table much as
before to create accessor methods for them.
package Some_Class;
my %ClassData = (
CData1 => "",
CData2 => "",
);
for my $datum (keys %ClassData) {
no strict "refs";
*$datum = sub {
shift; # XXX: ignore calling class/object
$ClassData{$datum} = shift if @_;
return $ClassData{$datum};
};
}
=head2 More Inheritance Concerns
It's about time to deal with the XXX issue again. Imagine that Some_Class
were used as a base class from which to derive Another_Class. If you
invoked a &CData method on the derived class or on an object of that class,
what would you get? Would the derived class have its own state, or
would it piggyback on its base class's versions of the class attributes?
The answer is that under the scheme outlined above, the derived class
would B<not> have its own state data. As before, whether you consider
this a good thing or a bad one depends on the semantics of the classes
involved and on the class data itself.
A clean way to implement per-class state, if that's what you want,
is to provide a helper method to fetch a reference to the class
object. We'll invoke that method &_classobj as we did earlier.
package Some_Class;
use strict;
my %ClassData = (
CData1 => "",
CData2 => "",
);
sub _classobj { \%ClassData }
for my $datum (keys %{ _classobj() } ) {
no strict "refs";
*$datum = sub {
my $self = shift->_classobj();
$self->{$datum} = shift if @_;
return $self->{$datum};
}
}
If you subclass Some_Class, now all you need to do to get separate,
per-class class attributes is to override the &_classobj
method in the derived class.
=head2 Locking the Door and Throwing Away the Key
As currently implemented, any code within the same scope as the
file-scoped lexical %ClassData can diddle it should they be so inclined.
Is that ok? Is it acceptable or even desirable to allow other parts of
the implementation of this class to access class data directly?
That depends on how much of a protocol stickler you want
to be. Think back to the Cosmos class. If the &nova method had directly
diddled $Cosmos::Stars or C<$Cosmos::Cosmos{stars}>, then we wouldn't
have been able to reuse the class when it came to inventing a Multiverse.
So letting even the class itself access its own class data without the
mediating intervention of properly designed accessor methods is probably
not a good idea after all.
Restricting access to class data from the class itself is usually not
enforcible even in strongly object-oriented languages. Is this something
that in Perl B<can> be enforced? Why of course it is! This is Perl,
in which all things are possible, albeit perhaps not always expedient.
Here's one way:
package Some_Class;
{ # scope for hiding $CData1
my $CData1;
sub CData1 {
shift; # XXX: unused
$CData1 = shift if @_;
return $CData1;
}
}
{ # scope for hiding $CData2
my $CData1;
sub CData2 {
shift; # XXX: unused
$CData2 = shift if @_;
return $CData2;
}
}
No one--absolutely no one--is allowed to diddle the class
data without the mediation of the managing accessor method,
since it and it alone can see its respective lexical variable.
Note that this use of mediated access to class data is far stronger
privacy than most OO languages provide. A black hole is more apt to
bleed data than are lexical closures. Perl's flexibility is such that
you can make classes that are even more privacy freaked than you can
is oft-touted systems with mixed private, partial, protected, public,
persnickety, friend, and foe datum attributes.
You could also use just one generic function to access all class data,
then hide those data in a lexical that's scoped to that function alone.
package Some_Class;
{
my %ClassData = (
CData1 => "",
CData2 => "",
);
sub classdata {
my ($obclass, $datum, $newval) = @_;
die "No such class datum: $datum"
unless exists $ClassData{$datum};
$ClassData{$datum} = $newval if @_ > 2;
return $ClassData{$datum};
}
}
In code that used that module, you might write
something like this:
my $obj = Some_Class->construction_function();
Some_Class->classdata(CData1 => "value");
print $obj->classdata("CData2");
If you don't like having just one accessor function, you could accomplish
the same thing using a single class meta-object and separate accessor
methods. Again, even other code in the class itself must go through
proper channels to get at the data.
package Some_Class;
{ # scope for class meta-object
my %ClassData = (
CData1 => "",
CData2 => "",
);
sub CData1 {
shift; # XXX: unused
$ClassData{CData1} = shift if @_;
return $ClassData{CData1};
}
sub CData1 {
shift; # XXX: unused
$ClassData{CData2} = shift if @_;
return $ClassData{CData2};
}
}
Two issues remain to chafe at our sensibilities. For one thing, there's
an unsatisfying repetition of code in creating per-datum accessor
methods. For another, the XXX ward against evil coding alerts us that
someday, somewhere, something might not work right. In particular,
derived classes will share their class state with their base class.
We're not package-relative here. But then again, we really can't be
package-relative, since packages are about grouping and sharing, and
we're trying to put road-blocks in the way of that kind of sharing.
Let's see what we can come up with anyway. We'll use a &_classobj method
to open a peephole into our collection of class data.
package Some_Class;
{ # scope for class meta-object
my %ClassData = (
CData1 => "",
CData2 => "",
);
sub _classobj { \%ClassData }
for my $datum (keys %{ _classobj() } ) {
no strict "refs";
*$datum = sub {
use strict "refs";
my $self = shift->_classobj();
$self->{$datum} = shift if @_;
return $self->{$datum};
}
}
}
Now when we subclass Some_Class, we can override &_classobj to provide
our own state data in the derived class separate from those in the
base class. (At some level, something smells fundamentally wrong
with even contemplating a need to override a method whose name begins
with an underscore.) Some might point out that there's no restriction
(beyond good taste) against anybody calling the &_classobj method and
thereby securing a reference to our private data. We have seen some
authors bend over backwards to try to stop this, even going so far into
paranoia to write code like this:
sub _classobj {
my @data = caller(1);
if (caller ne __PACKAGE__ || (@data &&
$data[1] ne __FILE__
&& $data[3] ne (__PACKAGE__ . "::__ANON__")
)
)
{
require Carp;
Carp::confess("illegal access to class data");
}
return \%ClassData;
}
It's at this point, or preferably a good bit before, that good taste
and common sense should have squelched the unhealthy urge to muddle
about in such twisted shenanigans. Being Perl, this is of course
not forbidden, but it's gone way past silly or clever and well
into tortuous and painful. If you find yourself building this kind of
fortress around your code, perhaps it's time to reconsider why you're
writing it in the first place. If your relationship with the users of
your code has so degraded that you feel this kind of thing necessary,
that give up now, because they can always just hack the source code,
the binary, or the interpreter. It's not worth the hassle trying to
defend your property against well-armed, professional SWAT teams with
a contract on your bone china.
=head1 NOTES
I use the hypothetical our() syntax for package variables. It works
like use vars, but looks like my(). It should be 5.006, I hope.
The usual mealy-mouthed package mungeing doubtless applies to setting
up names of instance data. For example, C<$self-E<gt>{ObData1}> should
probably be C<$self-E<gt>{ __PACKAGE__ . "_ObData1" }>, but that would
just confuse the examples.
=head1 SEE ALSO
L<perltoot>, L<perlobj>, L<perlmod>, and L<perlbot>.
The Tie::SecureHash module from CPAN is worth checking out.
=head1 AUTHOR AND COPYRIGHT
Copyright (c) 1999 Tom Christiansen
All rights reserved.
When included as part of the Standard Version of Perl, or as part of
its complete documentation whether printed or otherwise, this work
may be distributed only under the terms of Perl's Artistic License.
Any distribution of this file or derivatives thereof I<outside>
of that package require that special arrangements be made with
copyright holder.
Irrespective of its distribution, all code examples in this file
are hereby placed into the public domain. You are permitted and
encouraged to use this code in your own programs for fun
or for profit as you see fit. A simple comment in the code giving
credit would be courteous but is not required.
=head1 ACKNOWLEDGEMENTS
Russ Albery, Jon Orwant, Larry Rosler, and Nat Torkington all contributed
suggestions and corrections to this piece. Thanks especially to Damian
Conway for his ideas and feedback, and without whose indirect prodding
I might never have taken the time to show others how much Perl has to
offer in the way of objects once you start thinking outside the tiny
little box of today's "popular" object-oriented languages enforce.
--
Hackers should be judged by their hacking, not bogus criteria such as degrees,
age, race or position. -- From _Hackers_ heroes of the computer revolution --
------------------------------
Date: 15 May 1999 18:15:52 -0700
From: Russ Allbery <rra@stanford.edu>
Subject: Re: question about STDOUT
Message-Id: <ylbtfl4xx3.fsf@windlord.stanford.edu>
Zhan <zzhan@cs.nmsu.edu> writes:
> Yes! one trouble that I have thought for a long time, but I can't answer
> it, I have wrote a chatroom CGI with Perl 5.0005 under Linux, but now
> when I try run it under NT Server with ActivePerl 5.0005 for NT&95, a
> function (system calls) "STDOUT->flush;" or "autoflush STDOUT 1;" do not
> work, but it run under Linux very well,
Does $| = 1 work any better?
> And my chat room have another problem, I use IE to visit it is well, but
> it's Netscape killer now, if I use netscape for win version to visit it,
> netscape will be crashed, I don't know why?Can u have time to help me?
This sounds like a Netscape bug. Chances are you're generating some sort
of invalid HTML that it doesn't like.
--
#!/usr/bin/perl -- Russ Allbery, Just Another Perl Hacker
$^=q;@!>~|{>krw>yn{u<$$<[~||<Juukn{=,<S~|}<Jwx}qn{<Yn{u<Qjltn{ > 0gFzD gD,
00Fz, 0,,( 0hF 0g)F/=, 0> "L$/GEIFewe{,$/ 0C$~> "@=,m,|,(e 0.), 01,pnn,y{
rw} >;,$0=q,$,,($_=$^)=~y,$/ C-~><@=\n\r,-~$:-u/ #y,d,s,(\$.),$1,gee,print
------------------------------
Date: 16 May 1999 01:51:45 GMT
From: zenin@bawdycaste.org
Subject: Re: TROLL ALERT (Re: Perl "constructors")
Message-Id: <926819646.853143@thrush.omix.com>
Arved Sandstrom <Arved_37@chebucto.ns.ca> wrote:
>snip<
: But a fixed name for an object constructor ('new') is a feature of C++ and
: Java, for example. Why should Perl be defiantly different?
Following the same logic, why should it be the same?
: More to the point, what's wrong with having a discussion over the issue?
When it really is an "issue" we'll discuss it.
: Just to put my 1.5 CAN cents on the table, I like the ability to call a
: constructor anything, because sometimes "new" is not intuitive. But that's
: just me.
Welcome. You'll do well in Perl.
--
-Zenin (zenin@archive.rhps.org) Caffeine...for the mind.
Pizza......for the body.
Sushi......for the soul.
-- User Friendly
------------------------------
Date: 12 Dec 98 21:33:47 GMT (Last modified)
From: Perl-Request@ruby.oce.orst.edu (Perl-Users-Digest Admin)
Subject: Special: Digest Administrivia (Last modified: 12 Dec 98)
Message-Id: <null>
Administrivia:
Well, after 6 months, here's the answer to the quiz: what do we do about
comp.lang.perl.moderated. Answer: nothing.
]From: Russ Allbery <rra@stanford.edu>
]Date: 21 Sep 1998 19:53:43 -0700
]Subject: comp.lang.perl.moderated available via e-mail
]
]It is possible to subscribe to comp.lang.perl.moderated as a mailing list.
]To do so, send mail to majordomo@eyrie.org with "subscribe clpm" in the
]body. Majordomo will then send you instructions on how to confirm your
]subscription. This is provided as a general service for those people who
]cannot receive the newsgroup for whatever reason or who just prefer to
]receive messages via e-mail.
The Perl-Users Digest is a retransmission of the USENET newsgroup
comp.lang.perl.misc. For subscription or unsubscription requests, send
the single line:
subscribe perl-users
or:
unsubscribe perl-users
to almanac@ruby.oce.orst.edu.
To submit articles to comp.lang.perl.misc (and this Digest), send your
article to perl-users@ruby.oce.orst.edu.
To submit articles to comp.lang.perl.announce, send your article to
clpa@perl.com.
To request back copies (available for a week or so), send your request
to almanac@ruby.oce.orst.edu with the command "send perl-users x.y",
where x is the volume number and y is the issue number.
The Meta-FAQ, an article containing information about the FAQ, is
available by requesting "send perl-users meta-faq". The real FAQ, as it
appeared last in the newsgroup, can be retrieved with the request "send
perl-users FAQ". Due to their sizes, neither the Meta-FAQ nor the FAQ
are included in the digest.
The "mini-FAQ", which is an updated version of the Meta-FAQ, is
available by requesting "send perl-users mini-faq". It appears twice
weekly in the group, but is not distributed in the digest.
For other requests pertaining to the digest, send mail to
perl-users-request@ruby.oce.orst.edu. Do not waste your time or mine
sending perl questions to the -request address, I don't have time to
answer them even if I did know the answer.
------------------------------
End of Perl-Users Digest V8 Issue 5678
**************************************