perltoot2

You probably began to get a little suspicious about the duplicated code way
back earlier when we first showed you the Person class, and then later the
Employee class. Each method used to access the hash fields looked virtually
identical. This should have tickled that great programming virtue,
Impatience, but for the time, we let Laziness win out, and so did nothing.
Proxy methods can cure this.

Instead of writing a new function every time we want a new data field,
we'll use the autoload mechanism to generate (actually, mimic) methods on
the fly. To verify that we're accessing a valid member, we will check
against an _permitted (pronounced ``under-permitted'') field, which is a reference to a file-scoped lexical (like a
C file static) hash of permitted fields in this record called %fields. Why the underscore? For the same reason as the
_CENSUS field we once used: as a marker that means ``for internal use only''.

Here's what the module initialization code and class constructor will look
like when taking this approach:

If we wanted our record to have default values, we could fill those in
where current we have undef in the %fields hash.

Notice how we saved a reference to our class data on the object itself?
Remember that it's important to access class data through the object itself
instead of having any method reference %fields directly, or
else you won't have a decent inheritance.

The real magic, though, is going to reside in our proxy method, which will handle all calls to undefined methods for objects of class Person (or subclasses of Person). It has to be called
AUTOLOAD. Again, it's all caps because it's called for us implicitly by Perl itself, not by a user directly.

Pretty nifty, eh? All we have to do to add new data fields is modify
%fields. No new functions need be written.

I could have avoided the _permitted field entirely, but
I wanted to demonstrate how to store a reference to
class data on the object so you wouldn't have to access that class data
directly from an object method.

Even though proxy methods can provide a more convenient approach to making
more struct-like classes than tediously coding up data methods as
functions, it still leaves a bit to be desired. For one thing, it means you
have to handle bogus calls that you don't mean to trap via your proxy. It
also means you have to be quite careful when dealing with inheritance, as
detailed above.

Perl programmers have responded to this by creating several different class construction classes. These metaclasses are classes that create other classes.
A couple worth looking at are Class::Struct and Alias. These and other related metaclasses can be found in the modules directory on
CPAN.

One of the older ones is Class::Struct. In fact, its syntax and interface were sketched out long before perl5 even solidified into a real thing. What it does is provide you a way to ``declare'' a class as having objects whose fields are of a specific type. The function that does this is called, not surprisingly enough,
struct(). Because structures or records are not base types in Perl, each time you want to create a class to provide a record-like data object, you yourself have to define a
new() method, plus separate data-access methods for each of that record's fields. You'll quickly become bored with this process. The Class::Struct::struct() function alleviates this tedium.

You can declare types in the struct to be basic Perl types, or user-defined types (classes). User types will be initialized by calling that class's
new() method.

Here's a real-world example of using struct generation. Let's say you wanted to override Perl's idea of
gethostbyname() and
gethostbyaddr() so that they would return objects that acted like
C structures. We don't care about high-falutin'
OO gunk. All we want is for these objects to act like structs in the
C sense.

and then Class::Struct would have used an anonymous hash as the object
type, instead of an anonymous array. The array is faster and smaller, but
the hash works out better if you eventually want to do inheritance. Since
for this struct-like object we aren't planning on inheritance, this time
we'll opt for better speed and size over better flexibility.

We've snuck in quite a fair bit of other concepts besides just dynamic
class creation, like overriding core functions, import/export bits,
function prototyping, short-cut function call via &whatever, and function replacement with goto &whatever. These all mostly make sense from the perspective of a traditional module,
but as you can see, we can also use them in an object module.

You can look at other object-based, struct-like overrides of core functions in the 5.004 release of Perl in File::stat, Net::hostent, Net::netent, Net::protoent, Net::servent, Time::gmtime, Time::localtime, User::grent, and User::pwent. These modules have a final component that's all lowercase, by convention reserved for compiler pragmas, because they affect the compilation and change a builtin function. They also have the type names that a
C programmer would most expect.

If you're used to
C++ objects, then you're accustomed to being able to
get at an object's data members as simple variables from within a method.
The Alias module provides for this, as well as a good bit more, such as the
possibility of private methods that the object can call but folks outside
the class cannot.

Here's an example of creating a Person using the Alias module. When you
update these magical instance variables, you automatically update value
fields in the hash. Convenient, eh?

The need for the use vars declaration is because what Alias does is play with package globals with
the same name as the fields. To use globals while use strict is in effect, you have to predeclare them. These package variables are localized to the block enclosing the
attr() call just as if you'd used a
local() on them. However, that means that they're still considered global variables with temporary values, just as with any other
local().

It would be nice to combine Alias with something like Class::Struct or
Class::MethodMaker.

In the various
OO literature, it seems that a lot of different words
are used to describe only a few different concepts. If you're not already
an object programmer, then you don't need to worry about all these fancy
words. But if you are, then you might like to know how to get at the same
concepts in Perl.

For example, it's common to call an object an instance of a class and to call those objects' methods instance methods. Data fields peculiar to each object are often called instance data or object
attributes, and data fields common to all members of that class are
class data, class attributes, or static data members.

C++ programmers have static methods and virtual methods, but Perl only has class methods and object methods. Actually, Perl only has methods. Whether a method gets used as a class or
object method is by usage only. You could accidentally call a class method
(one expecting a string argument) on an object (one expecting a reference),
or vice versa.

From the
C++ perspective, all methods in Perl are virtual.
This, by the way, is why they are never checked for function prototypes in
the argument list as regular builtin and user-defined functions can be.

Because a class is itself something of an object, Perl's classes can be
taken as describing both a ``class as meta-object'' (also called object
factory) philosophy and the ``class as type definition'' (declaring
behaviour, not defining mechanism) idea.
C++ supports the latter notion, but not the former.

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