1N/Aperltooc - Tom's OO Tutorial for Class Data in Perl
1N/AWhen designing an object class, you are sometimes faced with the situation
1N/Aof wanting common state shared by all objects of that class.
1N/ASuch I<class attributes> act somewhat like global variables for the entire
1N/Aclass, but unlike program-wide globals, class attributes have meaning only to
1N/AHere are a few examples where class attributes might come in handy:
1N/Ato keep a count of the objects you've created, or how many are
1N/Ato extract the name or file descriptor for a logfile used by a debugging
1N/Ato access collective data, like the total amount of cash dispensed by
1N/Aall ATMs in a network in a given day.
1N/Ato access the last object created by a class, or the most accessed object,
1N/Aor to retrieve a list of all objects.
1N/AUnlike a true global, class attributes should not be accessed directly.
1N/AInstead, their state should be inspected, and perhaps altered, only
1N/Athrough the mediated access of I<class methods>. These class attributes
1N/Aaccessor methods are similar in spirit and function to accessors used
1N/Ato manipulate the state of instance attributes on an object. They provide a
1N/Aclear firewall between interface and implementation.
1N/AYou should allow access to class attributes through either the class
1N/Aname or any object of that class. If we assume that $an_object is of
1N/Atype Some_Class, and the &Some_Class::population_count method accesses
1N/Aclass attributes, then these two invocations should both be possible,
1N/Aand almost certainly equivalent.
1N/A Some_Class->population_count()
1N/A $an_object->population_count()
1N/AThe question is, where do you store the state which that method accesses?
1N/AUnlike more restrictive languages like C++, where these are called
1N/Astatic data members, Perl provides no syntactic mechanism to declare
1N/Aclass attributes, any more than it provides a syntactic mechanism to
1N/Adeclare instance attributes. Perl provides the developer with a broad
1N/Aset of powerful but flexible features that can be uniquely crafted to
1N/Athe particular demands of the situation.
1N/AA class in Perl is typically implemented in a module. A module consists
1N/Aof two complementary feature sets: a package for interfacing with the
1N/Aoutside world, and a lexical file scope for privacy. Either of these
1N/Atwo mechanisms can be used to implement class attributes. That means you
1N/Aget to decide whether to put your class attributes in package variables
1N/Aor to put them in lexical variables.
1N/AAnd those aren't the only decisions to make. If you choose to use package
1N/Avariables, you can make your class attribute accessor methods either ignorant
1N/Aof inheritance or sensitive to it. If you choose lexical variables,
1N/Ayou can elect to permit access to them from anywhere in the entire file
1N/Ascope, or you can limit direct data access exclusively to the methods
1N/Aimplementing those attributes.
1N/A=head1 Class Data in a Can
1N/AOne of the easiest ways to solve a hard problem is to let someone else
1N/Ado it for you! In this case, Class::Data::Inheritable (available on a
1N/ACPAN near you) offers a canned solution to the class data problem
1N/Ausing closures. So before you wade into this document, consider
1N/Ahaving a look at that module.
1N/A=head1 Class Data as Package Variables
1N/ABecause a class in Perl is really just a package, using package variables
1N/Ato hold class attributes is the most natural choice. This makes it simple
1N/Afor each class to have its own class attributes. Let's say you have a class
1N/Acalled Some_Class that needs a couple of different attributes that you'd
1N/Alike to be global to the entire class. The simplest thing to do is to
1N/Ause package variables like $Some_Class::CData1 and $Some_Class::CData2
1N/Ato hold these attributes. But we certainly don't want to encourage
1N/Aoutsiders to touch those data directly, so we provide methods
1N/AIn the accessor methods below, we'll for now just ignore the first
1N/Aargument--that part to the left of the arrow on method invocation, which
1N/Ais either a class name or an object reference.
1N/A $Some_Class::CData1 = shift if @_;
1N/A return $Some_Class::CData1;
1N/A $Some_Class::CData2 = shift if @_;
1N/A return $Some_Class::CData2;
1N/AThis technique is highly legible and should be completely straightforward
1N/Ato even the novice Perl programmer. By fully qualifying the package
1N/Avariables, they stand out clearly when reading the code. Unfortunately,
1N/Aif you misspell one of these, you've introduced an error that's hard
1N/Ato catch. It's also somewhat disconcerting to see the class name itself
1N/Ahard-coded in so many places.
1N/ABoth these problems can be easily fixed. Just add the C<use strict>
1N/Apragma, then pre-declare your package variables. (The C<our> operator
1N/Awill be new in 5.6, and will work for package globals just like C<my>
1N/Aworks for scoped lexicals.)
1N/A our($CData1, $CData2); # our() is new to perl5.6
1N/A $CData1 = shift if @_;
1N/A $CData2 = shift if @_;
1N/AAs with any other global variable, some programmers prefer to start their
1N/Apackage variables with capital letters. This helps clarity somewhat, but
1N/Aby no longer fully qualifying the package variables, their significance
1N/Acan be lost when reading the code. You can fix this easily enough by
1N/Achoosing better names than were used here.
1N/A=head2 Putting All Your Eggs in One Basket
1N/AJust as the mindless enumeration of accessor methods for instance attributes
1N/Agrows tedious after the first few (see L<perltoot>), so too does the
1N/Arepetition begin to grate when listing out accessor methods for class
1N/Adata. Repetition runs counter to the primary virtue of a programmer:
1N/ALaziness, here manifesting as that innate urge every programmer feels
1N/Ato factor out duplicate code whenever possible.
1N/AHere's what to do. First, make just one hash to hold all class attributes.
1N/A our %ClassData = ( # our() is new to perl5.6
1N/AUsing closures (see L<perlref>) and direct access to the package symbol
1N/Atable (see L<perlmod>), now clone an accessor method for each key in
1N/Athe %ClassData hash. Each of these methods is used to fetch or store
1N/Avalues to the specific, named class attribute.
1N/A for my $datum (keys %ClassData) {
1N/A no strict "refs"; # to register new methods in package
1N/A $ClassData{$datum} = shift if @_;
1N/A return $ClassData{$datum};
1N/AIt's true that you could work out a solution employing an &AUTOLOAD
1N/Amethod, but this approach is unlikely to prove satisfactory. Your
1N/Afunction would have to distinguish between class attributes and object
1N/Aattributes; it could interfere with inheritance; and it would have to
1N/Acareful about DESTROY. Such complexity is uncalled for in most cases,
1N/Aand certainly in this one.
1N/AYou may wonder why we're rescinding strict refs for the loop. We're
1N/Amanipulating the package's symbol table to introduce new function names
1N/Ausing symbolic references (indirect naming), which the strict pragma
1N/Awould otherwise forbid. Normally, symbolic references are a dodgy
1N/Anotion at best. This isn't just because they can be used accidentally
1N/Awhen you aren't meaning to. It's also because for most uses
1N/Ato which beginning Perl programmers attempt to put symbolic references,
1N/Awe have much better approaches, like nested hashes or hashes of arrays.
1N/ABut there's nothing wrong with using symbolic references to manipulate
1N/Asomething that is meaningful only from the perspective of the package
1N/Asymbol table, like method names or package variables. In other
1N/Awords, when you want to refer to the symbol table, use symbol references.
1N/AClustering all the class attributes in one place has several advantages.
1N/AThey're easy to spot, initialize, and change. The aggregation also
1N/Amakes them convenient to access externally, such as from a debugger
1N/Aor a persistence package. The only possible problem is that we don't
1N/Aautomatically know the name of each class's class object, should it have
1N/Aone. This issue is addressed below in L<"The Eponymous Meta-Object">.
1N/A=head2 Inheritance Concerns
1N/ASuppose you have an instance of a derived class, and you access class
1N/Adata using an inherited method call. Should that end up referring
1N/Ato the base class's attributes, or to those in the derived class?
1N/AHow would it work in the earlier examples? The derived class inherits
1N/Aall the base class's methods, including those that access class attributes.
1N/ABut what package are the class attributes stored in?
1N/AThe answer is that, as written, class attributes are stored in the package into
1N/Awhich those methods were compiled. When you invoke the &CData1 method
1N/Aon the name of the derived class or on one of that class's objects, the
1N/Aversion shown above is still run, so you'll access $Some_Class::CData1--or
1N/Ain the method cloning version, C<$Some_Class::ClassData{CData1}>.
1N/AThink of these class methods as executing in the context of their base
1N/Aclass, not in that of their derived class. Sometimes this is exactly
1N/Awhat you want. If Feline subclasses Carnivore, then the population of
1N/ACarnivores in the world should go up when a new Feline is born.
1N/ABut what if you wanted to figure out how many Felines you have apart
1N/Afrom Carnivores? The current approach doesn't support that.
1N/AYou'll have to decide on a case-by-case basis whether it makes any sense
1N/Afor class attributes to be package-relative. If you want it to be so,
1N/Athen stop ignoring the first argument to the function. Either it will
1N/Abe a package name if the method was invoked directly on a class name,
1N/Aor else it will be an object reference if the method was invoked on an
1N/Aobject reference. In the latter case, the ref() function provides the
1N/Aclass of that object.
1N/A my $obclass = shift;
1N/A my $class = ref($obclass) || $obclass;
1N/A my $varname = $class . "::CData1";
1N/A no strict "refs"; # to access package data symbolically
1N/A $$varname = shift if @_;
1N/AAnd then do likewise for all other class attributes (such as CData2,
1N/Aetc.) that you wish to access as package variables in the invoking package
1N/Ainstead of the compiling package as we had previously.
1N/AOnce again we temporarily disable the strict references ban, because
1N/Aotherwise we couldn't use the fully-qualified symbolic name for
1N/Athe package global. This is perfectly reasonable: since all package
1N/Avariables by definition live in a package, there's nothing wrong with
1N/Aaccessing them via that package's symbol table. That's what it's there
1N/Afor (well, somewhat).
1N/AWhat about just using a single hash for everything and then cloning
1N/Amethods? What would that look like? The only difference would be the
1N/Aclosure used to produce new method entries for the class's symbol table.
1N/A my $obclass = shift;
1N/A my $class = ref($obclass) || $obclass;
1N/A my $varname = $class . "::ClassData";
1N/A $varname->{$datum} = shift if @_;
1N/A return $varname->{$datum};
1N/A=head2 The Eponymous Meta-Object
1N/AIt could be argued that the %ClassData hash in the previous example is
1N/Aneither the most imaginative nor the most intuitive of names. Is there
1N/Asomething else that might make more sense, be more useful, or both?
1N/AAs it happens, yes, there is. For the "class meta-object", we'll use
1N/Aa package variable of the same name as the package itself. Within the
1N/Ascope of a package Some_Class declaration, we'll use the eponymously
1N/Anamed hash %Some_Class as that class's meta-object. (Using an eponymously
1N/Anamed hash is somewhat reminiscent of classes that name their constructors
1N/Aeponymously in the Python or C++ fashion. That is, class Some_Class would
1N/Ause &Some_Class::Some_Class as a constructor, probably even exporting that
1N/Aname as well. The StrNum class in Recipe 13.14 in I<The Perl Cookbook>
1N/Adoes this, if you're looking for an example.)
1N/AThis predictable approach has many benefits, including having a well-known
1N/Aidentifier to aid in debugging, transparent persistence,
1N/Aor checkpointing. It's also the obvious name for monadic classes and
1N/Atranslucent attributes, discussed later.
1N/AHere's an example of such a class. Notice how the name of the
1N/Ahash storing the meta-object is the same as the name of the package
1N/Aused to implement the class.
1N/A # create class meta-object using that most perfect of names
1N/A our %Some_Class = ( # our() is new to perl5.6
1N/A # this accessor is calling-package-relative
1N/A my $obclass = shift;
1N/A my $class = ref($obclass) || $obclass;
1N/A no strict "refs"; # to access eponymous meta-object
1N/A $class->{CData1} = shift if @_;
1N/A return $class->{CData1};
1N/A # but this accessor is not
1N/A no strict "refs"; # to access eponymous meta-object
1N/A __PACKAGE__ -> {CData2} = shift if @_;
1N/A return __PACKAGE__ -> {CData2};
1N/AIn the second accessor method, the __PACKAGE__ notation was used for
1N/Atwo reasons. First, to avoid hardcoding the literal package name
1N/Ain the code in case we later want to change that name. Second, to
1N/Aclarify to the reader that what matters here is the package currently
1N/Abeing compiled into, not the package of the invoking object or class.
1N/AIf the long sequence of non-alphabetic characters bothers you, you can
1N/Aalways put the __PACKAGE__ in a variable first.
1N/A no strict "refs"; # to access eponymous meta-object
1N/A my $class = __PACKAGE__;
1N/A $class->{CData2} = shift if @_;
1N/A return $class->{CData2};
1N/AEven though we're using symbolic references for good not evil, some
1N/Afolks tend to become unnerved when they see so many places with strict
1N/Aref checking disabled. Given a symbolic reference, you can always
1N/Aproduce a real reference (the reverse is not true, though). So we'll
1N/Acreate a subroutine that does this conversion for us. If invoked as a
1N/Afunction of no arguments, it returns a reference to the compiling class's
1N/Aeponymous hash. Invoked as a class method, it returns a reference to
1N/Athe eponymous hash of its caller. And when invoked as an object method,
1N/Athis function returns a reference to the eponymous hash for whatever
1N/Aclass the object belongs to.
1N/A our %Some_Class = ( # our() is new to perl5.6
1N/A # tri-natured: function, class method, or object method
1N/A my $obclass = shift || __PACKAGE__;
1N/A my $class = ref($obclass) || $obclass;
1N/A no strict "refs"; # to convert sym ref to real one
1N/A for my $datum (keys %{ _classobj() } ) {
1N/A # turn off strict refs so that we can
1N/A # register a method in the symbol table
1N/A my $self = shift->_classobj();
1N/A $self->{$datum} = shift if @_;
1N/A return $self->{$datum};
1N/A=head2 Indirect References to Class Data
1N/AA reasonably common strategy for handling class attributes is to store
1N/Aa reference to each package variable on the object itself. This is
1N/Aa strategy you've probably seen before, such as in L<perltoot> and
1N/AL<perlbot>, but there may be variations in the example below that you
1N/Ahaven't thought of before.
1N/A our($CData1, $CData2); # our() is new to perl5.6
1N/A my $obclass = shift;
1N/A return bless my $self = {
1N/A } => (ref $obclass || $obclass);
1N/A $self->{ObData1} = shift if @_;
1N/A return $self->{ObData1};
1N/A $self->{ObData2} = shift if @_;
1N/A return $self->{ObData2};
1N/A my $dataref = ref $self
1N/A $$dataref = shift if @_;
1N/A my $dataref = ref $self
1N/A $$dataref = shift if @_;
1N/AAs written above, a derived class will inherit these methods, which
1N/Awill consequently access package variables in the base class's package.
1N/AThis is not necessarily expected behavior in all circumstances. Here's an
1N/Aexample that uses a variable meta-object, taking care to access the
1N/Aproper package's data.
1N/A our %Some_Class = ( # our() is new to perl5.6
1N/A my $class = ref($self) || $self;
1N/A # get (hard) ref to eponymous meta-object
1N/A my $obclass = shift;
1N/A my $classobj = $obclass->_classobj();
1N/A CData1 => \$classobj->{CData1},
1N/A CData2 => \$classobj->{CData2},
1N/A } => (ref $obclass || $obclass);
1N/A $self->{ObData1} = shift if @_;
1N/A return $self->{ObData1};
1N/A $self->{ObData2} = shift if @_;
1N/A return $self->{ObData2};
1N/A $self = $self->_classobj() unless ref $self;
1N/A my $dataref = $self->{CData1};
1N/A $$dataref = shift if @_;
1N/A $self = $self->_classobj() unless ref $self;
1N/A my $dataref = $self->{CData2};
1N/A $$dataref = shift if @_;
1N/ANot only are we now strict refs clean, using an eponymous meta-object
1N/Aseems to make the code cleaner. Unlike the previous version, this one
1N/Adoes something interesting in the face of inheritance: it accesses the
1N/Aclass meta-object in the invoking class instead of the one into which
1N/Athe method was initially compiled.
1N/AYou can easily access data in the class meta-object, making
1N/Ait easy to dump the complete class state using an external mechanism such
1N/Aas when debugging or implementing a persistent class. This works because
1N/Athe class meta-object is a package variable, has a well-known name, and
1N/Aclusters all its data together. (Transparent persistence
1N/Ais not always feasible, but it's certainly an appealing idea.)
1N/AThere's still no check that object accessor methods have not been
1N/Ainvoked on a class name. If strict ref checking is enabled, you'd
1N/Ablow up. If not, then you get the eponymous meta-object. What you do
1N/Awith--or about--this is up to you. The next two sections demonstrate
1N/Ainnovative uses for this powerful feature.
1N/A=head2 Monadic Classes
1N/ASome of the standard modules shipped with Perl provide class interfaces
1N/Awithout any attribute methods whatsoever. The most commonly used module
1N/Anot numbered amongst the pragmata, the Exporter module, is a class with
1N/Aneither constructors nor attributes. Its job is simply to provide a
1N/Astandard interface for modules wishing to export part of their namespace
1N/Ainto that of their caller. Modules use the Exporter's &import method by
1N/Asetting their inheritance list in their package's @ISA array to mention
1N/A"Exporter". But class Exporter provides no constructor, so you can't
1N/Ahave several instances of the class. In fact, you can't have any--it
1N/Ajust doesn't make any sense. All you get is its methods. Its interface
1N/Acontains no statefulness, so state data is wholly superfluous.
1N/AAnother sort of class that pops up from time to time is one that supports
1N/Aa unique instance. Such classes are called I<monadic classes>, or less
1N/Aformally, I<singletons> or I<highlander classes>.
1N/AIf a class is monadic, where do you store its state, that is,
1N/Aits attributes? How do you make sure that there's never more than
1N/Aone instance? While you could merely use a slew of package variables,
1N/Ait's a lot cleaner to use the eponymously named hash. Here's a complete
1N/Aexample of a monadic class:
1N/A # accessor method for "name" attribute
1N/A $self->{name} = shift if @_;
1N/A return $self->{name};
1N/A # read-only accessor method for "birthday" attribute
1N/A die "can't reset birthday" if @_; # XXX: croak() is better
1N/A return $self->{birthday};
1N/A # accessor method for "stars" attribute
1N/A $self->{stars} = shift if @_;
1N/A return $self->{stars};
1N/A # oh my - one of our stars just went out!
1N/A my $count = $self->stars();
1N/A $self->stars($count - 1) if $count > 0;
1N/A name => "the world according to tchrist",
1N/A return $self; # yes, it's probably a class. SURPRISE!
1N/A # After the class is compiled, but before any use or require
1N/A # returns, we start off the universe with a bang.
1N/A __PACKAGE__ -> bigbang();
1N/AHold on, that doesn't look like anything special. Those attribute
1N/Aaccessors look no different than they would if this were a regular class
1N/Ainstead of a monadic one. The crux of the matter is there's nothing
1N/Athat says that $self must hold a reference to a blessed object. It merely
1N/Ahas to be something you can invoke methods on. Here the package name
1N/Aitself, Cosmos, works as an object. Look at the &supernova method. Is that
1N/Aa class method or an object method? The answer is that static analysis
1N/Acannot reveal the answer. Perl doesn't care, and neither should you.
1N/AIn the three attribute methods, C<%$self> is really accessing the %Cosmos
1N/AIf like Stephen Hawking, you posit the existence of multiple, sequential,
1N/Aand unrelated universes, then you can invoke the &bigbang method yourself
1N/Aat any time to start everything all over again. You might think of
1N/A&bigbang as more of an initializer than a constructor, since the function
1N/Adoesn't allocate new memory; it only initializes what's already there.
1N/ABut like any other constructor, it does return a scalar value to use
1N/Afor later method invocations.
1N/AImagine that some day in the future, you decide that one universe just
1N/Aisn't enough. You could write a new class from scratch, but you already
1N/Ahave an existing class that does what you want--except that it's monadic,
1N/Aand you want more than just one cosmos.
1N/AThat's what code reuse via subclassing is all about. Look how short
1N/A my $protoverse = shift;
1N/A my $class = ref($protoverse) || $protoverse;
1N/A return bless($self, $class)->bigbang();
1N/ABecause we were careful to be good little creators when we designed our
1N/ACosmos class, we can now reuse it without touching a single line of code
1N/Awhen it comes time to write our Multiverse class. The same code that
1N/Aworked when invoked as a class method continues to work perfectly well
1N/Awhen invoked against separate instances of a derived class.
1N/AThe astonishing thing about the Cosmos class above is that the value
1N/Areturned by the &bigbang "constructor" is not a reference to a blessed
1N/Aobject at all. It's just the class's own name. A class name is, for
1N/Avirtually all intents and purposes, a perfectly acceptable object.
1N/AIt has state, behavior, and identity, the three crucial components
1N/Aof an object system. It even manifests inheritance, polymorphism,
1N/Aand encapsulation. And what more can you ask of an object?
1N/ATo understand object orientation in Perl, it's important to recognize the
1N/Aunification of what other programming languages might think of as class
1N/Amethods and object methods into just plain methods. "Class methods"
1N/Aand "object methods" are distinct only in the compartmentalizing mind
1N/Aof the Perl programmer, not in the Perl language itself.
1N/AAlong those same lines, a constructor is nothing special either, which
1N/Ais one reason why Perl has no pre-ordained name for them. "Constructor"
1N/Ais just an informal term loosely used to describe a method that returns
1N/Aa scalar value that you can make further method calls against. So long
1N/Aas it's either a class name or an object reference, that's good enough.
1N/AIt doesn't even have to be a reference to a brand new object.
1N/AYou can have as many--or as few--constructors as you want, and you can
1N/Aname them whatever you care to. Blindly and obediently using new()
1N/Afor each and every constructor you ever write is to speak Perl with
1N/Asuch a severe C++ accent that you do a disservice to both languages.
1N/AThere's no reason to insist that each class have but one constructor,
1N/Aor that a constructor be named new(), or that a constructor be
1N/Aused solely as a class method and not an object method.
1N/AThe next section shows how useful it can be to further distance ourselves
1N/Afrom any formal distinction between class method calls and object method
1N/Acalls, both in constructors and in accessor methods.
1N/A=head2 Translucent Attributes
1N/AA package's eponymous hash can be used for more than just containing
1N/Aper-class, global state data. It can also serve as a sort of template
1N/Acontaining default settings for object attributes. These default
1N/Asettings can then be used in constructors for initialization of a
1N/Aparticular object. The class's eponymous hash can also be used to
1N/Aimplement I<translucent attributes>. A translucent attribute is one
1N/Athat has a class-wide default. Each object can set its own value for the
1N/Aattribute, in which case C<< $object->attribute() >> returns that value.
1N/ABut if no value has been set, then C<< $object->attribute() >> returns
1N/Athe class-wide default.
1N/AWe'll apply something of a copy-on-write approach to these translucent
1N/Aattributes. If you're just fetching values from them, you get
1N/Atranslucency. But if you store a new value to them, that new value is
1N/Aset on the current object. On the other hand, if you use the class as
1N/Aan object and store the attribute value directly on the class, then the
1N/Ameta-object's value changes, and later fetch operations on objects with
1N/Auninitialized values for those attributes will retrieve the meta-object's
1N/Anew values. Objects with their own initialized values, however, won't
1N/ALet's look at some concrete examples of using these properties before we
1N/Ashow how to implement them. Suppose that a class named Some_Class
1N/Ahad a translucent data attribute called "color". First you set the color
1N/Ain the meta-object, then you create three objects using a constructor
1N/Athat happens to be named &spawn.
1N/A Vermin->color("vermilion");
1N/A $ob1 = Vermin->spawn(); # so that's where Jedi come from
1N/A $ob2 = Vermin->spawn();
1N/A $ob3 = Vermin->spawn();
1N/A print $obj3->color(); # prints "vermilion"
1N/AEach of these objects' colors is now "vermilion", because that's the
1N/Ameta-object's value for that attribute, and these objects do not have
1N/Aindividual color values set.
1N/AChanging the attribute on one object has no effect on other objects
1N/A $ob3->color("chartreuse");
1N/A print $ob3->color(); # prints "chartreuse"
1N/A print $ob1->color(); # prints "vermilion", translucently
1N/AIf you now use $ob3 to spawn off another object, the new object will
1N/Atake the color its parent held, which now happens to be "chartreuse".
1N/AThat's because the constructor uses the invoking object as its template
1N/Afor initializing attributes. When that invoking object is the
1N/Aclass name, the object used as a template is the eponymous meta-object.
1N/AWhen the invoking object is a reference to an instantiated object, the
1N/A&spawn constructor uses that existing object as a template.
1N/A $ob4 = $ob3->spawn(); # $ob3 now template, not %Vermin
1N/A print $ob4->color(); # prints "chartreuse"
1N/AAny actual values set on the template object will be copied to the
1N/Anew object. But attributes undefined in the template object, being
1N/Atranslucent, will remain undefined and consequently translucent in the
1N/ANow let's change the color attribute on the entire class:
1N/A Vermin->color("azure");
1N/A print $ob1->color(); # prints "azure"
1N/A print $ob2->color(); # prints "azure"
1N/A print $ob3->color(); # prints "chartreuse"
1N/A print $ob4->color(); # prints "chartreuse"
1N/AThat color change took effect only in the first pair of objects, which
1N/Awere still translucently accessing the meta-object's values. The second
1N/Apair had per-object initialized colors, and so didn't change.
1N/AOne important question remains. Changes to the meta-object are reflected
1N/Ain translucent attributes in the entire class, but what about
1N/Achanges to discrete objects? If you change the color of $ob3, does the
1N/Avalue of $ob4 see that change? Or vice-versa. If you change the color
1N/Aof $ob4, does then the value of $ob3 shift?
1N/A $ob3->color("amethyst");
1N/A print $ob3->color(); # prints "amethyst"
1N/A print $ob4->color(); # hmm: "chartreuse" or "amethyst"?
1N/AWhile one could argue that in certain rare cases it should, let's not
1N/Ado that. Good taste aside, we want the answer to the question posed in
1N/Athe comment above to be "chartreuse", not "amethyst". So we'll treat
1N/Athese attributes similar to the way process attributes like environment
1N/Avariables, user and group IDs, or the current working directory are
1N/Atreated across a fork(). You can change only yourself, but you will see
1N/Athose changes reflected in your unspawned children. Changes to one object
1N/Awill propagate neither up to the parent nor down to any existing child objects.
1N/AThose objects made later, however, will see the changes.
1N/AIf you have an object with an actual attribute value, and you want to
1N/Amake that object's attribute value translucent again, what do you do?
1N/ALet's design the class so that when you invoke an accessor method with
1N/AC<undef> as its argument, that attribute returns to translucency.
1N/A $ob4->color(undef); # back to "azure"
1N/AHere's a complete implementation of Vermin as described above.
1N/A # here's the class meta-object, eponymously named.
1N/A # it holds all class attributes, and also all instance attributes
1N/A # so the latter can be used for both initialization
1N/A our %Vermin = ( # our() is new to perl5.6
1N/A PopCount => 0, # capital for class attributes
1N/A color => "beige", # small for instance attributes
1N/A # constructor method
1N/A # invoked as class method or object method
1N/A my $obclass = shift;
1N/A my $class = ref($obclass) || $obclass;
1N/A bless($self, $class);
1N/A $class->{PopCount}++;
1N/A # init fields from invoking object, or omit if
1N/A # invoking object is the class to provide translucency
1N/A %$self = %$obclass if ref $obclass;
1N/A # translucent accessor for "color" attribute
1N/A # invoked as class method or object method
1N/A my $class = ref($self) || $self;
1N/A # handle class invocation
1N/A unless (ref $self) {
1N/A $class->{color} = shift if @_;
1N/A return $class->{color}
1N/A # handle object invocation
1N/A $self->{color} = shift if @_;
1N/A if (defined $self->{color}) { # not exists!
1N/A return $self->{color};
1N/A return $class->{color};
1N/A # accessor for "PopCount" class attribute
1N/A # invoked as class method or object method
1N/A # but uses object solely to locate meta-object
1N/A my $obclass = shift;
1N/A my $class = ref($obclass) || $obclass;
1N/A return $class->{PopCount};
1N/A # instance destructor
1N/A # invoked only as object method
1N/A my $class = ref $self;
1N/A $class->{PopCount}--;
1N/AHere are a couple of helper methods that might be convenient. They aren't
1N/Aaccessor methods at all. They're used to detect accessibility of data
1N/Aattributes. The &is_translucent method determines whether a particular
1N/Aobject attribute is coming from the meta-object. The &has_attribute
1N/Amethod detects whether a class implements a particular property at all.
1N/AIt could also be used to distinguish undefined properties from non-existent
1N/A # detect whether an object attribute is translucent
1N/A # (typically?) invoked only as object method
1N/A sub is_translucent {
1N/A my($self, $attr) = @_;
1N/A return !defined $self->{$attr};
1N/A # test for presence of attribute in class
1N/A # invoked as class method or object method
1N/A my($self, $attr) = @_;
1N/A my $class = ref($self) || $self;
1N/A return exists $class->{$attr};
1N/AIf you prefer to install your accessors more generically, you can make
1N/Ause of the upper-case versus lower-case convention to register into the
1N/Apackage appropriate methods cloned from generic closures.
1N/A for my $datum (keys %{ +__PACKAGE__ }) {
1N/A *$datum = ($datum =~ /^[A-Z]/)
1N/A ? sub { # install class accessor
1N/A my $obclass = shift;
1N/A my $class = ref($obclass) || $obclass;
1N/A return $class->{$datum};
1N/A : sub { # install translucent accessor
1N/A my $class = ref($self) || $self;
1N/A unless (ref $self) {
1N/A $class->{$datum} = shift if @_;
1N/A return $class->{$datum}
1N/A $self->{$datum} = shift if @_;
1N/A return defined $self->{$datum}
1N/A : $class -> {$datum}
1N/ATranslations of this closure-based approach into C++, Java, and Python
1N/Ahave been left as exercises for the reader. Be sure to send us mail as
1N/A=head1 Class Data as Lexical Variables
1N/A=head2 Privacy and Responsibility
1N/AUnlike conventions used by some Perl programmers, in the previous
1N/Aexamples, we didn't prefix the package variables used for class attributes
1N/Awith an underscore, nor did we do so for the names of the hash keys used
1N/Afor instance attributes. You don't need little markers on data names to
1N/Asuggest nominal privacy on attribute variables or hash keys, because these
1N/Aare B<already> notionally private! Outsiders have no business whatsoever
1N/Aplaying with anything within a class save through the mediated access of
1N/Aits documented interface; in other words, through method invocations.
1N/AAnd not even through just any method, either. Methods that begin with
1N/Aan underscore are traditionally considered off-limits outside the class.
1N/AIf outsiders skip the documented method interface to poke around the
1N/Ainternals of your class and end up breaking something, that's not your
1N/APerl believes in individual responsibility rather than mandated control.
1N/APerl respects you enough to let you choose your own preferred level of
1N/Apain, or of pleasure. Perl believes that you are creative, intelligent,
1N/Aand capable of making your own decisions--and fully expects you to
1N/Atake complete responsibility for your own actions. In a perfect world,
1N/Athese admonitions alone would suffice, and everyone would be intelligent,
1N/Aresponsible, happy, and creative. And careful. One probably shouldn't
1N/Aforget careful, and that's a good bit harder to expect. Even Einstein
1N/Awould take wrong turns by accident and end up lost in the wrong part
1N/ASome folks get the heebie-jeebies when they see package variables
1N/Ahanging out there for anyone to reach over and alter them. Some folks
1N/Alive in constant fear that someone somewhere might do something wicked.
1N/AThe solution to that problem is simply to fire the wicked, of course.
1N/ABut unfortunately, it's not as simple as all that. These cautious
1N/Atypes are also afraid that they or others will do something not so
1N/Amuch wicked as careless, whether by accident or out of desperation.
1N/AIf we fire everyone who ever gets careless, pretty soon there won't be
1N/Aanybody left to get any work done.
1N/AWhether it's needless paranoia or sensible caution, this uneasiness can
1N/Abe a problem for some people. We can take the edge off their discomfort
1N/Aby providing the option of storing class attributes as lexical variables
1N/Ainstead of as package variables. The my() operator is the source of
1N/Aall privacy in Perl, and it is a powerful form of privacy indeed.
1N/AIt is widely perceived, and indeed has often been written, that Perl
1N/Aprovides no data hiding, that it affords the class designer no privacy
1N/Anor isolation, merely a rag-tag assortment of weak and unenforcible
1N/Asocial conventions instead. This perception is demonstrably false and
1N/Aeasily disproven. In the next section, we show how to implement forms
1N/Aof privacy that are far stronger than those provided in nearly any
1N/Aother object-oriented language.
1N/A=head2 File-Scoped Lexicals
1N/AA lexical variable is visible only through the end of its static scope.
1N/AThat means that the only code able to access that variable is code
1N/Aresiding textually below the my() operator through the end of its block
1N/Aif it has one, or through the end of the current file if it doesn't.
1N/AStarting again with our simplest example given at the start of this
1N/Adocument, we replace our() variables with my() versions.
1N/A my($CData1, $CData2); # file scope, not in any package
1N/A $CData1 = shift if @_;
1N/A $CData2 = shift if @_;
1N/ASo much for that old $Some_Class::CData1 package variable and its brethren!
1N/AThose are gone now, replaced with lexicals. No one outside the
1N/Ascope can reach in and alter the class state without resorting to the
1N/Adocumented interface. Not even subclasses or superclasses of
1N/Athis one have unmediated access to $CData1. They have to invoke the &CData1
1N/Amethod against Some_Class or an instance thereof, just like anybody else.
1N/ATo be scrupulously honest, that last statement assumes you haven't packed
1N/Aseveral classes together into the same file scope, nor strewn your class
1N/Aimplementation across several different files. Accessibility of those
1N/Avariables is based uniquely on the static file scope. It has nothing to
1N/Ado with the package. That means that code in a different file but
1N/Athe same package (class) could not access those variables, yet code in the
1N/Asame file but a different package (class) could. There are sound reasons
1N/Awhy we usually suggest a one-to-one mapping between files and packages
1N/Aand modules and classes. You don't have to stick to this suggestion if
1N/Ayou really know what you're doing, but you're apt to confuse yourself
1N/Aotherwise, especially at first.
1N/AIf you'd like to aggregate your class attributes into one lexically scoped,
1N/Acomposite structure, you're perfectly free to do so.
1N/A $ClassData{CData1} = shift if @_;
1N/A return $ClassData{CData1};
1N/A $ClassData{CData2} = shift if @_;
1N/A return $ClassData{CData2};
1N/ATo make this more scalable as other class attributes are added, we can
1N/Aagain register closures into the package symbol table to create accessor
1N/A for my $datum (keys %ClassData) {
1N/A $ClassData{$datum} = shift if @_;
1N/A return $ClassData{$datum};
1N/ARequiring even your own class to use accessor methods like anybody else is
1N/Aprobably a good thing. But demanding and expecting that everyone else,
1N/Abe they subclass or superclass, friend or foe, will all come to your
1N/Aobject through mediation is more than just a good idea. It's absolutely
1N/Acritical to the model. Let there be in your mind no such thing as
1N/A"public" data, nor even "protected" data, which is a seductive but
1N/Aultimately destructive notion. Both will come back to bite at you.
1N/AThat's because as soon as you take that first step out of the solid
1N/Aposition in which all state is considered completely private, save from the
1N/Aperspective of its own accessor methods, you have violated the envelope.
1N/AAnd, having pierced that encapsulating envelope, you shall doubtless
1N/Asomeday pay the price when future changes in the implementation break
1N/Aunrelated code. Considering that avoiding this infelicitous outcome was
1N/Aprecisely why you consented to suffer the slings and arrows of obsequious
1N/Aabstraction by turning to object orientation in the first place, such
1N/Abreakage seems unfortunate in the extreme.
1N/A=head2 More Inheritance Concerns
1N/ASuppose that Some_Class were used as a base class from which to derive
1N/AAnother_Class. If you invoke a &CData method on the derived class or
1N/Aon an object of that class, what do you get? Would the derived class
1N/Ahave its own state, or would it piggyback on its base class's versions
1N/Aof the class attributes?
1N/AThe answer is that under the scheme outlined above, the derived class
1N/Awould B<not> have its own state data. As before, whether you consider
1N/Athis a good thing or a bad one depends on the semantics of the classes
1N/AThe cleanest, sanest, simplest way to address per-class state in a
1N/Alexical is for the derived class to override its base class's version
1N/Aof the method that accesses the class attributes. Since the actual method
1N/Acalled is the one in the object's derived class if this exists, you
1N/Aautomatically get per-class state this way. Any urge to provide an
1N/Aunadvertised method to sneak out a reference to the %ClassData hash
1N/Ashould be strenuously resisted.
1N/AAs with any other overridden method, the implementation in the
1N/Aderived class always has the option of invoking its base class's
1N/Aversion of the method in addition to its own. Here's an example:
1N/A package Another_Class;
1N/A @ISA = qw(Some_Class);
1N/A my($self, $newvalue) = @_;
1N/A $ClassData{CData1} = $newvalue;
1N/A # then pass the buck up to the first
1N/A # overridden version, if there is one
1N/A if ($self->can("SUPER::CData1")) {
1N/A $self->SUPER::CData1($newvalue);
1N/A return $ClassData{CData1};
1N/AThose dabbling in multiple inheritance might be concerned
1N/Aabout there being more than one override.
1N/A for my $parent (@ISA) {
1N/A my $methname = $parent . "::CData1";
1N/A if ($self->can($methname)) {
1N/A $self->$methname($newvalue);
1N/ABecause the &UNIVERSAL::can method returns a reference
1N/Ato the function directly, you can use this directly
1N/Afor a significant performance improvement:
1N/A for my $parent (@ISA) {
1N/A if (my $coderef = $self->can($parent . "::CData1")) {
1N/A $self->$coderef($newvalue);
1N/A=head2 Locking the Door and Throwing Away the Key
1N/AAs currently implemented, any code within the same scope as the
1N/Afile-scoped lexical %ClassData can alter that hash directly. Is that
1N/Aok? Is it acceptable or even desirable to allow other parts of the
1N/Aimplementation of this class to access class attributes directly?
1N/AThat depends on how careful you want to be. Think back to the Cosmos
1N/Aclass. If the &supernova method had directly altered $Cosmos::Stars or
1N/AC<$Cosmos::Cosmos{stars}>, then we wouldn't have been able to reuse the
1N/Aclass when it came to inventing a Multiverse. So letting even the class
1N/Aitself access its own class attributes without the mediating intervention of
1N/Aproperly designed accessor methods is probably not a good idea after all.
1N/ARestricting access to class attributes from the class itself is usually
1N/Anot enforcible even in strongly object-oriented languages. But in Perl,
1N/A { # scope for hiding $CData1
1N/A shift; # XXX: unused
1N/A $CData1 = shift if @_;
1N/A { # scope for hiding $CData2
1N/A shift; # XXX: unused
1N/A $CData2 = shift if @_;
1N/ANo one--absolutely no one--is allowed to read or write the class
1N/Aattributes without the mediation of the managing accessor method, since
1N/Aonly that method has access to the lexical variable it's managing.
1N/AThis use of mediated access to class attributes is a form of privacy far
1N/Astronger than most OO languages provide.
1N/AThe repetition of code used to create per-datum accessor methods chafes
1N/Aat our Laziness, so we'll again use closures to create similar
1N/A { # scope for ultra-private meta-object for class attributes
1N/A for my $datum (keys %ClassData ) {
1N/A my ($self, $newvalue) = @_;
1N/A $ClassData{$datum} = $newvalue if @_ > 1;
1N/A return $ClassData{$datum};
1N/AThe closure above can be modified to take inheritance into account using
1N/Athe &UNIVERSAL::can method and SUPER as shown previously.
1N/A=head2 Translucency Revisited
1N/AThe Vermin class demonstrates translucency using a package variable,
1N/Aeponymously named %Vermin, as its meta-object. If you prefer to
1N/Ause absolutely no package variables beyond those necessary to appease
1N/Ainheritance or possibly the Exporter, this strategy is closed to you.
1N/AThat's too bad, because translucent attributes are an appealing
1N/Atechnique, so it would be valuable to devise an implementation using
1N/AThere's a second reason why you might wish to avoid the eponymous
1N/Apackage hash. If you use class names with double-colons in them, you
1N/Awould end up poking around somewhere you might not have meant to poke.
1N/A $class->{PopCount}++;
1N/A # accesses $Vermin::Vermin{PopCount}
1N/A package Vermin::Noxious;
1N/A $class = "Vermin::Noxious";
1N/A $class->{PopCount}++;
1N/A # accesses $Vermin::Noxious{PopCount}
1N/AIn the first case, because the class name had no double-colons, we got
1N/Athe hash in the current package. But in the second case, instead of
1N/Agetting some hash in the current package, we got the hash %Noxious in
1N/Athe Vermin package. (The noxious vermin just invaded another package and
1N/Asprayed their data around it. :-) Perl doesn't support relative packages
1N/Ain its naming conventions, so any double-colons trigger a fully-qualified
1N/Alookup instead of just looking in the current package.
1N/AIn practice, it is unlikely that the Vermin class had an existing
1N/Apackage variable named %Noxious that you just blew away. If you're
1N/Astill mistrustful, you could always stake out your own territory
1N/Awhere you know the rules, such as using Eponymous::Vermin::Noxious or
1N/AHieronymus::Vermin::Boschious or Leave_Me_Alone::Vermin::Noxious as class
1N/Anames instead. Sure, it's in theory possible that someone else has
1N/Aa class named Eponymous::Vermin with its own %Noxious hash, but this
1N/Akind of thing is always true. There's no arbiter of package names.
1N/AIt's always the case that globals like @Cwd::ISA would collide if more
1N/Athan one class uses the same Cwd package.
1N/AIf this still leaves you with an uncomfortable twinge of paranoia,
1N/Awe have another solution for you. There's nothing that says that you
1N/Ahave to have a package variable to hold a class meta-object, either for
1N/Amonadic classes or for translucent attributes. Just code up the methods
1N/Aso that they access a lexical instead.
1N/AHere's another implementation of the Vermin class with semantics identical
1N/Ato those given previously, but this time using no package variables.
1N/A # Here's the class meta-object, eponymously named.
1N/A # It holds all class data, and also all instance data
1N/A # so the latter can be used for both initialization
1N/A # and translucency. it's a template.
1N/A PopCount => 0, # capital for class attributes
1N/A color => "beige", # small for instance attributes
1N/A # constructor method
1N/A # invoked as class method or object method
1N/A my $obclass = shift;
1N/A my $class = ref($obclass) || $obclass;
1N/A bless($self, $class);
1N/A $ClassData{PopCount}++;
1N/A # init fields from invoking object, or omit if
1N/A # invoking object is the class to provide translucency
1N/A %$self = %$obclass if ref $obclass;
1N/A # translucent accessor for "color" attribute
1N/A # invoked as class method or object method
1N/A # handle class invocation
1N/A unless (ref $self) {
1N/A $ClassData{color} = shift if @_;
1N/A return $ClassData{color}
1N/A # handle object invocation
1N/A $self->{color} = shift if @_;
1N/A if (defined $self->{color}) { # not exists!
1N/A return $self->{color};
1N/A return $ClassData{color};
1N/A # class attribute accessor for "PopCount" attribute
1N/A # invoked as class method or object method
1N/A return $ClassData{PopCount};
1N/A # instance destructor; invoked only as object method
1N/A $ClassData{PopCount}--;
1N/A # detect whether an object attribute is translucent
1N/A # (typically?) invoked only as object method
1N/A sub is_translucent {
1N/A my($self, $attr) = @_;
1N/A $self = \%ClassData if !ref $self;
1N/A return !defined $self->{$attr};
1N/A # test for presence of attribute in class
1N/A # invoked as class method or object method
1N/A my($self, $attr) = @_;
1N/A return exists $ClassData{$attr};
1N/AInheritance is a powerful but subtle device, best used only after careful
1N/Aforethought and design. Aggregation instead of inheritance is often a
1N/AYou can't use file-scoped lexicals in conjunction with the SelfLoader
1N/Aor the AutoLoader, because they alter the lexical scope in which the
1N/Amodule's methods wind up getting compiled.
1N/AThe usual mealy-mouthed package-mungeing doubtless applies to setting
1N/Aup names of object attributes. For example, C<< $self->{ObData1} >>
1N/Ashould probably be C<< $self->{ __PACKAGE__ . "_ObData1" } >>, but that
1N/Awould just confuse the examples.
1N/AL<perltoot>, L<perlobj>, L<perlmod>, and L<perlbot>.
1N/AThe Tie::SecureHash and Class::Data::Inheritable modules from CPAN are
1N/A=head1 AUTHOR AND COPYRIGHT
1N/ACopyright (c) 1999 Tom Christiansen.
1N/AThis documentation is free; you can redistribute it
and/or modify it
1N/Aunder the same terms as Perl itself.
1N/AIrrespective of its distribution, all code examples in this file
1N/Aare hereby placed into the public domain. You are permitted and
1N/Aencouraged to use this code in your own programs for fun
1N/Aor for profit as you see fit. A simple comment in the code giving
1N/Acredit would be courteous but is not required.
1N/A=head1 ACKNOWLEDGEMENTS
1N/ARuss Allbery, Jon Orwant, Randy Ray, Larry Rosler, Nat Torkington,
1N/Aand Stephen Warren all contributed suggestions and corrections to this
1N/Apiece. Thanks especially to Damian Conway for his ideas and feedback,
1N/Aand without whose indirect prodding I might never have taken the time
1N/Ato show others how much Perl has to offer in the way of objects once
1N/Ayou start thinking outside the tiny little box that today's "popular"
1N/Aobject-oriented languages enforce.
1N/ALast edit: Sun Feb 4 20:50:28 EST 2001